diff --git a/api/og-story.js b/api/og-story.js
index 63b48dd6e..5b1f114e6 100644
--- a/api/og-story.js
+++ b/api/og-story.js
@@ -25,12 +25,17 @@ const LEVEL_LABELS = {
low: 'LOW RISK',
};
+function normalizeLevel(rawLevel) {
+ const level = String(rawLevel || '').toLowerCase();
+ return Object.prototype.hasOwnProperty.call(LEVEL_COLORS, level) ? level : 'normal';
+}
+
export default function handler(req, res) {
const url = new URL(req.url, `https://${req.headers.host}`);
const countryCode = (url.searchParams.get('c') || '').toUpperCase();
const type = url.searchParams.get('t') || 'ciianalysis';
const score = url.searchParams.get('s');
- const level = url.searchParams.get('l') || 'normal';
+ const level = normalizeLevel(url.searchParams.get('l'));
const countryName = COUNTRY_NAMES[countryCode] || countryCode || 'Global';
const levelColor = LEVEL_COLORS[level] || '#eab308';
diff --git a/api/og-story.test.mjs b/api/og-story.test.mjs
new file mode 100644
index 000000000..d398d529e
--- /dev/null
+++ b/api/og-story.test.mjs
@@ -0,0 +1,48 @@
+import { strict as assert } from 'node:assert';
+import test from 'node:test';
+import handler from './og-story.js';
+
+function renderOgStory(query = '') {
+ const req = {
+ url: `https://worldmonitor.app/api/og-story${query ? `?${query}` : ''}`,
+ headers: { host: 'worldmonitor.app' },
+ };
+
+ let statusCode = 0;
+ let body = '';
+ const headers = {};
+
+ const res = {
+ setHeader(name, value) {
+ headers[String(name).toLowerCase()] = String(value);
+ },
+ status(code) {
+ statusCode = code;
+ return this;
+ },
+ send(payload) {
+ body = String(payload);
+ },
+ };
+
+ handler(req, res);
+ return { statusCode, body, headers };
+}
+
+test('normalizes unsupported level values to prevent SVG script injection', () => {
+ const injectedLevel = encodeURIComponent('');
+ const response = renderOgStory(`c=US&s=50&l=${injectedLevel}`);
+
+ assert.equal(response.statusCode, 200);
+ assert.equal(/NORMAL<\/text>/);
+});
+
+test('uses a known level when it is allowlisted', () => {
+ const response = renderOgStory('c=US&s=88&l=critical');
+
+ assert.equal(response.statusCode, 200);
+ assert.match(response.body, />CRITICAL<\/text>/);
+ assert.match(response.body, /#ef4444/);
+});
+