fix: Sanitise IdP provided information in SAML test pages

This commit is contained in:
Valya Bullions 2024-10-08 17:54:57 +01:00
parent 94cfb3727c
commit b4cf627cd3
No known key found for this signature in database
4 changed files with 104 additions and 26 deletions

View file

@ -0,0 +1,33 @@
import { getSamlConnectionTestFailedView } from '../saml-connection-test-failed';
const basicXssPayload = '<script>alert("1");</script>';
const basicXssMitigated = '&lt;script&gt;alert(&quot;1&quot;);&lt;/script&gt;';
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('<strong>Email:</strong> (n/a)');
});
});

View file

@ -0,0 +1,27 @@
import { getSamlConnectionTestSuccessView } from '../saml-connection-test-success';
const basicXssPayload = '<script>alert("1");</script>';
const basicXssMitigated = '&lt;script&gt;alert(&quot;1&quot;);&lt;/script&gt;';
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('<strong>Email:</strong> (n/a)');
});
});

View file

@ -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 }>(`
<http>
<head>
<title>n8n - SAML Connection Test Result</title>
@ -20,23 +18,34 @@ export function getSamlConnectionTestFailedView(
<body>
<div style="text-align:center">
<h1>SAML Connection Test failed</h1>
<h2>${message ?? 'A common issue could be that no email attribute is set'}</h2>
<h2>{{message}}</h2>
<button onclick="window.close()">You can close this window now</button>
<p></p>
${
attributes
? `
<h2>Here are the attributes returned by your SAML IdP:</h2>
<ul>
<li><strong>Email:</strong> ${attributes?.email ?? '(n/a)'}</li>
<li><strong>First Name:</strong> ${attributes?.firstName ?? '(n/a)'}</li>
<li><strong>Last Name:</strong> ${attributes?.lastName ?? '(n/a)'}</li>
<li><strong>UPN:</strong> ${attributes?.userPrincipalName ?? '(n/a)'}</li>
</ul>`
: ''
}
{{#with attributes}}
<h2>Here are the attributes returned by your SAML IdP:</h2>
<ul>
<li><strong>Email:</strong> {{email}}</li>
<li><strong>First Name:</strong> {{firstName}}</li>
<li><strong>Last Name:</strong> {{lastName}}</li>
<li><strong>UPN:</strong> {{userPrincipalName}}</li>
{{/with}}
</ul>
</div>
</body>
</http>
`;
`);
export function getSamlConnectionTestFailedView(
message?: string,
attributes?: Partial<SamlUserAttributes>,
): 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)',
},
});
}

View file

@ -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<SamlUserAttributes>(`
<http>
<head>
<title>n8n - SAML Connection Test Result</title>
@ -21,13 +22,21 @@ export function getSamlConnectionTestSuccessView(attributes: SamlUserAttributes)
<p></p>
<h2>Here are the attributes returned by your SAML IdP:</h2>
<ul>
<li><strong>Email:</strong> ${attributes.email ?? '(n/a)'}</li>
<li><strong>First Name:</strong> ${attributes.firstName ?? '(n/a)'}</li>
<li><strong>Last Name:</strong> ${attributes.lastName ?? '(n/a)'}</li>
<li><strong>UPN:</strong> ${attributes.userPrincipalName ?? '(n/a)'}</li>
<li><strong>Email:</strong> {{email}}</li>
<li><strong>First Name:</strong> {{firstName}}</li>
<li><strong>Last Name:</strong> {{lastName}}</li>
<li><strong>UPN:</strong> {{userPrincipalName}}</li>
</ul>
</div>
</body>
</http>
`;
`);
export function getSamlConnectionTestSuccessView(attributes: Partial<SamlUserAttributes>): string {
return successTemplate({
email: attributes.email ?? '(n/a)',
firstName: attributes.firstName ?? '(n/a)',
lastName: attributes.lastName ?? '(n/a)',
userPrincipalName: attributes.userPrincipalName ?? '(n/a)',
});
}