Skip to content

Commit

Permalink
Hide password values from raw HTTP logs. (#4066)
Browse files Browse the repository at this point in the history
Co-authored-by: Priyansh Garg <priyanshgarg30@gmail.com>
  • Loading branch information
dikwickley and garg3133 committed Mar 7, 2024
1 parent 3502e34 commit a441ca4
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 10 deletions.
10 changes: 6 additions & 4 deletions lib/http/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const http = require('http');
const https = require('https');
const dns = require('dns');
const path = require('path');
const {Key} = require('selenium-webdriver');

const HttpUtil = require('./http.js');
const HttpOptions = require('./options.js');
Expand Down Expand Up @@ -126,7 +127,6 @@ class HttpRequest extends EventEmitter {
this.reqOptions = this.createHttpOptions(options);
this.hostname = Formatter.formatHostname(this.reqOptions.host, this.reqOptions.port, this.use_ssl);
this.retryAttempts = this.httpOpts.retry_attempts;
this.redact = options.redact || false;

return this;
}
Expand Down Expand Up @@ -253,13 +253,15 @@ class HttpRequest extends EventEmitter {

return arg;
});
} else if (this.reqOptions.method === 'POST' && this.reqOptions.path.endsWith('/value') && params.text.startsWith(Key.NULL)) {
params.text = '*******';
params.value = '*******'.split('');
}

const content = ` Request ${[this.reqOptions.method, this.hostname + this.reqOptions.path, retryStr + ' '].join(' ')}`;
const paramsStr = this.redact ? '<REDACTED>' : params;

Logger.request(content, paramsStr);
Logger.info(content, paramsStr);
Logger.request(content, params);
Logger.info(content, params);

this.httpRequest.on('error', err => this.onRequestError(err));
}
Expand Down
9 changes: 5 additions & 4 deletions lib/transport/selenium-webdriver/method-mappings.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const {WebElement, WebDriver, Origin, By, until, Condition} = require('selenium-webdriver');
const {WebElement, WebDriver, Origin, By, until, Condition, Key} = require('selenium-webdriver');
const {Locator} = require('../../element');
const NightwatchLocator = require('../../element/locator-factory.js');
const {isString} = require('../../utils');
Expand Down Expand Up @@ -590,9 +590,10 @@ module.exports = class MethodMappings {
return null;
},

setElementValueRedacted(...args) {
// FIXME: redact password in verbose HTTP logs, it's only redacted in the command logs
return this.methods.session.setElementValue.call(this, ...args);
setElementValueRedacted(webElementOrId, value) {
const modifiedValue = [Key.NULL].concat(value);

return this.methods.session.setElementValue.call(this, webElementOrId, modifiedValue);
},

async setElementValue(webElementOrId, value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
describe('value redaction in setPassword', function() {
test('test setPassword', async browser => {
browser
.setPassword('#weblogin', 'password')
.setValue('#weblogin', 'simpletext');
});
});
5 changes: 3 additions & 2 deletions test/src/api/commands/element/testSetPassword.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const assert = require('assert');
const {Key} = require('selenium-webdriver');
const MockServer = require('../../../../lib/mockserver.js');
const CommandGlobals = require('../../../../lib/globals/commands.js');

Expand All @@ -17,8 +18,8 @@ describe('setPassword', function() {
url: '/wd/hub/session/1352110219202/element/0/value',
method: 'POST',
postdata: {
text: 'password',
value: ['p', 'a', 's', 's', 'w', 'o', 'r', 'd']
text: Key.NULL + 'password',
value: [Key.NULL, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd']
},
response: {
sessionId: '1352110219202',
Expand Down
104 changes: 104 additions & 0 deletions test/src/api/commands/element/testSetPasswordReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const assert = require('assert');
const path = require('path');
const {Key} = require('selenium-webdriver');
const common = require('../../../../common.js');
const Mocks = require('../../../../lib/command-mocks.js');
const {settings} = common;
const NightwatchClient = common.require('index.js');
const MockServer = require('../../../../lib/mockserver.js');

describe('setPassword report check', function() {
before(function(done) {
this.server = MockServer.init();
this.server.on('listening', () => done());
});

after(function(done) {
this.server.close(function() {
done();
});
});

it('client.setPassword() value redacted in rawHttpOutput', async function() {
let sendKeysPasswordMockCalled = false;
let sendKeysNormalMockCalled = false;
let globalReporterCalled = false;

Mocks.createNewW3CSession({
testName: 'Actions API demo tests'
});

MockServer.addMock({
url: '/session/13521-10219-202/element/5cc459b8-36a8-3042-8b4a-258883ea642b/clear',
method: 'POST',
statusCode: 200,
response: {
value: null
},
times: 2
});

MockServer.addMock({
url: '/session/13521-10219-202/element/5cc459b8-36a8-3042-8b4a-258883ea642b/value',
method: 'POST',
postdata: {text: Key.NULL + 'password', value: [Key.NULL, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd']},
response: {
value: null
},
onRequest: () => {
sendKeysPasswordMockCalled = true;
}
}, true);

MockServer.addMock({
url: '/session/13521-10219-202/element/5cc459b8-36a8-3042-8b4a-258883ea642b/value',
method: 'POST',
postdata: {text: 'simpletext', value: ['s', 'i', 'm', 'p', 'l', 'e', 't', 'e', 'x', 't']},
response: {
value: null
},
onRequest: () => {
sendKeysNormalMockCalled = true;
}
}, true);

const testsPath = [
path.join(__dirname, '../../../../sampletests/passwordValueRedacted/passwordValueRedacted.js')
];

const globals = {
reporter(results) {
globalReporterCalled = true;

assert.strictEqual(sendKeysPasswordMockCalled, true);
assert.strictEqual(sendKeysNormalMockCalled, true);
assert.strictEqual(results.errmessages.length, 0);

const rawHttpOutput = results.modules.passwordValueRedacted.rawHttpOutput;
const requests = rawHttpOutput
.filter((req) => {
return req[1].includes('element/5cc459b8-36a8-3042-8b4a-258883ea642b/value') &&
req[1].includes('Request POST');
});

assert.strictEqual(requests.length, 2);

// First request (setPassword) should contain redacted value
assert.strictEqual(requests[0][2].includes('password'), false);
assert.strictEqual(requests[0][2].includes('*******'), true);

// Second request (setValue) should NOT contain redacted value
assert.strictEqual(requests[1][2].includes('simpletext'), true);
assert.strictEqual(requests[1][2].includes('*******'), false);
}
};

await NightwatchClient.runTests(testsPath, settings({
globals,
output_folder: 'output',
selenium_host: null
}));

assert.strictEqual(globalReporterCalled, true);
});
});

0 comments on commit a441ca4

Please sign in to comment.