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
33 changes: 33 additions & 0 deletions src/modules/creator/creator-bio-sanitize.utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { sanitizeBio } from './creator-bio-sanitize.utils';

describe('sanitizeBio', () => {
it('strips HTML tags from the bio', () => {
expect(sanitizeBio('Hello <b>world</b>! <p>How are you?</p>')).toBe(
'Hello world! How are you?'
);
});

it('removes invisible Unicode characters', () => {
expect(
sanitizeBio('Hello\u200Bworld\u200D!\uFEFFHow\u200Care you?')
).toBe('Helloworld!Howare you?');
});

it('normalizes whitespace to single spaces', () => {
expect(sanitizeBio('Hello world ! How are you?')).toBe(
'Hello world ! How are you?'
);
});

it('trims leading and trailing whitespace', () => {
expect(sanitizeBio(' Hello world! ')).toBe('Hello world!');
});

it('handles all sanitization requirements together', () => {
expect(
sanitizeBio(
'<div> Hello\u200B <b>world</b>! \uFEFF How are <i>you</i>? </div>'
)
).toBe('Hello world! How are you?');
});
});
17 changes: 17 additions & 0 deletions src/modules/creator/creator-bio-sanitize.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function sanitizeBio(bio: string): string {
let sanitized = bio;

// Strip HTML tags
sanitized = sanitized.replace(/<[^>]*>/g, '');

// Remove invisible Unicode characters (zero-width spaces, control characters, etc.)
// This includes:
// - Control characters (C0 and C1) except for space (0x20), tab (0x09), line feed (0x0A), carriage return (0x0D)
// - Zero-width spaces, joiners, non-joiners, etc.
sanitized = sanitized.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200D\u2060\uFEFF]/g, '');

// Normalize whitespace
sanitized = sanitized.replace(/\s+/g, ' ').trim();

return sanitized;
}
2 changes: 2 additions & 0 deletions src/modules/creator/creator-profile.schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod';
import { withCreatorSlugEmptyStringNormalization } from './creator-slug-input.utils';
import { normalizeSocialLinkUrl } from './creator-social-link-url.utils';
import { sanitizeBio } from './creator-bio-sanitize.utils';

/**
* Shared creator profile identifier schema for route params.
Expand Down Expand Up @@ -67,6 +68,7 @@ export const UpsertCreatorProfileBodySchema = z.object({
.string()
.trim()
.max(1000, 'Bio must be at most 1000 characters')
.transform(sanitizeBio)
.optional(),
avatarUrl: z
.string()
Expand Down
Loading