From dc9908674e717ad79112f223e28b8c2f37442b87 Mon Sep 17 00:00:00 2001 From: David Evans Date: Thu, 7 Jan 2021 19:30:11 +0000 Subject: [PATCH 1/3] Replace function constructor with defineProperty to preserve arity without eval [#41] --- index.js | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index 1bf2fcf..0742fd9 100644 --- a/index.js +++ b/index.js @@ -64,20 +64,6 @@ function convertDataDescriptorToAccessor (obj, prop, message) { return descriptor } -/** - * Create arguments string to keep arity. - */ - -function createArgumentsString (arity) { - var str = '' - - for (var i = 0; i < arity; i++) { - str += ', arg' + i - } - - return str.substr(2) -} - /** * Create stack string from stack. */ @@ -415,20 +401,17 @@ function wrapfunction (fn, message) { throw new TypeError('argument fn must be a function') } - var args = createArgumentsString(fn.length) + var deprecate = this var stack = getStack() var site = callSiteLocation(stack[1]) site.name = fn.name - // eslint-disable-next-line no-new-func - var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', - '"use strict"\n' + - 'return function (' + args + ') {' + - 'log.call(deprecate, message, site)\n' + - 'return fn.apply(this, arguments)\n' + - '}')(fn, log, this, message, site) - + function deprecatedfn () { + log.call(deprecate, message, site) + return fn.apply(this, arguments) + } + Object.defineProperty(deprecatedfn, 'length', { value: fn.length }) return deprecatedfn } From 71460f24adc6d1171f5db85af4e585e6658c7c39 Mon Sep 17 00:00:00 2001 From: David Evans Date: Thu, 7 Jan 2021 20:14:03 +0000 Subject: [PATCH 2/3] Put in fallback to old code for super-old platforms --- index.js | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 0742fd9..f5a8ba3 100644 --- a/index.js +++ b/index.js @@ -360,6 +360,20 @@ function formatLocation (callSite) { ':' + callSite[2] } +/** + * Create arguments string to keep arity. + */ + +function createArgumentsString (arity) { + var str = '' + + for (var i = 0; i < arity; i++) { + str += ', arg' + i + } + + return str.substr(2) +} + /** * Get the stack as array of call sites. */ @@ -407,11 +421,23 @@ function wrapfunction (fn, message) { site.name = fn.name - function deprecatedfn () { + var deprecatedfn = function () { log.call(deprecate, message, site) return fn.apply(this, arguments) } - Object.defineProperty(deprecatedfn, 'length', { value: fn.length }) + try { + Object.defineProperty(deprecatedfn, 'length', { value: fn.length }) + } catch (e) { + // Fallback for NodeJS 2.5 and below + var args = createArgumentsString(fn.length) + // eslint-disable-next-line no-new-func + deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', + '"use strict"\n' + + 'return function (' + args + ') {' + + 'log.call(deprecate, message, site)\n' + + 'return fn.apply(this, arguments)\n' + + '}')(fn, log, this, message, site) + } return deprecatedfn } From 9ee652b9e9bfdf6372e4a0e3c217f60d3695a69a Mon Sep 17 00:00:00 2001 From: David Evans Date: Mon, 29 Mar 2021 15:47:43 +0100 Subject: [PATCH 3/3] Add unit test for --disallow-code-generation-from-strings --- test/fixtures/basic.js | 8 ++++++++ test/test.js | 29 ++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/basic.js diff --git a/test/fixtures/basic.js b/test/fixtures/basic.js new file mode 100644 index 0000000..320a3aa --- /dev/null +++ b/test/fixtures/basic.js @@ -0,0 +1,8 @@ + +var deprecate = require('../..')('basic') + +var object = { foo: 'bar' } +deprecate.property(object, 'foo') + +function fn () {} +deprecate.function(fn) diff --git a/test/test.js b/test/test.js index 5a66253..5e5bce9 100644 --- a/test/test.js +++ b/test/test.js @@ -10,6 +10,16 @@ var script = path.join(__dirname, 'fixtures', 'script.js') var spawn = require('child_process').spawn var strictlib = libs.strict +function isNodeVersionGE (required) { + var nodeVersion = process.version.substr(1).split('.') + for (var i = 0; i < required.length; i++) { + if (+nodeVersion[i] < required[i]) { + return false + } + } + return true +} + describe('depd(namespace)', function () { it('creates deprecated function', function () { assert.strictEqual(typeof depd('test'), 'function') @@ -730,9 +740,9 @@ describe('node script.js', function () { ;(function () { // --*-deprecation switches are 0.8+ // no good way to feature detect this sync - var describe = /^v0\.6\./.test(process.version) - ? global.describe.skip - : global.describe + var describe = isNodeVersionGE([0, 8]) + ? global.describe + : global.describe.skip describe('node --no-deprecation script.js', function () { it('should suppress deprecation message', function (done) { @@ -755,6 +765,19 @@ describe('node script.js', function () { }) }()) +describe('node --disallow-code-generation-from-strings script.js', function () { + it('should run without error', function (done) { + if (!isNodeVersionGE([9])) this.skip() // --disallow-code-generation-from-strings is 9+ + + var basic = path.join(__dirname, 'fixtures', 'basic.js') + captureChildStderr(basic, ['--disallow-code-generation-from-strings'], function (err, stderr) { + if (err) return done(err) + assert.strictEqual(stderr, '') + done() + }) + }) +}) + function captureChildStderr (script, opts, callback) { var chunks = [] var env = { PATH: process.env.PATH }