diff --git a/.gitignore b/.gitignore index fe02460..91b7e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ lib-cov node_modules +.vscode +package-lock.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b59d0..32b5414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.0.2 + +- fix(security): :lock: Prototype Pollution in cli-tableau +- feat(test): :lock: Prototype Pollution security test + ## 2.0.0 - add borders: false option @@ -12,55 +17,54 @@ 0.3.1 / 2014-10-22 ================== - * fix example for new paths - * Readme badges - * Lighter production installs - * Safe colors - * In addition to 256-xterm ansi colors, handle 24-bit colors - * set up .travis.yml +- fix example for new paths +- Readme badges +- Lighter production installs +- Safe colors +- In addition to 256-xterm ansi colors, handle 24-bit colors +- set up .travis.yml 0.3.0 / 2014-02-02 ================== - * Switch version of colors to avoid npm broken-ness - * Handle custom colored strings correctly - * Removing var completely as return var width caused other problems. - * Fixing global leak of width variable. - * Omit horizontal decoration lines if empty - * Add a test for the the compact mode - * Make line() return the generated string instead of appending it to ret - * Customize the vertical cell separator separately from the right one - * Allow newer versions of colors to be used - * Added test for bordercolor - * Add bordercolor in style options and enable deepcopy of options +- Switch version of colors to avoid npm broken-ness +- Handle custom colored strings correctly +- Removing var completely as return var width caused other problems. +- Fixing global leak of width variable. +- Omit horizontal decoration lines if empty +- Add a test for the the compact mode +- Make line() return the generated string instead of appending it to ret +- Customize the vertical cell separator separately from the right one +- Allow newer versions of colors to be used +- Added test for bordercolor +- Add bordercolor in style options and enable deepcopy of options 0.2.0 / 2012-10-21 ================== - * test: avoid module dep in tests - * fix type bug on integer vertical table value - * handle newlines in vertical and cross tables - * factor out common style setting function - * handle newlines in body cells - * fix render bug when no header provided - * correctly calculate width of cells with newlines - * handles newlines in header cells - * ability to create cross tables - * changing table chars to ones that windows supports - * allow empty arguments to Table constructor - * fix headless tables containing empty first row - * add vertical tables - * remove reference to require.paths - * compact style for dense tables - * fix toString without col widths by cloning array - * [api]: Added abiltity to strip out ANSI color escape codes when calculating cell padding +- test: avoid module dep in tests +- fix type bug on integer vertical table value +- handle newlines in vertical and cross tables +- factor out common style setting function +- handle newlines in body cells +- fix render bug when no header provided +- correctly calculate width of cells with newlines +- handles newlines in header cells +- ability to create cross tables +- changing table chars to ones that windows supports +- allow empty arguments to Table constructor +- fix headless tables containing empty first row +- add vertical tables +- remove reference to require.paths +- compact style for dense tables +- fix toString without col widths by cloning array +- [api]: Added abiltity to strip out ANSI color escape codes when calculating cell padding 0.0.1 / 2011-01-03 ================== Initial release - ## Jun 28, 2017 Fork of `Automattic/cli-table` diff --git a/README.md b/README.md index bf8588d..39e93c7 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ Build Status - ### Horizontal Tables + ```javascript var Table = require('cli-tableau'); @@ -39,6 +39,7 @@ console.log(table.toString()); ``` ### Cross Tables + Cross tables are very similar to vertical tables, with two key differences: 1. They require a `head` setting when instantiated that has an empty string as the first header @@ -59,6 +60,7 @@ console.log(table.toString()); ### Custom styles The ```chars``` property controls how the table is drawn: + ```javascript var table = new Table({ chars: { @@ -86,6 +88,7 @@ console.log(table.toString()); Empty decoration lines will be skipped, to avoid vertical separator rows just set the 'mid', 'left-mid', 'mid-mid', 'right-mid' to the empty string: + ```javascript var table = new Table({ chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''} }); table.push( @@ -104,6 +107,7 @@ console.log(table.toString()); By setting all chars to empty with the exception of 'middle' being set to a single space and by setting padding to zero, it's possible to get the most compact layout with no decorations: + ```javascript var table = new Table({ chars: { diff --git a/examples/revs.js b/examples/revs.js index 59bf158..96731be 100644 --- a/examples/revs.js +++ b/examples/revs.js @@ -1,9 +1,8 @@ - /** * Module requirements. */ -var Table = require('../lib') +var Table = require("../lib"); /** * Example. @@ -11,45 +10,71 @@ var Table = require('../lib') /* col widths */ var table = new Table({ - head: ['Rel', 'Change', 'By', 'When'], - colWidths: [6, 21, 25, 17] -}) + head: ["Rel", "Change", "By", "When"], + colWidths: [6, 21, 25, 17], +}); table.push( - ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '7 minutes ago'] - , ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '8 minutes ago'] -) + ["v0.1", "Testing something cool", "rauchg@gmail.com", "7 minutes ago"], + ["v0.1", "Testing something cool", "rauchg@gmail.com", "8 minutes ago"] +); -console.log(table.toString()) +console.log(table.toString()); /* compact */ var table2 = new Table({ - head: ['Rel', 'Change', 'By', 'When'], + head: ["Rel", "Change", "By", "When"], colWidths: [6, 21, 25, 17], - style: {compact: true, 'padding-left': 1} -}) + style: { compact: true, "padding-left": 1 }, +}); table2.push( - ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '7 minutes ago'] - , ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '8 minutes ago'] - , [] - , ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '8 minutes ago'] -) + ["v0.1", "Testing something cool", "rauchg@gmail.com", "7 minutes ago"], + ["v0.1", "Testing something cool", "rauchg@gmail.com", "8 minutes ago"], + [], + ["v0.1", "Testing something cool", "rauchg@gmail.com", "8 minutes ago"] +); -console.log(table.toString()) +console.log(table.toString()); /* headless */ -var headlessTable = new Table() -headlessTable.push(['v0.1', 'Testing something cool', 'rauchg@gmail.com', '7 minutes ago']) -console.log(headlessTable.toString()) +var headlessTable = new Table(); +headlessTable.push([ + "v0.1", + "Testing something cool", + "rauchg@gmail.com", + "7 minutes ago", +]); +console.log(headlessTable.toString()); /* vertical */ -var verticalTable = new Table() -verticalTable.push({'Some Key': 'Some Value'}, {'Another much longer key': 'And its corresponding longer value'}) +var verticalTable = new Table(); +verticalTable.push( + { "Some Key": "Some Value" }, + { "Another much longer key": "And its corresponding longer value" } +); -console.log(verticalTable.toString()) +console.log(verticalTable.toString()); /* cross */ -var crossTable = new Table({head: ['', 'Header #1', 'Header #2']}) -crossTable.push({'Header #3': ['Value 1', 'Value 2']}, {'Header #4': ['Value 3', 'Value 4']}) -console.log(crossTable.toString()) +var crossTable = new Table({ head: ["", "Header #1", "Header #2"] }); +crossTable.push( + { "Header #3": ["Value 1", "Value 2"] }, + { "Header #4": ["Value 3", "Value 4"] } +); +console.log(crossTable.toString()); + +/* Prototype Pollution in cli-tableau */ +let attackerObject = + '{"__proto__":{"attackerControlledValue":"Attackers Payload"},"proto":{"attackPropFromProto":"changed"},"constructor":{"prototype":{"attackPropFromConstructorProto":"changed"}}}'; + +let attackedTable = new Table(JSON.parse(attackerObject)); + +attackedTable.push({ + Vulnerability: [ + "Prototype Pollution", + !!attackedTable.options.attackerControlledValue, + ], +}); + +console.log(attackedTable.toString()); diff --git a/lib/index.js b/lib/index.js index 198cc07..0365ba1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,12 +1,11 @@ - /** * Module dependencies. */ -var colors = require('chalk') -var utils = require('./utils') -var repeat = utils.repeat -var truncate = utils.truncate +var colors = require("chalk"); +var utils = require("./utils"); +var repeat = utils.repeat; +var truncate = utils.truncate; /** * Table constructor @@ -15,64 +14,68 @@ var truncate = utils.truncate * @api public */ -function Table (options) { - this.options = utils.options({ - chars: { - 'top': '─', - 'top-mid': '┬', - 'top-left': '┌', - 'top-right': '┐', - 'bottom': '─', - 'bottom-mid': '┴', - 'bottom-left': '└', - 'bottom-right': '┘', - 'left': '│', - 'left-mid': '├', - 'mid': '─', - 'mid-mid': '┼', - 'right': '│', - 'right-mid': '┤', - 'middle': '│' - }, - truncate: '…', - colWidths: [], - colAligns: [], - style: { - 'padding-left': 1, - 'padding-right': 1, - head: ['red'], - border: ['grey'], - compact: false +function Table(options) { + this.options = utils.options( + { + chars: { + top: "─", + "top-mid": "┬", + "top-left": "┌", + "top-right": "┐", + bottom: "─", + "bottom-mid": "┴", + "bottom-left": "└", + "bottom-right": "┘", + left: "│", + "left-mid": "├", + mid: "─", + "mid-mid": "┼", + right: "│", + "right-mid": "┤", + middle: "│", + }, + truncate: "…", + borders: true, + colWidths: [], + colAligns: [], + style: { + "padding-left": 1, + "padding-right": 1, + head: ["red"], + border: ["grey"], + compact: false, + }, + head: [], }, - head: [] - }, options) - - if (options.borders == false) { - this.options.chars = { - 'top': '', - 'top-mid': '', - 'top-left': '', - 'top-right': '', - 'bottom': '', - 'bottom-mid': '', - 'bottom-left': '', - 'bottom-right': '', - 'left': '', - 'left-mid': '', - 'mid': '', - 'mid-mid': '', - 'right': '', - 'right-mid': '', - 'middle': '' - } + options || {} + ); + + if (!this.options.borders) { + this.options.chars = { + top: "", + "top-mid": "", + "top-left": "", + "top-right": "", + bottom: "", + "bottom-mid": "", + "bottom-left": "", + "bottom-right": "", + left: "", + "left-mid": "", + mid: "", + "mid-mid": "", + right: "", + "right-mid": "", + middle: "", + }; } -}; +} /** * Inherit from Array. */ -Table.prototype = new Array +Table.prototype = new Array(); /** * Width getter @@ -81,11 +84,11 @@ Table.prototype = new Array * @api public */ -Table.prototype.__defineGetter__('width', function () { - var str = this.toString().split('\n') - if (str.length) return str[0].length - return 0 -}) +Table.prototype.__defineGetter__("width", function () { + var str = this.toString().split("\n"); + if (str.length) return str[0].length; + return 0; +}); /** * Render to a string. @@ -94,223 +97,266 @@ Table.prototype.__defineGetter__('width', function () { * @api public */ -Table.prototype.render = -Table.prototype.toString = function () { - var ret = '' - var options = this.options - var style = options.style - var head = options.head - var chars = options.chars - var truncater = options.truncate - var colWidths = options.colWidths || new Array(this.head.length) - var totalWidth = 0 +Table.prototype.render = Table.prototype.toString = function () { + var ret = ""; + var options = this.options; + var style = options.style; + var head = options.head; + var chars = options.chars; + var truncater = options.truncate; + var colWidths = options.colWidths || new Array(this.head.length); + var totalWidth = 0; - if (!head.length && !this.length) return '' + if (!head.length && !this.length) return ""; if (!colWidths.length) { - var everyRows = this.slice(0) - if (head.length) { everyRows = everyRows.concat([head]) }; + var everyRows = this.slice(0); + if (head.length) { + everyRows = everyRows.concat([head]); + } everyRows.forEach(function (cells) { - // horizontal (arrays) + // horizontal (arrays) if (Array.isArray(cells) && cells.length) { - extractColumnWidths(cells) + extractColumnWidths(cells); // vertical (objects) } else { - var headerCell = Object.keys(cells)[0] - var valueCell = cells[headerCell] + var headerCell = Object.keys(cells)[0]; + var valueCell = cells[headerCell]; - colWidths[0] = Math.max(colWidths[0] || 0, getWidth(headerCell) || 0) + colWidths[0] = Math.max(colWidths[0] || 0, getWidth(headerCell) || 0); - // cross (objects w/ array values) + // cross (objects w/ array values) if (Array.isArray(valueCell) && valueCell.length) { - extractColumnWidths(valueCell, 1) + extractColumnWidths(valueCell, 1); } else { - colWidths[1] = Math.max(colWidths[1] || 0, getWidth(valueCell) || 0) + colWidths[1] = Math.max(colWidths[1] || 0, getWidth(valueCell) || 0); } } - }) - }; - - totalWidth = (colWidths.length === 1 ? colWidths[0] : colWidths.reduce( - function (a, b) { - return a + b - })) + colWidths.length + 1 + }); + } - function extractColumnWidths (arr, offset) { - offset = offset || 0 + totalWidth = + (colWidths.length === 1 + ? colWidths[0] + : colWidths.reduce(function (a, b) { + return a + b; + })) + + colWidths.length + + 1; + + function extractColumnWidths(arr, offset) { + offset = offset || 0; arr.forEach(function (cell, i) { - colWidths[i + offset] = Math.max(colWidths[i + offset] || 0, getWidth(cell) || 0) - }) - }; - - function getWidth (obj) { - return typeof obj === 'object' && obj && obj.width !== undefined - ? obj.width - : ((typeof obj === 'object' && obj !== null ? utils.strlen(obj.text) : utils.strlen(obj)) + (style['padding-left'] || 0) + (style['padding-right'] || 0)) + colWidths[i + offset] = Math.max( + colWidths[i + offset] || 0, + getWidth(cell) || 0 + ); + }); + } + + function getWidth(obj) { + return typeof obj === "object" && obj && obj.width !== undefined + ? obj.width + : (typeof obj === "object" && obj !== null + ? utils.strlen(obj.text) + : utils.strlen(obj)) + + (style["padding-left"] || 0) + + (style["padding-right"] || 0); } // draws a line - function line (line, left, right, intersection) { - var width = 0 - line = left + repeat(line, totalWidth - 2) + right + function line(line, left, right, intersection) { + var width = 0; + line = left + repeat(line, totalWidth - 2) + right; colWidths.forEach(function (w, i) { - if (i === colWidths.length - 1) return - width += w + 1 - line = line.substr(0, width) + intersection + line.substr(width + 1) - }) + if (i === colWidths.length - 1) return; + width += w + 1; + line = line.substr(0, width) + intersection + line.substr(width + 1); + }); - return applyStyles(options.style.border, line) - }; + return applyStyles(options.style.border, line); + } // draws the top line - function lineTop () { - var l = line(chars.top, - chars['top-left'] || chars.top, - chars['top-right'] || chars.top, - chars['top-mid']) + function lineTop() { + var l = line( + chars.top, + chars["top-left"] || chars.top, + chars["top-right"] || chars.top, + chars["top-mid"] + ); if (l) { - ret += l + '\n' + ret += l + "\n"; } - }; + } - function generateRow (items, style) { - var cells = [] - var maxHeight = 0 + function generateRow(items, style) { + var cells = []; + var maxHeight = 0; // prepare vertical and cross table data - if (!Array.isArray(items) && typeof items === 'object') { - var key = Object.keys(items)[0] - var value = items[key] - var firstCellHead = true + if (!Array.isArray(items) && typeof items === "object") { + var key = Object.keys(items)[0]; + var value = items[key]; + var firstCellHead = true; if (Array.isArray(value)) { - items = value - items.unshift(key) + items = value; + items.unshift(key); } else { - items = [key, value] + items = [key, value]; } } // transform array of item strings into structure of cells items.forEach(function (item, i) { - var contents = (item === null || item === undefined ? '' : item).toString().split('\n').reduce(function (memo, l) { - memo.push(string(l, i)) - return memo - }, []) - - var height = contents.length - if (height > maxHeight) { maxHeight = height }; + var contents = (item === null || item === undefined ? "" : item) + .toString() + .split("\n") + .reduce(function (memo, l) { + memo.push(string(l, i)); + return memo; + }, []); + + var height = contents.length; + if (height > maxHeight) { + maxHeight = height; + } - cells.push({ contents: contents, height: height }) - }) + cells.push({ contents: contents, height: height }); + }); // transform vertical cells into horizontal lines - var lines = new Array(maxHeight) + var lines = new Array(maxHeight); cells.forEach(function (cell, i) { cell.contents.forEach(function (line, j) { - if (!lines[j]) { lines[j] = [] }; + if (!lines[j]) { + lines[j] = []; + } if (style || (firstCellHead && i === 0 && options.style.head)) { - line = applyStyles(options.style.head, line) + line = applyStyles(options.style.head, line); } - lines[j].push(line) - }) + lines[j].push(line); + }); // populate empty lines in cell for (var j = cell.height, l = maxHeight; j < l; j++) { - if (!lines[j]) { lines[j] = [] }; - lines[j].push(string('', i)) + if (!lines[j]) { + lines[j] = []; + } + lines[j].push(string("", i)); } - }) + }); - var ret = '' + var ret = ""; lines.forEach(function (line, index) { if (ret.length > 0) { - ret += '\n' + applyStyles(options.style.border, chars.left) + ret += "\n" + applyStyles(options.style.border, chars.left); } - ret += line.join(applyStyles(options.style.border, chars.middle)) + applyStyles(options.style.border, chars.right) - }) + ret += + line.join(applyStyles(options.style.border, chars.middle)) + + applyStyles(options.style.border, chars.right); + }); - return applyStyles(options.style.border, chars.left) + ret - }; + return applyStyles(options.style.border, chars.left) + ret; + } - function applyStyles (styles, subject) { + function applyStyles(styles, subject) { if (!subject) { - return '' + return ""; } styles.forEach(function (style) { - subject = colors[style](subject) - }) + subject = colors[style](subject); + }); - return subject - }; + return subject; + } // renders a string, by padding it or truncating it - function string (str, index) { - str = String(typeof str === 'object' && str.text ? str.text : str) - var length = utils.strlen(str) - var width = colWidths[index] - (style['padding-left'] || 0) - (style['padding-right'] || 0) - var align = options.colAligns[index] || 'left' - - return repeat(' ', style['padding-left'] || 0) + - (length === width ? str - : (length < width - ? str.padEnd((width + (str.length - length)), ' ', align === 'left' ? 'right' - : (align === 'middle' ? 'both' : 'left')) - : (truncater ? truncate(str, width, truncater) : str)) - ) + - repeat(' ', style['padding-right'] || 0) - }; + function string(str, index) { + str = String(typeof str === "object" && str.text ? str.text : str); + var length = utils.strlen(str); + var width = + colWidths[index] - + (style["padding-left"] || 0) - + (style["padding-right"] || 0); + var align = options.colAligns[index] || "left"; + + return ( + repeat(" ", style["padding-left"] || 0) + + (length === width + ? str + : length < width + ? str.padEnd( + width + (str.length - length), + " ", + align === "left" ? "right" : align === "middle" ? "both" : "left" + ) + : truncater + ? truncate(str, width, truncater) + : str) + + repeat(" ", style["padding-right"] || 0) + ); + } if (head.length) { - lineTop() + lineTop(); - ret += generateRow(head, style.head) + '\n' + ret += generateRow(head, style.head) + "\n"; } if (this.length) { this.forEach(function (cells, i) { - if (!head.length && i === 0) { lineTop() } else { - if (!style.compact || i < (!!head.length) ? 1 : 0 || cells.length === 0) { - var l = line(chars.mid - , chars['left-mid'] - , chars['right-mid'] - , chars['mid-mid']) - if (l) { ret += l + '\n' } + if (!head.length && i === 0) { + lineTop(); + } else { + if (!style.compact || i < !!head.length ? 1 : 0 || cells.length === 0) { + var l = line( + chars.mid, + chars["left-mid"], + chars["right-mid"], + chars["mid-mid"] + ); + if (l) { + ret += l + "\n"; + } } } if (Array.isArray(cells) && !cells.length) { - return + return; } else { - ret += generateRow(cells) + '\n' - }; - }) + ret += generateRow(cells) + "\n"; + } + }); } - var l = line(chars.bottom, - chars['bottom-left'] || chars.bottom, - chars['bottom-right'] || chars.bottom, - chars['bottom-mid']) + var l = line( + chars.bottom, + chars["bottom-left"] || chars.bottom, + chars["bottom-right"] || chars.bottom, + chars["bottom-mid"] + ); if (l) { - ret += l + ret += l; } else { // trim the last '\n' if we didn't add the bottom decoration - ret = ret.slice(0, -1) + ret = ret.slice(0, -1); } - return ret -} + return ret; +}; /** * Module exports. */ -module.exports = Table +module.exports = Table; -module.exports.version = '2.0.0' +module.exports.version = "2.0.2"; diff --git a/lib/utils.js b/lib/utils.js index ffb8f54..1d6eb7a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,4 +1,3 @@ - /** * Repeats a string. * @@ -8,8 +7,8 @@ */ exports.repeat = function (str, times) { - return Array(times + 1).join(str) -} + return Array(times + 1).join(str); +}; /** * Truncates a string @@ -18,9 +17,9 @@ exports.repeat = function (str, times) { */ exports.truncate = function (str, length, chr) { - chr = chr || '…' - return str.length >= length ? str.substr(0, length - chr.length) + chr : str -} + chr = chr || "…"; + return str.length >= length ? str.substr(0, length - chr.length) + chr : str; +}; /** * Copies and merges options with defaults. @@ -30,19 +29,22 @@ exports.truncate = function (str, length, chr) { * @return {Object} new (merged) object */ -function options (defaults, opts) { +function options(defaults, opts) { for (var p in opts) { + if (p === "__proto__" || p === "constructor" || p === "prototype") { + continue; + } if (opts[p] && opts[p].constructor && opts[p].constructor === Object) { - defaults[p] = defaults[p] || {} - options(defaults[p], opts[p]) + defaults[p] = defaults[p] || {}; + options(defaults[p], opts[p]); } else { - defaults[p] = opts[p] + defaults[p] = opts[p]; } } - return defaults -}; -exports.options = options + return defaults; +} +exports.options = options; // // For consideration of terminal "color" programs like colors.js, @@ -52,8 +54,10 @@ exports.options = options // see: http://en.wikipedia.org/wiki/ANSI_escape_code // exports.strlen = function (str) { - var code = /\u001b\[(?:\d*;){0,5}\d*m/g - var stripped = ('' + (str != null ? str : '')).replace(code, '') - var split = stripped.split('\n') - return split.reduce(function (memo, s) { return (s.length > memo) ? s.length : memo }, 0) -} + var code = /\u001b\[(?:\d*;){0,5}\d*m/g; + var stripped = ("" + (str != null ? str : "")).replace(code, ""); + var split = stripped.split("\n"); + return split.reduce(function (memo, s) { + return s.length > memo ? s.length : memo; + }, 0); +}; diff --git a/package.json b/package.json index 97c40b7..c97e9e0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cli-tableau", "description": "Pretty unicode tables for the CLI", - "version": "2.0.1", + "version": "2.0.2", "engines": { "node": ">=8.10.0" }, @@ -34,8 +34,8 @@ "chalk": "3.0.0" }, "devDependencies": { - "should": "~0.6", - "mocha": "^7.1.1" + "mocha": "^10.2.0", + "should": "~0.6" }, "main": "lib", "files": [ @@ -45,4 +45,4 @@ "test": "mocha test/*" }, "licence": "MIT" -} +} \ No newline at end of file diff --git a/test/index.mocha.js b/test/index.mocha.js index 2c07fd9..bcd70ff 100644 --- a/test/index.mocha.js +++ b/test/index.mocha.js @@ -1,313 +1,352 @@ - - /** * Module requirements. */ -require('should') +require("should"); -var Table = require('../') +var Table = require("../"); /** * Tests. */ -describe('Table', function() { - it('should test complete table ', () => { - var table = new Table({ - head: ['Rel', 'Change', 'By', 'When'], - style: { - 'padding-left': 1, - 'padding-right': 1, - head: [], - border: [] - }, - colWidths: [6, 21, 25, 17] - }) - - table.push( - ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '7 minutes ago'], - ['v0.1', 'Testing something cool', 'rauchg@gmail.com', '8 minutes ago'] - ) - - var expected = [ - '┌──────┬─────────────────────┬─────────────────────────┬─────────────────┐', - '│ Rel │ Change │ By │ When │', - '├──────┼─────────────────────┼─────────────────────────┼─────────────────┤', - '│ v0.1 │ Testing something … │ rauchg@gmail.com │ 7 minutes ago │', - '├──────┼─────────────────────┼─────────────────────────┼─────────────────┤', - '│ v0.1 │ Testing something … │ rauchg@gmail.com │ 8 minutes ago │', - '└──────┴─────────────────────┴─────────────────────────┴─────────────────┘' - ] - - table.toString().should.eql(expected.join('\n')) - table.render().should.eql(expected.join('\n')) - }) - - it('test width property', function () { +describe("Table", function () { + it("should test complete table ", () => { + var table = new Table({ + head: ["Rel", "Change", "By", "When"], + style: { + "padding-left": 1, + "padding-right": 1, + head: [], + border: [], + }, + colWidths: [6, 21, 25, 17], + }); + + table.push( + ["v0.1", "Testing something cool", "rauchg@gmail.com", "7 minutes ago"], + ["v0.1", "Testing something cool", "rauchg@gmail.com", "8 minutes ago"] + ); + + var expected = [ + "┌──────┬─────────────────────┬─────────────────────────┬─────────────────┐", + "│ Rel │ Change │ By │ When │", + "├──────┼─────────────────────┼─────────────────────────┼─────────────────┤", + "│ v0.1 │ Testing something … │ rauchg@gmail.com │ 7 minutes ago │", + "├──────┼─────────────────────┼─────────────────────────┼─────────────────┤", + "│ v0.1 │ Testing something … │ rauchg@gmail.com │ 8 minutes ago │", + "└──────┴─────────────────────┴─────────────────────────┴─────────────────┘", + ]; + + table.toString().should.eql(expected.join("\n")); + table.render().should.eql(expected.join("\n")); + }); + + it("test width property", function () { var table = new Table({ - head: ['Cool'], + head: ["Cool"], style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.width.should.eql(8) - }) + table.width.should.eql(8); + }); - it('test vertical table output', function () { - var table = new Table({ style: {'padding-left': 0, 'padding-right': 0, head: [], border: []} }) // clear styles to prevent color output + it("test vertical table output", function () { + var table = new Table({ + style: { "padding-left": 0, "padding-right": 0, head: [], border: [] }, + }); // clear styles to prevent color output table.push( - {'v0.1': 'Testing something cool'}, - {'v0.1': 'Testing something cool'} - ) + { "v0.1": "Testing something cool" }, + { "v0.1": "Testing something cool" } + ); var expected = [ - '┌────┬──────────────────────┐', - '│v0.1│Testing something cool│', - '├────┼──────────────────────┤', - '│v0.1│Testing something cool│', - '└────┴──────────────────────┘' - ] + "┌────┬──────────────────────┐", + "│v0.1│Testing something cool│", + "├────┼──────────────────────┤", + "│v0.1│Testing something cool│", + "└────┴──────────────────────┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test cross table output', function () { - var table = new Table({ head: ['', 'Header 1', 'Header 2'], style: {'padding-left': 0, 'padding-right': 0, head: [], border: []} }) // clear styles to prevent color output + it("test cross table output", function () { + var table = new Table({ + head: ["", "Header 1", "Header 2"], + style: { "padding-left": 0, "padding-right": 0, head: [], border: [] }, + }); // clear styles to prevent color output table.push( - {'Header 3': ['v0.1', 'Testing something cool']}, - {'Header 4': ['v0.1', 'Testing something cool']} - ) + { "Header 3": ["v0.1", "Testing something cool"] }, + { "Header 4": ["v0.1", "Testing something cool"] } + ); var expected = [ - '┌────────┬────────┬──────────────────────┐', - '│ │Header 1│Header 2 │', - '├────────┼────────┼──────────────────────┤', - '│Header 3│v0.1 │Testing something cool│', - '├────────┼────────┼──────────────────────┤', - '│Header 4│v0.1 │Testing something cool│', - '└────────┴────────┴──────────────────────┘' - ] - - table.toString().should.eql(expected.join('\n')) - }) - - it('test table colors', function () { + "┌────────┬────────┬──────────────────────┐", + "│ │Header 1│Header 2 │", + "├────────┼────────┼──────────────────────┤", + "│Header 3│v0.1 │Testing something cool│", + "├────────┼────────┼──────────────────────┤", + "│Header 4│v0.1 │Testing something cool│", + "└────────┴────────┴──────────────────────┘", + ]; + + table.toString().should.eql(expected.join("\n")); + }); + + it("test table colors", function () { var table = new Table({ - head: ['Rel', 'By'], - style: {head: ['red'], border: ['grey']} - }) + head: ["Rel", "By"], + style: { head: ["red"], border: ["grey"] }, + }); - var off = '\u001b[39m' - var red = '\u001b[31m' - var orange = '\u001b[38;5;221m' - var grey = '\u001b[90m' + var off = "\u001b[39m"; + var red = "\u001b[31m"; + var orange = "\u001b[38;5;221m"; + var grey = "\u001b[90m"; - c256s = orange + 'v0.1' + off + c256s = orange + "v0.1" + off; - table.push([c256s, 'rauchg@gmail.com']) + table.push([c256s, "rauchg@gmail.com"]); var expected = [ - grey + '┌──────┬──────────────────┐' + off, - grey + '│' + off + red + ' Rel ' + off + grey + '│' + off + red + ' By ' + off + grey + '│' + off, - grey + '├──────┼──────────────────┤' + off, - grey + '│' + off + ' ' + c256s + ' ' + grey + '│' + off + ' rauchg@gmail.com ' + grey + '│' + off, - grey + '└──────┴──────────────────┘' + off - ] - - table.toString().should.eql(expected.join('\n')) - }) - - it('test custom chars', function () { + grey + "┌──────┬──────────────────┐" + off, + grey + + "│" + + off + + red + + " Rel " + + off + + grey + + "│" + + off + + red + + " By " + + off + + grey + + "│" + + off, + grey + "├──────┼──────────────────┤" + off, + grey + + "│" + + off + + " " + + c256s + + " " + + grey + + "│" + + off + + " rauchg@gmail.com " + + grey + + "│" + + off, + grey + "└──────┴──────────────────┘" + off, + ]; + + table.toString().should.eql(expected.join("\n")); + }); + + it("test custom chars", function () { var table = new Table({ chars: { - 'top': '═', - 'top-mid': '╤', - 'top-left': '╔', - 'top-right': '╗', - 'bottom': '═', - 'bottom-mid': '╧', - 'bottom-left': '╚', - 'bottom-right': '╝', - 'left': '║', - 'left-mid': '╟', - 'right': '║', - 'right-mid': '╢' + top: "═", + "top-mid": "╤", + "top-left": "╔", + "top-right": "╗", + bottom: "═", + "bottom-mid": "╧", + "bottom-left": "╚", + "bottom-right": "╝", + left: "║", + "left-mid": "╟", + right: "║", + "right-mid": "╢", }, style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.push(['foo', 'bar', 'baz'], ['frob', 'bar', 'quuz']) + table.push(["foo", "bar", "baz"], ["frob", "bar", "quuz"]); var expected = [ - '╔══════╤═════╤══════╗', - '║ foo │ bar │ baz ║', - '╟──────┼─────┼──────╢', - '║ frob │ bar │ quuz ║', - '╚══════╧═════╧══════╝' - ] + "╔══════╤═════╤══════╗", + "║ foo │ bar │ baz ║", + "╟──────┼─────┼──────╢", + "║ frob │ bar │ quuz ║", + "╚══════╧═════╧══════╝", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test compact shortand', function () { + it("test compact shortand", function () { var table = new Table({ style: { head: [], border: [], - compact: true - } - }) + compact: true, + }, + }); - table.push(['foo', 'bar', 'baz'], ['frob', 'bar', 'quuz']) + table.push(["foo", "bar", "baz"], ["frob", "bar", "quuz"]); var expected = [ - '┌──────┬─────┬──────┐', - '│ foo │ bar │ baz │', - '│ frob │ bar │ quuz │', - '└──────┴─────┴──────┘' - ] + "┌──────┬─────┬──────┐", + "│ foo │ bar │ baz │", + "│ frob │ bar │ quuz │", + "└──────┴─────┴──────┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test compact empty mid line', function () { + it("test compact empty mid line", function () { var table = new Table({ chars: { - 'mid': '', - 'left-mid': '', - 'mid-mid': '', - 'right-mid': '' + mid: "", + "left-mid": "", + "mid-mid": "", + "right-mid": "", }, style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.push(['foo', 'bar', 'baz'], ['frob', 'bar', 'quuz']) + table.push(["foo", "bar", "baz"], ["frob", "bar", "quuz"]); var expected = [ - '┌──────┬─────┬──────┐', - '│ foo │ bar │ baz │', - '│ frob │ bar │ quuz │', - '└──────┴─────┴──────┘' - ] + "┌──────┬─────┬──────┐", + "│ foo │ bar │ baz │", + "│ frob │ bar │ quuz │", + "└──────┴─────┴──────┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test decoration lines disabled', function () { + it("test decoration lines disabled", function () { var table = new Table({ chars: { - 'top': '', - 'top-mid': '', - 'top-left': '', - 'top-right': '', - 'bottom': '', - 'bottom-mid': '', - 'bottom-left': '', - 'bottom-right': '', - 'left': '', - 'left-mid': '', - 'mid': '', - 'mid-mid': '', - 'right': '', - 'right-mid': '', - 'middle': ' ' // a single space + top: "", + "top-mid": "", + "top-left": "", + "top-right": "", + bottom: "", + "bottom-mid": "", + "bottom-left": "", + "bottom-right": "", + left: "", + "left-mid": "", + mid: "", + "mid-mid": "", + right: "", + "right-mid": "", + middle: " ", // a single space }, style: { head: [], border: [], - 'padding-left': 0, - 'padding-right': 0 - } - }) + "padding-left": 0, + "padding-right": 0, + }, + }); - table.push(['foo', 'bar', 'baz'], ['frobnicate', 'bar', 'quuz']) + table.push(["foo", "bar", "baz"], ["frobnicate", "bar", "quuz"]); - var expected = [ - 'foo bar baz ', - 'frobnicate bar quuz' - ] + var expected = ["foo bar baz ", "frobnicate bar quuz"]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test with null/undefined as values or column names', function () { + it("test with null/undefined as values or column names", function () { var table = new Table({ - head: [null, undefined, 0, ''], + head: [null, undefined, 0, ""], style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.push( - [null, undefined, 0, ''] - ) + table.push([null, undefined, 0, ""]); var expected = [ - '┌──┬──┬───┬──┐', - '│ │ │ 0 │ │', - '├──┼──┼───┼──┤', - '│ │ │ 0 │ │', - '└──┴──┴───┴──┘' - ] - - table.toString().should.eql(expected.join('\n')) - }) + "┌──┬──┬───┬──┐", + "│ │ │ 0 │ │", + "├──┼──┼───┼──┤", + "│ │ │ 0 │ │", + "└──┴──┴───┴──┘", + ]; - it('test with toString things', function() { + table.toString().should.eql(expected.join("\n")); + }); + it("test with toString things", function () { var table = new Table({ - head: ['Some test toString', 'foo'], + head: ["Some test toString", "foo"], style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.push([{}, {toString: function() { return 'foo'}}]) + table.push([ + {}, + { + toString: function () { + return "foo"; + }, + }, + ]); var expected = [ - '┌────────────────────┬─────┐', - '│ Some test toString │ foo │', - '├────────────────────┼─────┤', - '│ [object Object] │ foo │', - '└────────────────────┴─────┘' - ] - - table.toString().should.eql(expected.join('\n')) - }) + "┌────────────────────┬─────┐", + "│ Some test toString │ foo │", + "├────────────────────┼─────┤", + "│ [object Object] │ foo │", + "└────────────────────┴─────┘", + ]; - it('test with array method values', function() { + table.toString().should.eql(expected.join("\n")); + }); + it("test with array method values", function () { var table = new Table({ - head: ['pop', 'push', 'slice'], + head: ["pop", "push", "slice"], style: { head: [], - border: [] - } - }) + border: [], + }, + }); - table.push(['push', 'slice', 'pop']) + table.push(["push", "slice", "pop"]); var expected = [ - '┌──────┬───────┬───────┐', - '│ pop │ push │ slice │', - '├──────┼───────┼───────┤', - '│ push │ slice │ pop │', - '└──────┴───────┴───────┘' - ] - - table.toString().should.eql(expected.join('\n')) - }) -}) + "┌──────┬───────┬───────┐", + "│ pop │ push │ slice │", + "├──────┼───────┼───────┤", + "│ push │ slice │ pop │", + "└──────┴───────┴───────┘", + ]; + + table.toString().should.eql(expected.join("\n")); + }); +}); + +describe("Table Securities", function () { + it("test prototype pollution", function () { + let attackerObject = + '{"__proto__":{"attackerControlledValue":"Attackers Payload"},"proto":{"attackPropFromProto":"changed"},"constructor":{"prototype":{"attackPropFromConstructorProto":"changed"}}}'; + var table = new Table(JSON.parse(attackerObject)); + + table.options.should.not.have.property("attackerControlledValue"); + }); +}); diff --git a/test/newline.mocha.js b/test/newline.mocha.js index 8f5798e..86714d5 100644 --- a/test/newline.mocha.js +++ b/test/newline.mocha.js @@ -2,91 +2,97 @@ * Module requirements. */ -require('should') +require("should"); -var Table = require('../') +var Table = require("../"); /** * Tests. */ -describe('', function() { - it('test table with newlines in headers', function () { +describe("New line", function () { + it("test table with newlines in headers", function () { var table = new Table({ - head: ['Test', '1\n2\n3'], + head: ["Test", "1\n2\n3"], style: { - 'padding-left': 1, - 'padding-right': 1, + "padding-left": 1, + "padding-right": 1, head: [], - border: [] - } - }) + border: [], + }, + }); var expected = [ - '┌──────┬───┐', - '│ Test │ 1 │', - '│ │ 2 │', - '│ │ 3 │', - '└──────┴───┘' - ] + "┌──────┬───┐", + "│ Test │ 1 │", + "│ │ 2 │", + "│ │ 3 │", + "└──────┴───┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test column width is accurately reflected when newlines are present', function () { - var table = new Table({ head: ['Test\nWidth'], style: {head: [], border: []} }) - table.width.should.eql(9) - }) + it("test column width is accurately reflected when newlines are present", function () { + var table = new Table({ + head: ["Test\nWidth"], + style: { head: [], border: [] }, + }); + table.width.should.eql(9); + }); - it('test newlines in body cells', function () { - var table = new Table({style: {head: [], border: []}}) + it("test newlines in body cells", function () { + var table = new Table({ style: { head: [], border: [] } }); - table.push(['something\nwith\nnewlines']) + table.push(["something\nwith\nnewlines"]); var expected = [ - '┌───────────┐', - '│ something │', - '│ with │', - '│ newlines │', - '└───────────┘' - ] + "┌───────────┐", + "│ something │", + "│ with │", + "│ newlines │", + "└───────────┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test newlines in vertical cell header and body', function () { - var table = new Table({ style: {'padding-left': 0, 'padding-right': 0, head: [], border: []} }) + it("test newlines in vertical cell header and body", function () { + var table = new Table({ + style: { "padding-left": 0, "padding-right": 0, head: [], border: [] }, + }); - table.push( - {'v\n0.1': 'Testing\nsomething cool'} - ) + table.push({ "v\n0.1": "Testing\nsomething cool" }); var expected = [ - '┌───┬──────────────┐', - '│v │Testing │', - '│0.1│something cool│', - '└───┴──────────────┘' - ] + "┌───┬──────────────┐", + "│v │Testing │", + "│0.1│something cool│", + "└───┴──────────────┘", + ]; - table.toString().should.eql(expected.join('\n')) - }) + table.toString().should.eql(expected.join("\n")); + }); - it('test newlines in cross table header and body', function () { - var table = new Table({ head: ['', 'Header\n1'], style: {'padding-left': 0, 'padding-right': 0, head: [], border: []} }) + it("test newlines in cross table header and body", function () { + var table = new Table({ + head: ["", "Header\n1"], + style: { "padding-left": 0, "padding-right": 0, head: [], border: [] }, + }); - table.push({ 'Header\n2': ['Testing\nsomething\ncool'] }) + table.push({ "Header\n2": ["Testing\nsomething\ncool"] }); var expected = [ - '┌──────┬─────────┐', - '│ │Header │', - '│ │1 │', - '├──────┼─────────┤', - '│Header│Testing │', - '│2 │something│', - '│ │cool │', - '└──────┴─────────┘' - ] - - table.toString().should.eql(expected.join('\n')) - }) -}) + "┌──────┬─────────┐", + "│ │Header │", + "│ │1 │", + "├──────┼─────────┤", + "│Header│Testing │", + "│2 │something│", + "│ │cool │", + "└──────┴─────────┘", + ]; + + table.toString().should.eql(expected.join("\n")); + }); +});