From a5f0a4b11ad013c0620f4ad9d3bd82ee6cde6b22 Mon Sep 17 00:00:00 2001 From: KobeNguyenT <7845001+kobenguyent@users.noreply.github.com> Date: Fri, 30 Jun 2023 06:59:32 +0200 Subject: [PATCH] fix: secret function (#3707) --- docs/helpers/REST.md | 8 ++++---- lib/helper/REST.js | 17 +++++++++++++---- lib/secret.js | 9 +++++---- lib/step.js | 7 ++++++- test/rest/REST_test.js | 11 ++++++----- test/unit/secret_test.js | 19 +++++++++++++++++++ 6 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 test/unit/secret_test.js diff --git a/docs/helpers/REST.md b/docs/helpers/REST.md index 05019167e..a23760624 100644 --- a/docs/helpers/REST.md +++ b/docs/helpers/REST.md @@ -115,7 +115,7 @@ I.sendDeleteRequest('/api/users/1'); #### Parameters - `url` **any** -- `headers` **[object][4]** the headers object to be sent. By default it is sent as an empty object +- `headers` **[object][4]** the headers object to be sent. By default, it is sent as an empty object Returns **[Promise][2]<any>** response @@ -130,7 +130,7 @@ I.sendGetRequest('/api/users.json'); #### Parameters - `url` **any** -- `headers` **[object][4]** the headers object to be sent. By default it is sent as an empty object +- `headers` **[object][4]** the headers object to be sent. By default, it is sent as an empty object Returns **[Promise][2]<any>** response @@ -167,8 +167,8 @@ I.sendPostRequest('/api/users.json', secret({ "email": "user@user.com" })); #### Parameters - `url` **any** -- `payload` **any** the payload to be sent. By default it is sent as an empty object -- `headers` **[object][4]** the headers object to be sent. By default it is sent as an empty object +- `payload` **any** the payload to be sent. By default, it is sent as an empty object +- `headers` **[object][4]** the headers object to be sent. By default, it is sent as an empty object Returns **[Promise][2]<any>** response diff --git a/lib/helper/REST.js b/lib/helper/REST.js index 182f948c9..150466155 100644 --- a/lib/helper/REST.js +++ b/lib/helper/REST.js @@ -137,6 +137,15 @@ class REST extends Helper { request.auth = this.headers.auth; } + if (typeof request.data === 'object') { + const returnedValue = {}; + for (const [key, value] of Object.entries(request.data)) { + returnedValue[key] = value; + if (value instanceof Secret) returnedValue[key] = value.getMasked(); + } + _debugRequest.data = returnedValue; + } + if (request.data instanceof Secret) { _debugRequest.data = '*****'; request.data = (typeof request.data === 'object' && !(request.data instanceof Secret)) ? { ...request.data.toString() } : request.data.toString(); @@ -199,7 +208,7 @@ class REST extends Helper { * ``` * * @param {*} url - * @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object + * @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object * * @returns {Promise<*>} response */ @@ -223,8 +232,8 @@ class REST extends Helper { * ``` * * @param {*} url - * @param {*} [payload={}] - the payload to be sent. By default it is sent as an empty object - * @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object + * @param {*} [payload={}] - the payload to be sent. By default, it is sent as an empty object + * @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object * * @returns {Promise<*>} response */ @@ -318,7 +327,7 @@ class REST extends Helper { * ``` * * @param {*} url - * @param {object} [headers={}] - the headers object to be sent. By default it is sent as an empty object + * @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object * * @returns {Promise<*>} response */ diff --git a/lib/secret.js b/lib/secret.js index cde2c5d8a..38786bcf6 100644 --- a/lib/secret.js +++ b/lib/secret.js @@ -1,6 +1,8 @@ /* eslint-disable max-classes-per-file */ const { deepClone } = require('./utils'); +const maskedString = '*****'; + /** @param {string} secret */ class Secret { constructor(secret) { @@ -13,7 +15,7 @@ class Secret { } getMasked() { - return '*****'; + return maskedString; } /** @@ -36,12 +38,11 @@ function secretObject(obj, fieldsToHide = []) { if (prop === 'toString') { return function () { const maskedObject = deepClone(obj); - fieldsToHide.forEach(f => maskedObject[f] = '****'); + fieldsToHide.forEach(f => maskedObject[f] = maskedString); return JSON.stringify(maskedObject); }; } - - return obj[prop]; + return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]; }, }; diff --git a/lib/step.js b/lib/step.js index 8ab23de7e..2e642f230 100644 --- a/lib/step.js +++ b/lib/step.js @@ -172,7 +172,12 @@ class Step { } else if (arg.toString && arg.toString() !== '[object Object]') { return arg.toString(); } else if (typeof arg === 'object') { - return JSON.stringify(arg); + const returnedArg = {}; + for (const [key, value] of Object.entries(arg)) { + returnedArg[key] = value; + if (value instanceof Secret) returnedArg[key] = value.getMasked(); + } + return JSON.stringify(returnedArg); } return arg; }).join(', '); diff --git a/test/rest/REST_test.js b/test/rest/REST_test.js index dc7bdc5d7..775e4cbc7 100644 --- a/test/rest/REST_test.js +++ b/test/rest/REST_test.js @@ -1,11 +1,12 @@ const path = require('path'); +const expect = require('expect'); const fs = require('fs'); const FormData = require('form-data'); -const { secret } = require('../../lib/secret'); const TestHelper = require('../support/TestHelper'); const REST = require('../../lib/helper/REST'); const Container = require('../../lib/container'); +const Secret = require("../../lib/secret"); const api_url = TestHelper.jsonServerUrl(); global.codeceptjs = require('../../lib'); @@ -71,15 +72,15 @@ describe('REST', () => { }); it('should send POST requests with secret', async () => { - const secretData = secret({ name: 'john', password: '123456' }, 'password'); + const secretData = Secret.secret({ name: 'john', password: '123456' }, 'password'); const response = await I.sendPostRequest('/user', secretData); response.data.name.should.eql('john'); - response.data.password.should.eql('123456'); - secretData.toString().should.include('"password":"****"'); + expect(response.data.password).toEqual({ _secret: '123456' }); + expect(secretData.password.getMasked()).toEqual('*****'); }); it('should send POST requests with secret form encoded is not converted to string', async () => { - const secretData = secret('name=john&password=123456'); + const secretData = Secret.secret('name=john&password=123456'); const response = await I.sendPostRequest('/user', secretData); response.data.name.should.eql('john'); response.data.password.should.eql('123456'); diff --git a/test/unit/secret_test.js b/test/unit/secret_test.js new file mode 100644 index 000000000..c575fa57e --- /dev/null +++ b/test/unit/secret_test.js @@ -0,0 +1,19 @@ +const expect = require('expect'); +const Secret = require('../../lib/secret'); + +describe('Secret tests', () => { + it('should be the Secret instance', () => { + const string = Secret.secret('hello'); + expect(string).toBeInstanceOf(Secret); + }); + + it('should be the Secret instance when using as object', () => { + const obj = Secret.secret({ password: 'world' }, 'password'); + expect(obj.password).toBeInstanceOf(Secret); + }); + + it('should mask the field when provided', () => { + const obj = Secret.secret({ password: 'world' }, 'password'); + expect(obj.password.getMasked()).toBe('*****'); + }); +});