diff --git a/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-failed.test.ts b/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-failed.test.ts new file mode 100644 index 0000000000..3cc0cf16bb --- /dev/null +++ b/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-failed.test.ts @@ -0,0 +1,33 @@ +import { getSamlConnectionTestFailedView } from '../saml-connection-test-failed'; + +const basicXssPayload = ''; +const basicXssMitigated = '<script>alert("1");</script>'; + +describe('SAML Connection Test Failed', () => { + test('should not allow XSS via error message', () => { + const result = getSamlConnectionTestFailedView('Test ' + basicXssPayload); + expect(result).not.toMatch(basicXssPayload); + expect(result).toMatch(basicXssMitigated); + }); + + test('should not allow XSS via attributes', () => { + const result = getSamlConnectionTestFailedView('', { + email: 'test@example.com', + firstName: 'Test', + lastName: 'McXss' + basicXssPayload, + userPrincipalName: 'test@example.com', + }); + expect(result).not.toMatch(basicXssPayload); + expect(result).toMatch(basicXssMitigated); + }); + + test('should replace undefined with (n/a)', () => { + expect( + getSamlConnectionTestFailedView('', { + firstName: 'No', + lastName: 'Email', + userPrincipalName: 'test@example.com', + }), + ).toMatch('Email: (n/a)'); + }); +}); diff --git a/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-success.test.ts b/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-success.test.ts new file mode 100644 index 0000000000..2d448abab4 --- /dev/null +++ b/packages/cli/src/sso/saml/views/__tests__/saml-connection-test-success.test.ts @@ -0,0 +1,27 @@ +import { getSamlConnectionTestSuccessView } from '../saml-connection-test-success'; + +const basicXssPayload = ''; +const basicXssMitigated = '<script>alert("1");</script>'; + +describe('SAML Connection Test Succeeded', () => { + test('should not allow XSS via attributes', () => { + const result = getSamlConnectionTestSuccessView({ + email: 'test@example.com', + firstName: 'Test', + lastName: 'McXss' + basicXssPayload, + userPrincipalName: 'test@example.com', + }); + expect(result).not.toMatch(basicXssPayload); + expect(result).toMatch(basicXssMitigated); + }); + + test('should replace undefined with (n/a)', () => { + expect( + getSamlConnectionTestSuccessView({ + firstName: 'No', + lastName: 'Email', + userPrincipalName: 'test@example.com', + }), + ).toMatch('Email: (n/a)'); + }); +}); diff --git a/packages/cli/src/sso/saml/views/saml-connection-test-failed.ts b/packages/cli/src/sso/saml/views/saml-connection-test-failed.ts index 4ce2a3e3ac..9890ffc94b 100644 --- a/packages/cli/src/sso/saml/views/saml-connection-test-failed.ts +++ b/packages/cli/src/sso/saml/views/saml-connection-test-failed.ts @@ -1,10 +1,8 @@ +import { compile } from 'handlebars'; + import type { SamlUserAttributes } from '../types/saml-user-attributes'; -export function getSamlConnectionTestFailedView( - message: string, - attributes?: SamlUserAttributes, -): string { - return ` +const failedTemplate = compile<{ message?: string; attributes?: SamlUserAttributes }>(` n8n - SAML Connection Test Result @@ -20,23 +18,34 @@ export function getSamlConnectionTestFailedView(

SAML Connection Test failed

-

${message ?? 'A common issue could be that no email attribute is set'}

+

{{message}}

- ${ - attributes - ? ` -

Here are the attributes returned by your SAML IdP:

- ` - : '' - } + {{#with attributes}} +

Here are the attributes returned by your SAML IdP:

+
- `; +`); + +export function getSamlConnectionTestFailedView( + message?: string, + attributes?: Partial, +): string { + return failedTemplate({ + message: message ?? 'A common issue could be that no email attribute is set', + attributes: attributes && { + email: attributes.email ?? '(n/a)', + firstName: attributes.firstName ?? '(n/a)', + lastName: attributes.lastName ?? '(n/a)', + userPrincipalName: attributes.userPrincipalName ?? '(n/a)', + }, + }); } diff --git a/packages/cli/src/sso/saml/views/saml-connection-test-success.ts b/packages/cli/src/sso/saml/views/saml-connection-test-success.ts index f647527cd0..29d9f0f009 100644 --- a/packages/cli/src/sso/saml/views/saml-connection-test-success.ts +++ b/packages/cli/src/sso/saml/views/saml-connection-test-success.ts @@ -1,7 +1,8 @@ +import { compile } from 'handlebars'; + import type { SamlUserAttributes } from '../types/saml-user-attributes'; -export function getSamlConnectionTestSuccessView(attributes: SamlUserAttributes): string { - return ` +const successTemplate = compile(` n8n - SAML Connection Test Result @@ -21,13 +22,21 @@ export function getSamlConnectionTestSuccessView(attributes: SamlUserAttributes)

Here are the attributes returned by your SAML IdP:

    -
  • Email: ${attributes.email ?? '(n/a)'}
  • -
  • First Name: ${attributes.firstName ?? '(n/a)'}
  • -
  • Last Name: ${attributes.lastName ?? '(n/a)'}
  • -
  • UPN: ${attributes.userPrincipalName ?? '(n/a)'}
  • +
  • Email: {{email}}
  • +
  • First Name: {{firstName}}
  • +
  • Last Name: {{lastName}}
  • +
  • UPN: {{userPrincipalName}}
- `; + `); + +export function getSamlConnectionTestSuccessView(attributes: Partial): string { + return successTemplate({ + email: attributes.email ?? '(n/a)', + firstName: attributes.firstName ?? '(n/a)', + lastName: attributes.lastName ?? '(n/a)', + userPrincipalName: attributes.userPrincipalName ?? '(n/a)', + }); }