Skip to content

Commit

Permalink
Merge pull request from GHSA-6gg3-pmm7-97xc
Browse files Browse the repository at this point in the history
* Replace usage of i18n.html with i18n.str on vcode ask screen

* Add tests for passwordless email xss issue

* Change enterprise HRD screen to use i18n.str
  • Loading branch information
Steve Hobbs authored Aug 14, 2020
1 parent 195b5d2 commit 3711fb5
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`HRDScreen Component renders correctly when there is an enterprise domain 1`] = `
<div
data-__type="hrd_pane"
data-header={
<p>
Please enter your corporate credentials at [ 'domain.com' ].
</p>
}
data-i18n={
Object {
"str": [Function],
}
}
data-model={
Immutable.Map {
"id": "__lock-id__",
"i18n": Immutable.Map {
"strings": Immutable.Map {
"enterpriseLoginIntructions": "Login with your corporate credentials.",
"enterpriseActiveLoginInstructions": "Please enter your corporate credentials at %s.",
},
},
}
}
data-passwordInputPlaceholder=" []"
data-usernameInputPlaceholder=" []"
/>
`;

exports[`HRDScreen Component renders correctly when there is no enterprise domain 1`] = `
<div
data-__type="hrd_pane"
data-header={
<p>
Login with your corporate credentials. []
</p>
}
data-i18n={
Object {
"str": [Function],
}
}
data-model={
Immutable.Map {
"id": "__lock-id__",
"i18n": Immutable.Map {
"strings": Immutable.Map {
"enterpriseLoginIntructions": "Login with your corporate credentials.",
"enterpriseActiveLoginInstructions": "Please enter your corporate credentials at %s.",
},
},
}
}
data-passwordInputPlaceholder=" []"
data-usernameInputPlaceholder=" []"
/>
`;
52 changes: 52 additions & 0 deletions src/__tests__/connection/enterprise/hrd_screen.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { mockComponent, expectComponent } from 'testUtils';
import I from 'immutable';
import { dataFns } from '../../../utils/data_utils';
import * as i18n from '../../../i18n';

const { set } = dataFns(['i18n']);

jest.mock('engine/classic');
jest.mock('connection/enterprise/hrd_pane', () => mockComponent('hrd_pane'));
jest.mock('connection/enterprise', () => ({
enterpriseDomain: jest.fn(() => 'domain.com')
}));

const getComponent = () => {
const HRDScreen = require('connection/enterprise/hrd_screen').default;
const screen = new HRDScreen();
return screen.render();
};

describe('HRDScreen Component', () => {
let i18nProp;
let lock;

beforeEach(() => {
lock = I.fromJS({ id: '__lock-id__' });

jest.resetModules();

const lang = I.fromJS({
enterpriseLoginIntructions: 'Login with your corporate credentials.',
enterpriseActiveLoginInstructions: 'Please enter your corporate credentials at %s.'
});

lock = set(lock, 'strings', lang);

i18nProp = {
str: (keypath, ...args) => i18n.str(lock, keypath, args)
};
});

it('renders correctly when there is an enterprise domain', () => {
const Component = getComponent();
expectComponent(<Component model={lock} i18n={i18nProp} />).toMatchSnapshot();
});

it('renders correctly when there is no enterprise domain', () => {
require('connection/enterprise').enterpriseDomain.mockImplementation(() => null);
const Component = getComponent();
expectComponent(<Component model={lock} i18n={i18nProp} />).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AskVCode renders correctly when logging in with a phone number 1`] = `
<div
data-__type="vcode_pane"
data-instructions="An SMS with the code has been sent to [ '+44 456 789' ]."
data-lock={
Immutable.Map {
"id": "__lock-id__",
"i18n": Immutable.Map {
"strings": Immutable.Map {
"passwordlessEmailCodeInstructions": "An email with the code has been sent to %s.",
"passwordlessSMSCodeInstructions": "An SMS with the code has been sent to %s.",
},
},
"field": Immutable.Map {
"phoneNumber": Immutable.Map {
"valid": true,
"value": "456 789",
"showInvalid": false,
},
"location": Immutable.Map {
"country": "United Kingdom",
"diallingCode": "+44",
"isoCode": "UK",
"label": "+44 UK United Kingdom",
"value": "+44 UK",
"options": Immutable.List [
Immutable.Map {
"country": "United Kingdom",
"diallingCode": "+44",
"isoCode": "UK",
"label": "+44 UK United Kingdom",
"value": "+44 UK",
},
],
"showInvalid": false,
"valid": true,
},
},
}
}
data-onRestart={[Function]}
data-placeholder=" []"
data-resendLabel=" []"
/>
`;

exports[`AskVCode renders correctly when logging in with email 1`] = `
<div
data-__type="vcode_pane"
data-instructions="An email with the code has been sent to [ 'test@user.com' ]."
data-lock={
Immutable.Map {
"id": "__lock-id__",
"i18n": Immutable.Map {
"strings": Immutable.Map {
"passwordlessEmailCodeInstructions": "An email with the code has been sent to %s.",
"passwordlessSMSCodeInstructions": "An SMS with the code has been sent to %s.",
},
},
"field": Immutable.Map {
"email": Immutable.Map {
"valid": true,
"value": "test@user.com",
"showInvalid": false,
},
},
}
}
data-onRestart={[Function]}
data-placeholder=" []"
data-resendLabel=" []"
/>
`;
68 changes: 68 additions & 0 deletions src/__tests__/connection/passwordless/ask_vcode.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import { mockComponent } from 'testUtils';
import I from 'immutable';
import { setField } from '../../../field';
import * as i18n from '../../../i18n';
import { expectComponent } from '../../testUtils';
import { dataFns } from '../../../utils/data_utils';
import { setPhoneNumber, initLocation } from '../../../field/phone_number';

const { set } = dataFns(['i18n']);

jest.mock('engine/classic');
jest.mock('field/vcode/vcode_pane', () => mockComponent('vcode_pane'));
jest.mock('field/phone-number/locations', () => ({
__esModule: true,
default: [['United Kingdom', 'UK', '+44']]
}));

jest.mock('connection/passwordless/index', () => ({
isEmail: jest.fn()
}));

const getComponent = () => {
const VCodeScreen = require('connection/passwordless/ask_vcode').default;
const screen = new VCodeScreen();
return screen.render();
};

describe('AskVCode', () => {
let lock;
let i18nProp;

beforeEach(() => {
lock = I.fromJS({ id: '__lock-id__' });

jest.resetModules();

const lang = I.fromJS({
passwordlessEmailCodeInstructions: 'An email with the code has been sent to %s.',
passwordlessSMSCodeInstructions: 'An SMS with the code has been sent to %s.'
});

lock = set(lock, 'strings', lang);

i18nProp = {
str: (keypath, ...args) => i18n.str(lock, keypath, args)
};
});

it('renders correctly when logging in with email', () => {
require('connection/passwordless/index').isEmail.mockImplementation(() => true);

const Component = getComponent();
const l = setField(lock, 'email', 'test@user.com');

expectComponent(<Component model={l} i18n={i18nProp} />).toMatchSnapshot();
});

it('renders correctly when logging in with a phone number', () => {
require('connection/passwordless/index').isEmail.mockImplementation(() => false);

const Component = getComponent();
let l = setPhoneNumber(lock, '456 789');
l = initLocation(l, 'UK');

expectComponent(<Component model={l} i18n={i18nProp} />).toMatchSnapshot();
});
});
2 changes: 1 addition & 1 deletion src/connection/enterprise/hrd_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class HRDPane extends React.Component {
}

HRDPane.propTypes = {
header: PropTypes.element,
header: PropTypes.string,
i18n: PropTypes.object.isRequired,
model: PropTypes.object.isRequired,
passwordInputPlaceholder: PropTypes.string.isRequired,
Expand Down
6 changes: 3 additions & 3 deletions src/connection/enterprise/hrd_screen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const Component = ({ i18n, model }) => {

var headerText;

if (domain != null) {
headerText = i18n.html('enterpriseActiveLoginInstructions', domain);
if (domain !== null) {
headerText = i18n.str('enterpriseActiveLoginInstructions', domain);
} else {
headerText = i18n.html('enterpriseLoginIntructions');
headerText = i18n.str('enterpriseLoginIntructions');
}

headerText = headerText || null;
Expand Down
4 changes: 2 additions & 2 deletions src/connection/passwordless/ask_vcode.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { humanPhoneNumberWithDiallingCode } from '../../field/phone_number';

const Component = ({ i18n, model }) => {
const instructions = isEmail(model)
? i18n.html('passwordlessEmailCodeInstructions', getFieldValue(model, 'email'))
: i18n.html('passwordlessSMSCodeInstructions', humanPhoneNumberWithDiallingCode(model));
? i18n.str('passwordlessEmailCodeInstructions', getFieldValue(model, 'email'))
: i18n.str('passwordlessSMSCodeInstructions', humanPhoneNumberWithDiallingCode(model));

return (
<VcodePane
Expand Down
2 changes: 1 addition & 1 deletion src/field/vcode/vcode_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class VcodePane extends React.Component {
}

VcodePane.propTypes = {
instructions: PropTypes.element,
instructions: PropTypes.string,
lock: PropTypes.object.isRequired,
placeholder: PropTypes.string.isRequired,
resendLabel: PropTypes.string.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default {
passwordlessEmailInstructions: 'Enter your email to sign in<br/>or create an account',
passwordlessSMSAlternativeInstructions:
'Otherwise, enter your phone to sign in<br/>or create an account',
passwordlessSMSCodeInstructions: 'An SMS with the code has been sent<br/>to %s.',
passwordlessSMSCodeInstructions: 'An SMS with the code has been sent to %s.',
passwordlessSMSInstructions: 'Enter your phone to sign in<br/>or create an account',
phoneNumberInputPlaceholder: 'your phone number',
resendCodeAction: 'Did not get the code?',
Expand Down

0 comments on commit 3711fb5

Please sign in to comment.