diff --git a/src/modules/creator/creator-bio-sanitize.utils.test.ts b/src/modules/creator/creator-bio-sanitize.utils.test.ts
new file mode 100644
index 0000000..2bfcde7
--- /dev/null
+++ b/src/modules/creator/creator-bio-sanitize.utils.test.ts
@@ -0,0 +1,33 @@
+import { sanitizeBio } from './creator-bio-sanitize.utils';
+
+describe('sanitizeBio', () => {
+ it('strips HTML tags from the bio', () => {
+ expect(sanitizeBio('Hello world!
How are you?
')).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(
+ ' Hello\u200B world! \uFEFF How are you?
'
+ )
+ ).toBe('Hello world! How are you?');
+ });
+});
diff --git a/src/modules/creator/creator-bio-sanitize.utils.ts b/src/modules/creator/creator-bio-sanitize.utils.ts
new file mode 100644
index 0000000..c540ab8
--- /dev/null
+++ b/src/modules/creator/creator-bio-sanitize.utils.ts
@@ -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;
+}
diff --git a/src/modules/creator/creator-profile.schemas.ts b/src/modules/creator/creator-profile.schemas.ts
index d223dde..c7b5fcf 100644
--- a/src/modules/creator/creator-profile.schemas.ts
+++ b/src/modules/creator/creator-profile.schemas.ts
@@ -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.
@@ -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()