Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,92 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`HRDScreen Component renders correctly when enterprise domain is a whitespace string 1`] = `
<div
data-__type="hrd_pane"
data-header={
<p>
Login with your corporate credentials. []
</p>
}
data-i18n={
{
"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 enterprise domain is an empty string 1`] = `
<div
data-__type="hrd_pane"
data-header={
<p>
Login with your corporate credentials. []
</p>
}
data-i18n={
{
"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 enterprise domain is undefined 1`] = `
<div
data-__type="hrd_pane"
data-header={
<p>
Login with your corporate credentials. []
</p>
}
data-i18n={
{
"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 an enterprise domain 1`] = `
<div
data-__type="hrd_pane"
Expand Down
36 changes: 35 additions & 1 deletion src/__tests__/connection/enterprise/hrd_screen.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { mockComponent, expectComponent } from 'testUtils';
import { mockComponent, expectComponent, renderShallowComponent } from 'testUtils';
import I from 'immutable';
import { dataFns } from '../../../utils/data_utils';
import * as i18n from '../../../i18n';
Expand Down Expand Up @@ -49,4 +49,38 @@ describe('HRDScreen Component', () => {
const Component = getComponent();
expectComponent(<Component model={lock} i18n={i18nProp} />).toMatchSnapshot();
});

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

it('renders correctly when enterprise domain is a whitespace string', () => {
require('connection/enterprise').enterpriseDomain.mockImplementation(() => ' ');
const Component = getComponent();
expectComponent(<Component model={lock} i18n={i18nProp} />).toMatchSnapshot();
});

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

it('does not show "undefined" in message when enterprise domain is undefined', () => {
require('connection/enterprise').enterpriseDomain.mockImplementation(() => undefined);
const Component = getComponent();
const rendered = renderShallowComponent(<Component model={lock} i18n={i18nProp} />);
const headerText = rendered.props.header && rendered.props.header.props.children;
expect(String(headerText)).not.toContain('undefined');
});

it('does not show "undefined" in message when enterprise domain is null', () => {
require('connection/enterprise').enterpriseDomain.mockImplementation(() => null);
const Component = getComponent();
const rendered = renderShallowComponent(<Component model={lock} i18n={i18nProp} />);
const headerText = rendered.props.header && rendered.props.header.props.children;
expect(String(headerText)).not.toContain('undefined');
});
});
39 changes: 39 additions & 0 deletions src/__tests__/connection/enterprise/matchConnection.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import I from 'immutable';
import { matchConnection } from '../../../connection/enterprise';

// Build a minimal lock model with enterprise connections at the path
// l.connections() calls tget(m, ['connections', type]) where tget uses
// dataFns(['core']).tget, which reads m.getIn(['core', 'transient', 'connections', type])
const buildModel = (connections) =>
I.fromJS({ core: { transient: { connections: { enterprise: connections } } } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity vulnerability may affect your project—review required:
Line 8 lists a dependency (immutable) with a known High severity vulnerability.

ℹ️ Why this matters

Affected versions of immutable are vulnerable to Inefficient Algorithmic Complexity / Uncontrolled Resource Consumption. Immutable.js uses a deterministic, publicly known 32-bit string hash whose collision buckets are scanned linearly. Building an Immutable.Map, Set, OrderedMap, or OrderedSet (including via fromJS) from keys an attacker controls lets them craft many colliding keys, degrading operations from O(1) to O(n) (O(n^2) for bulk inserts) and exhausting CPU / stalling the event loop (algorithmic-complexity denial of service).

References: GHSA, CVE

To resolve this comment:
Check if you build these collections from keys derived from untrusted input.

  • If you're affected, upgrade this dependency to at least version 4.3.9 at package-lock.json.
  • If you're not affected, comment /fp we don't use this [condition]
💬 Ignore this finding

To ignore this, reply with:

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

You can view more details on this finding in the Semgrep AppSec Platform here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/fp Lock v13 maintains IE11 compatibility and cannot upgrade immutable to 4.x as it dropped IE11 support. Collections in this codebase are not built from untrusted user input.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status updated to ignored - false positive by @ankita10119.

Reply with /open to re-open this finding


describe('matchConnection', () => {
it('returns the matching connection when domains contains the email domain', () => {
const model = buildModel([
{ name: 'my-connection', strategy: 'waad', domains: ['example.com'] }
]);
const result = matchConnection(model, 'user@example.com');
expect(result).toBeDefined();
expect(result.get('name')).toBe('my-connection');
});

it('returns undefined when no connection matches the email domain', () => {
const model = buildModel([
{ name: 'my-connection', strategy: 'waad', domains: ['other.com'] }
]);
expect(matchConnection(model, 'user@example.com')).toBeUndefined();
});

it('does not throw when a connection has null domains', () => {
const model = buildModel([{ name: 'no-domains', strategy: 'waad', domains: null }]);
expect(() => matchConnection(model, 'user@example.com')).not.toThrow();
expect(matchConnection(model, 'user@example.com')).toBeUndefined();
});

it('returns false when email has no domain part', () => {
const model = buildModel([
{ name: 'my-connection', strategy: 'waad', domains: ['example.com'] }
]);
expect(matchConnection(model, 'notanemail')).toBe(false);
});
});
4 changes: 2 additions & 2 deletions src/connection/enterprise.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function matchConnection(m, email, strategies = []) {
const target = emailDomain(email);
if (!target) return false;
return l.connections(m, 'enterprise', ...strategies).find(x => {
return x.get('domains').contains(target);
return x.get('domains') && x.get('domains').contains(target);
});
}

Expand Down Expand Up @@ -108,7 +108,7 @@ export function isADEnabled(m) {

export function findADConnectionWithoutDomain(m, name = undefined) {
return l.connections(m, 'enterprise', 'ad', 'auth0-adldap').find(x => {
return x.get('domains').isEmpty() && (!name || x.get('name') === name);
return x.get('domains') && x.get('domains').isEmpty() && (!name || x.get('name') === name);
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/connection/enterprise/hrd_screen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Component = ({ i18n, model }) => {

var headerText;

if (domain !== null) {
if (domain && domain.trim()) {
headerText = i18n.str('enterpriseActiveLoginInstructions', domain);
} else {
headerText = i18n.str('enterpriseLoginIntructions');
Expand Down
Loading