diff --git a/src/api/utils/RoomChatFormatter.test.ts b/src/api/utils/RoomChatFormatter.test.ts
new file mode 100644
index 0000000..403fd87
--- /dev/null
+++ b/src/api/utils/RoomChatFormatter.test.ts
@@ -0,0 +1,112 @@
+import { describe, expect, it } from 'vitest';
+
+import { RoomChatFormatter } from './RoomChatFormatter';
+
+/**
+ * Security + behaviour suite for the chat formatter.
+ *
+ * The formatter output is injected into the DOM via `dangerouslySetInnerHTML`
+ * in ChatWidgetMessageView, so the security contract is: after the browser
+ * parses the formatted string as HTML, NO attacker-controlled executable
+ * markup may survive (no ');
+ expect(div.querySelector('script')).toBeNull();
+ });
+
+ it('does not produce an element with an onerror handler', () =>
+ {
+ const div = parse('');
+ const img = div.querySelector('img');
+ expect(img).toBeNull();
+ });
+
+ it('does not produce an