mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-31 03:47:57 +08:00
fix(Form Node): Preserve CSS child combinator selectors in custom styling (#24106)
Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com>
This commit is contained in:
co-authored by
Michael Kret
parent
c6eb487518
commit
680f264731
@@ -21,6 +21,7 @@ import {
|
||||
resolveRawData,
|
||||
isFormConnected,
|
||||
sanitizeHtml,
|
||||
sanitizeCustomCss,
|
||||
validateResponseModeConfiguration,
|
||||
prepareFormFields,
|
||||
addFormResponseDataToReturnItem,
|
||||
@@ -347,6 +348,55 @@ describe('FormTrigger, sanitizeHtml', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeCustomCss', () => {
|
||||
it('should return undefined for undefined input', () => {
|
||||
expect(sanitizeCustomCss(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined for empty string', () => {
|
||||
expect(sanitizeCustomCss('')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should preserve CSS child combinator selectors (>)', () => {
|
||||
const css = '#n8n-form > div.form-header > p { text-align: left; }';
|
||||
expect(sanitizeCustomCss(css)).toBe(css);
|
||||
});
|
||||
|
||||
it('should preserve CSS adjacent sibling selectors (+)', () => {
|
||||
const css = 'h1 + p { margin-top: 0; }';
|
||||
expect(sanitizeCustomCss(css)).toBe(css);
|
||||
});
|
||||
|
||||
it('should preserve CSS general sibling selectors (~)', () => {
|
||||
const css = 'h1 ~ p { color: gray; }';
|
||||
expect(sanitizeCustomCss(css)).toBe(css);
|
||||
});
|
||||
|
||||
it('should remove script tags from CSS', () => {
|
||||
const css = '.container { color: red; }<script>alert("xss")</script>';
|
||||
expect(sanitizeCustomCss(css)).toBe('.container { color: red; }');
|
||||
});
|
||||
|
||||
it('should remove style closing tags that could break out of style block', () => {
|
||||
const css = '.container { color: red; }</style><script>alert("xss")</script>';
|
||||
expect(sanitizeCustomCss(css)).toBe('.container { color: red; }');
|
||||
});
|
||||
|
||||
it('should preserve url() in CSS', () => {
|
||||
const css = '.bg { background-image: url(https://example.com/image.png); }';
|
||||
expect(sanitizeCustomCss(css)).toBe(css);
|
||||
});
|
||||
|
||||
it('should preserve complex CSS with multiple selectors and properties', () => {
|
||||
const css = `
|
||||
#n8n-form > div.form-header > p { text-align: left; }
|
||||
.form-container > .input-group + .input-group { margin-top: 1rem; }
|
||||
button:hover { background-color: #0056b3; }
|
||||
`;
|
||||
expect(sanitizeCustomCss(css)).toBe(css);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FormTrigger, formWebhook', () => {
|
||||
const executeFunctions = mock<IWebhookFunctions>();
|
||||
executeFunctions.getNode.mockReturnValue({ typeVersion: 2.1 } as any);
|
||||
|
||||
@@ -131,8 +131,8 @@ export function sanitizeCustomCss(css: string | undefined): string | undefined {
|
||||
return sanitize(css, {
|
||||
allowedTags: [], // No HTML tags allowed
|
||||
allowedAttributes: {}, // No attributes allowed
|
||||
// This ensures we're only keeping the text content
|
||||
// which should be the CSS, while removing any HTML/script tags
|
||||
// Decode HTML entities that sanitize-html encodes, as they break CSS selectors like ">"
|
||||
textFilter: (text) => text.replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&'),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user