From 4505c79f775aabc26f0f4b7ea7a2a17603490169 Mon Sep 17 00:00:00 2001 From: Appurva Murawat Date: Wed, 25 Sep 2024 01:18:28 +0530 Subject: [PATCH] Use native atob and btoa --- CHANGELOG.yaml | 1 + lib/bundle/bundling-options.js | 7 ++- lib/environment.js | 4 +- lib/sandbox/postman-legacy-interface.js | 6 +-- lib/vendor/atob.js | 1 + lib/vendor/btoa.js | 1 + lib/vendor/buffer/buffer.js | 2 + lib/vendor/buffer/index.browser.js | 4 +- package-lock.json | 38 -------------- package.json | 2 - test/system/bootcode-dependencies.test.js | 4 -- test/system/repository.test.js | 2 +- test/unit/sandbox-libraries/legacy.test.js | 4 +- .../unit/sandbox-libraries/pm-require.test.js | 2 - test/unit/vendors/atob.test.js | 50 +++++++++++++++++++ test/unit/vendors/btoa.test.js | 50 +++++++++++++++++++ .../buffer.test.js | 35 ++++++++++++- 17 files changed, 153 insertions(+), 60 deletions(-) create mode 100644 lib/vendor/atob.js create mode 100644 lib/vendor/btoa.js create mode 100644 test/unit/vendors/atob.test.js create mode 100644 test/unit/vendors/btoa.test.js rename test/unit/{sandbox-libraries => vendors}/buffer.test.js (88%) diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml index 83b28a33..53a6398a 100644 --- a/CHANGELOG.yaml +++ b/CHANGELOG.yaml @@ -5,6 +5,7 @@ unreleased: - GH-1032 Enhanced performance when operating on buffers in Node environment - GH-1035 Added missing buffer APIs to expose a uniform interface across environments - GH-1052 Added support URL, Encoding, File, Cryptography, and Stream globals + - GH-1049 Replaced shims for atob and btoa with native implementations fixed bugs: - GH-1036 Fixed `uncaughtException` event listener not being removed - GH-1034 Fixed an issue where sandbox crashes for large response body diff --git a/lib/bundle/bundling-options.js b/lib/bundle/bundling-options.js index 27b671be..4e8922b6 100644 --- a/lib/bundle/bundling-options.js +++ b/lib/bundle/bundling-options.js @@ -4,5 +4,10 @@ module.exports = { browserField: false, bare: true, builtins: false, - commondir: true + commondir: true, + + // This is to prevent bundling errors for modules that + // are not in node_modules but are instead imported from a + // vendor and should be exposed via `require` inside the bundle. + ignoreMissing: true }; diff --git a/lib/environment.js b/lib/environment.js index 756937ed..af9a82ba 100644 --- a/lib/environment.js +++ b/lib/environment.js @@ -24,8 +24,8 @@ module.exports = { os: { preferBuiltin: true }, 'liquid-json': { expose: 'json', glob: true }, 'crypto-js': { glob: true }, - atob: { glob: true }, - btoa: { glob: true }, + atob: { resolve: '../vendor/atob.js', expose: 'atob', glob: true }, + btoa: { resolve: '../vendor/btoa.js', expose: 'btoa', glob: true }, ajv: { glob: true }, tv4: { glob: true }, xml2js: { glob: true }, diff --git a/lib/sandbox/postman-legacy-interface.js b/lib/sandbox/postman-legacy-interface.js index cf8f3d64..a3a7d98f 100644 --- a/lib/sandbox/postman-legacy-interface.js +++ b/lib/sandbox/postman-legacy-interface.js @@ -5,8 +5,6 @@ const _ = require('lodash'), JSON: require('liquid-json'), _: require('lodash3').noConflict(), CryptoJS: require('crypto-js'), - atob: require('atob'), - btoa: require('btoa'), tv4: require('tv4'), xml2Json: require('./xml2Json'), Backbone: require('backbone'), @@ -17,7 +15,7 @@ const _ = require('lodash'), 'tests', 'globals', 'environment', 'data', 'request', 'responseCookies', 'responseHeaders', 'responseTime', 'responseCode', 'responseBody', 'iteration', 'postman', // scope libraries - '_', 'CryptoJS', 'atob', 'btoa', 'tv4', 'xml2Json', 'Backbone', 'cheerio' + '_', 'CryptoJS', 'tv4', 'xml2Json', 'Backbone', 'cheerio' // 'JSON', // removing JSON from the list since it is a standard JS object ], @@ -35,8 +33,6 @@ const _ = require('lodash'), iteration: 'pm.info.iteration', _: 'require(\'lodash\')', CryptoJS: 'require(\'crypto-js\')', - atob: 'require(\'atob\')', - btoa: 'require(\'btoa\')', tv4: 'require(\'ajv\')', xml2Json: 'require(\'xml2js\')', Backbone: 'require(\'backbone\')', diff --git a/lib/vendor/atob.js b/lib/vendor/atob.js new file mode 100644 index 00000000..a179a1ca --- /dev/null +++ b/lib/vendor/atob.js @@ -0,0 +1 @@ +module.exports = require('buffer').atob; diff --git a/lib/vendor/btoa.js b/lib/vendor/btoa.js new file mode 100644 index 00000000..f0517f39 --- /dev/null +++ b/lib/vendor/btoa.js @@ -0,0 +1 @@ +module.exports = require('buffer').btoa; diff --git a/lib/vendor/buffer/buffer.js b/lib/vendor/buffer/buffer.js index ee4306cf..e17ecdac 100644 --- a/lib/vendor/buffer/buffer.js +++ b/lib/vendor/buffer/buffer.js @@ -12,6 +12,8 @@ function getBufferModule (buffer) { constants: buffer.constants, File: buffer.File, Blob: buffer.Blob, + atob: buffer.atob, + btoa: buffer.btoa, isAscii: NOT_IMPLEMENTED, isUtf8: NOT_IMPLEMENTED, resolveObjectURL: NOT_IMPLEMENTED, diff --git a/lib/vendor/buffer/index.browser.js b/lib/vendor/buffer/index.browser.js index 0bddc8d7..6a5b4ca4 100644 --- a/lib/vendor/buffer/index.browser.js +++ b/lib/vendor/buffer/index.browser.js @@ -15,5 +15,7 @@ module.exports = getBufferModule({ MAX_STRING_LENGTH: K_STRING_MAX_LENGTH }, File: File, - Blob: Blob + Blob: Blob, + atob: atob, + btoa: btoa }); diff --git a/package-lock.json b/package-lock.json index 4675e1f0..f769fd5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,10 +21,8 @@ "ajv": "6.12.5", "assert": "2.0.0", "async": "^3.2.6", - "atob": "2.1.2", "backbone": "1.6.0", "browserify": "^16.5.2", - "btoa": "1.2.1", "buffer": "6.0.3", "chai": "4.4.1", "chai-postman": "2.0.1", @@ -1591,18 +1589,6 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", @@ -2002,18 +1988,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "dev": true, - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -10159,12 +10133,6 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, "available-typed-arrays": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", @@ -10526,12 +10494,6 @@ "pako": "~1.0.5" } }, - "btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "dev": true - }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", diff --git a/package.json b/package.json index 72e15854..07a5e294 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,8 @@ "ajv": "6.12.5", "assert": "2.0.0", "async": "^3.2.6", - "atob": "2.1.2", "backbone": "1.6.0", "browserify": "^16.5.2", - "btoa": "1.2.1", "buffer": "6.0.3", "chai": "4.4.1", "chai-postman": "2.0.1", diff --git a/test/system/bootcode-dependencies.test.js b/test/system/bootcode-dependencies.test.js index 2c5b93d3..d397e894 100644 --- a/test/system/bootcode-dependencies.test.js +++ b/test/system/bootcode-dependencies.test.js @@ -9,13 +9,11 @@ const expect = require('chai').expect, 'array-filter', 'assert', 'assertion-error', - 'atob', 'available-typed-arrays', 'backbone', 'base64-js', 'boolbase', 'browserify', - 'btoa', 'buffer', 'call-bind', 'chai', @@ -127,12 +125,10 @@ const expect = require('chai').expect, 'array-filter', 'assert', 'assertion-error', - 'atob', 'available-typed-arrays', 'backbone', 'boolbase', 'browserify', - 'btoa', 'call-bind', 'chai', 'chai-postman', diff --git a/test/system/repository.test.js b/test/system/repository.test.js index 97a96543..9ff5d00b 100644 --- a/test/system/repository.test.js +++ b/test/system/repository.test.js @@ -75,7 +75,7 @@ describe('project repository', function () { it('should point to specific package version for bundled packages; (*, ^, ~) not expected', function () { [ - 'ajv', 'assert', 'atob', 'backbone', 'btoa', 'buffer', 'chai', + 'ajv', 'assert', 'backbone', 'buffer', 'chai', 'chai-postman', 'cheerio', 'crypto-js', 'csv-parse', 'liquid-json', 'lodash3', 'moment', '@postman/tough-cookie', 'tv4', 'uniscope', 'xml2js' diff --git a/test/unit/sandbox-libraries/legacy.test.js b/test/unit/sandbox-libraries/legacy.test.js index 3b3c5b3f..bd5870a9 100644 --- a/test/unit/sandbox-libraries/legacy.test.js +++ b/test/unit/sandbox-libraries/legacy.test.js @@ -55,7 +55,7 @@ describe('sandbox library - legacy', function () { context.on('console', consoleSpy); context.execute(` - atob('a'); + CryptoJS.AES.encrypt('my message', 'secret key 123') `, function (err) { if (err) { return done(err); @@ -64,7 +64,7 @@ describe('sandbox library - legacy', function () { expect(consoleSpy).to.be.calledOnce; expect(consoleSpy.firstCall.args[1]).to.equal('warn'); expect(consoleSpy.firstCall.args[2]) - .to.equal('Using "atob" is deprecated. Use "require(\'atob\')" instead.'); + .to.equal('Using "CryptoJS" is deprecated. Use "require(\'crypto-js\')" instead.'); done(); }); }); diff --git a/test/unit/sandbox-libraries/pm-require.test.js b/test/unit/sandbox-libraries/pm-require.test.js index de9bc273..56c48795 100644 --- a/test/unit/sandbox-libraries/pm-require.test.js +++ b/test/unit/sandbox-libraries/pm-require.test.js @@ -536,8 +536,6 @@ describe('sandbox library - pm.require api', function () { // scope libraries '_', 'CryptoJS', - 'atob', - 'btoa', 'tv4', 'xml2Json', 'Backbone', diff --git a/test/unit/vendors/atob.test.js b/test/unit/vendors/atob.test.js new file mode 100644 index 00000000..1c56f112 --- /dev/null +++ b/test/unit/vendors/atob.test.js @@ -0,0 +1,50 @@ +describe('sandbox vendor - atob', function () { + this.timeout(1000 * 60); + var Sandbox = require('../../../'), + context; + + beforeEach(function (done) { + Sandbox.createContext({ debug: true }, function (err, ctx) { + context = ctx; + done(err); + }); + }); + + afterEach(function () { + context.dispose(); + context = null; + }); + + it('should exist in global', function (done) { + context.execute(` + const assert = require('assert'); + assert.strictEqual(typeof atob, 'function', 'typeof atob must be function'); + `, done); + }); + + it('should be exposed via require', function (done) { + context.execute(` + const assert = require('assert'); + const atob = require('atob'); + assert.strictEqual(typeof atob, 'function', 'typeof atob must be function'); + `, done); + }); + + it('should have same implementation exposed via global, require and buffer', function (done) { + context.execute(` + const assert = require('assert'); + const requiredAtob = require('atob'); + const bufferAtob = require('buffer').atob; + assert.strictEqual(atob === requiredAtob, true); + assert.strictEqual(atob === bufferAtob, true); + `, done); + }); + + it('should decode base64 encoded string', function (done) { + context.execute(` + const assert = require('assert'); + const decoded = atob('cG9zdG1hbi1zYW5kYm94'); + assert.strictEqual(decoded, 'postman-sandbox'); + `, done); + }); +}); diff --git a/test/unit/vendors/btoa.test.js b/test/unit/vendors/btoa.test.js new file mode 100644 index 00000000..e6b58272 --- /dev/null +++ b/test/unit/vendors/btoa.test.js @@ -0,0 +1,50 @@ +describe('sandbox vendor - btoa', function () { + this.timeout(1000 * 60); + var Sandbox = require('../../../'), + context; + + beforeEach(function (done) { + Sandbox.createContext({ debug: true }, function (err, ctx) { + context = ctx; + done(err); + }); + }); + + afterEach(function () { + context.dispose(); + context = null; + }); + + it('should exist in global', function (done) { + context.execute(` + const assert = require('assert'); + assert.strictEqual(typeof btoa, 'function', 'typeof btoa must be function'); + `, done); + }); + + it('should be exposed via require', function (done) { + context.execute(` + const assert = require('assert'); + const btoa = require('btoa'); + assert.strictEqual(typeof btoa, 'function', 'typeof btoa must be function'); + `, done); + }); + + it('should have same implementation exposed via global, require and buffer', function (done) { + context.execute(` + const assert = require('assert'); + const requiredBtoa = require('btoa'); + const bufferBtoa = require('buffer').btoa; + assert.strictEqual(btoa === requiredBtoa, true); + assert.strictEqual(btoa === bufferBtoa, true); + `, done); + }); + + it('should encode a string to base64', function (done) { + context.execute(` + const assert = require('assert'); + const decoded = btoa('postman-sandbox'); + assert.strictEqual(decoded, 'cG9zdG1hbi1zYW5kYm94'); + `, done); + }); +}); diff --git a/test/unit/sandbox-libraries/buffer.test.js b/test/unit/vendors/buffer.test.js similarity index 88% rename from test/unit/sandbox-libraries/buffer.test.js rename to test/unit/vendors/buffer.test.js index 899bea1f..622c2327 100644 --- a/test/unit/sandbox-libraries/buffer.test.js +++ b/test/unit/vendors/buffer.test.js @@ -1,4 +1,6 @@ -describe('sandbox library - buffer', function () { +const isNode = typeof window === 'undefined'; + +describe('sandbox vendor - buffer', function () { this.timeout(1000 * 60); var Sandbox = require('../../../'), context; @@ -157,7 +159,7 @@ describe('sandbox library - buffer', function () { it('should be able to convert large buffer to string', function (done) { // For native buffer, the max string length is ~512MB // For browser buffer, the max string length is ~100MB - const SIZE = (typeof window === 'undefined' ? 511 : 100) * 1024 * 1024; + const SIZE = (isNode ? 511 : 100) * 1024 * 1024; context.execute(` const assert = require('assert'), @@ -286,4 +288,33 @@ describe('sandbox library - buffer', function () { assert.strictEqual(Blob === bufferBlob, true); `, done); }); + + (isNode ? it : it.skip)('should be in sync with latest available `buffer` module', function (done) { + const buffer = require('buffer'), + expectedProps = Object.getOwnPropertyNames(buffer).sort(); + + context.execute(` + const assert = require('assert'); + const buffer = require('buffer'); + const actualProps = Object.getOwnPropertyNames(buffer).sort(); + + assert.deepStrictEqual(actualProps, ${JSON.stringify(expectedProps)}); + `, done); + }); + + (isNode ? it : it.skip)('should be in sync with latest available `Buffer` class', function (done) { + const fnProps = Object.getOwnPropertyNames(function () { return 0; }).sort(), + expectedProps = Object.getOwnPropertyNames(Buffer).sort().filter((prop) => { + return !fnProps.includes(prop); + }); + + context.execute(` + const assert = require('assert'); + const actualProps = Object.getOwnPropertyNames(Buffer).sort().filter((prop) => { + return !${JSON.stringify(fnProps)}.includes(prop); + }); + + assert.deepStrictEqual(actualProps, ${JSON.stringify(expectedProps)}); + `, done); + }); });