diff --git a/CHANGELOG.md b/CHANGELOG.md index cea5cd0ad..1ea7f2a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 7.4.0 + +### Fixes + +- [#1421](https://github.com/okta/okta-auth-js/pull/1421) Throw error if there is incorrect `relatesTo` in IDX response + +### Other + +- [#1409](https://github.com/okta/okta-auth-js/pull/1409) Adds password page to React myaccount sample app + ## 7.3.0 ### Features @@ -35,7 +45,7 @@ - [#1343](https://github.com/okta/okta-auth-js/pull/1343) Supports Step Up MFA against `/authorize` and `/interact` endpoints -# Other +### Other - [#1342](https://github.com/okta/okta-auth-js/pull/1342) - fixes possible RCE in jsonpath-plus diff --git a/lib/idx/authenticator/util.ts b/lib/idx/authenticator/util.ts index 61181bc04..58248cfa6 100644 --- a/lib/idx/authenticator/util.ts +++ b/lib/idx/authenticator/util.ts @@ -35,7 +35,7 @@ export function findMatchedOption(authenticators, options) { let option; for (let authenticator of authenticators) { option = options - .find(({ relatesTo }) => relatesTo.key === authenticator.key); + .find(({ relatesTo }) => relatesTo.key && relatesTo.key === authenticator.key); if (option) { break; } diff --git a/lib/idx/idxState/v1/idxResponseParser.ts b/lib/idx/idxState/v1/idxResponseParser.ts index 015ddb8df..c5db593ab 100644 --- a/lib/idx/idxState/v1/idxResponseParser.ts +++ b/lib/idx/idxState/v1/idxResponseParser.ts @@ -17,6 +17,7 @@ import { OktaAuthIdxInterface } from '../../types'; // auth-js/types import { generateRemediationFunctions } from './remediationParser'; import generateIdxAction from './generateIdxAction'; import { jsonpath } from '../../../util/jsonpath'; +import { AuthSdkError } from '../../../errors'; const SKIP_FIELDS = Object.fromEntries([ 'remediation', // remediations are put into proceed/neededToProceed @@ -79,6 +80,8 @@ const expandRelatesTo = (idxResponse, value) => { if (result) { value[k] = result; return; + } else { + throw new AuthSdkError(`Cannot resolve relatesTo: ${query}`); } } } diff --git a/lib/idx/remediators/Base/SelectAuthenticator.ts b/lib/idx/remediators/Base/SelectAuthenticator.ts index 88a582a45..10e710917 100644 --- a/lib/idx/remediators/Base/SelectAuthenticator.ts +++ b/lib/idx/remediators/Base/SelectAuthenticator.ts @@ -33,7 +33,7 @@ export class SelectAuthenticator relatesTo.key === authenticator.key); + .find(({ relatesTo }) => relatesTo.key && relatesTo.key === authenticator.key); if (option) { break; } diff --git a/test/spec/idx/authenticator/util.ts b/test/spec/idx/authenticator/util.ts index 7d53a5457..071a1b9a7 100644 --- a/test/spec/idx/authenticator/util.ts +++ b/test/spec/idx/authenticator/util.ts @@ -70,5 +70,17 @@ describe('authenticator/util', () => { const res = findMatchedOption([authenticator], [option]); expect(res).toBe(undefined); }); + it('will not match if key is missing', () => { + const authenticator = { + id: 'a' + }; + const option = { + relatesTo: { + id: 'a' + } + }; + const res = findMatchedOption([authenticator], [option]); + expect(res).toBe(undefined); + }); }); }); \ No newline at end of file diff --git a/test/spec/idx/idxState/unit/v1/idxResponseParser.test.js b/test/spec/idx/idxState/unit/v1/idxResponseParser.test.js index 1bb61c2c3..ebca3de59 100644 --- a/test/spec/idx/idxState/unit/v1/idxResponseParser.test.js +++ b/test/spec/idx/idxState/unit/v1/idxResponseParser.test.js @@ -21,6 +21,11 @@ const mockComplexContextIdxResponse = require('../../mocks/poll-for-password'); const mockTerminalIdxResponse = require('../../mocks/terminal-return-email'); const mockMessageIdxResponse = require('../../mocks/unknown-user'); const mockSuccessIdxResponse = require('../../mocks/success'); +const mockIdxResponseWithBadRelationship = () => { + const mock = require('../../mocks/authenticator-verification-password'); + mock.remediation.value[1].value[0].options[0].relatesTo = '$.authenticatorEnrollments.value[999]'; + return mock; +}; jest.mock('../../../../../../lib/idx/idxState/v1/generateIdxAction'); jest.mock('../../../../../../lib/idx/idxState/v1/remediationParser'); @@ -164,5 +169,9 @@ describe('idxResponseParser', () => { expect( remediations[0]).toMatchSnapshot(); }); + it('throws error if relatesTo can\'t be resolved', () => { + const fn = () => parseIdxResponse( {}, mockIdxResponseWithBadRelationship() ); + expect(fn).toThrowError('Cannot resolve relatesTo: $.authenticatorEnrollments.value[999]'); + }); }); }); diff --git a/test/spec/idx/remediators/SelectAuthenticator.ts b/test/spec/idx/remediators/SelectAuthenticator.ts new file mode 100644 index 000000000..c183fb18b --- /dev/null +++ b/test/spec/idx/remediators/SelectAuthenticator.ts @@ -0,0 +1,32 @@ +import { SelectAuthenticatorAuthenticate } from '../../../../lib/idx/remediators'; +import { + SelectAuthenticatorAuthenticateRemediationFactory, + AuthenticatorValueFactory, + PhoneAuthenticatorOptionFactory, +} from '@okta/test.support/idx'; + +describe('remediators/Base/SelectAuthenticator', () => { + describe('canRemediate', () => { + it('retuns false if can\'t find matched authenticator by key', () => { + const remediation = SelectAuthenticatorAuthenticateRemediationFactory.build({ + value: [ + AuthenticatorValueFactory.build({ + options: [ + PhoneAuthenticatorOptionFactory.params({ + // prevent resolving of authenticator by `relatesTo` in purpose + // eslint-disable-next-line @typescript-eslint/no-explicit-any + _authenticator: 'cant_be_resolved' as any + }).build(), + ] + }) + ] + }); + + const authenticators = [ + { id: 'foo' } + ]; + const r = new SelectAuthenticatorAuthenticate(remediation, { authenticators }); + expect(r.canRemediate()).toBe(false); + }); + }); +});