Skip to content

Commit 4597235

Browse files
committed
sanitize html email; exit early if no recipients
1 parent 7505214 commit 4597235

File tree

3 files changed

+275
-19
lines changed

3 files changed

+275
-19
lines changed

src/api/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"pino": "^9.6.0",
5959
"pluralize": "^8.0.0",
6060
"qrcode": "^1.5.4",
61+
"sanitize-html": "^2.17.0",
6162
"stripe": "^18.0.0",
6263
"uuid": "^11.1.0",
6364
"zod": "^4.0.14",
@@ -67,9 +68,10 @@
6768
"@tsconfig/node22": "^22.0.1",
6869
"@types/aws-lambda": "^8.10.152",
6970
"@types/qrcode": "^1.5.5",
71+
"@types/sanitize-html": "^2.16.0",
7072
"esbuild-copy-static-files": "^0.1.0",
7173
"nodemon": "^3.1.10",
7274
"pino-pretty": "^13.1.1",
7375
"yaml": "^2.8.0"
7476
}
75-
}
77+
}

src/api/sqs/handlers/emailNotifications.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createAuditLogEntry } from "api/functions/auditLog.js";
66
import { Modules } from "common/modules.js";
77
import Handlebars from "handlebars";
88
import emailTemplate from "./templates/notification.js";
9+
import sanitizeHtml from "sanitize-html";
910

1011
Handlebars.registerHelper("nl2br", (text) => {
1112
let nl2br = `${text}`.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1<br>$2");
@@ -16,17 +17,23 @@ Handlebars.registerHelper("nl2br", (text) => {
1617
const compiledTemplate = Handlebars.compile(emailTemplate);
1718

1819
const stripHtml = (html: string): string => {
19-
return html
20-
.replace(/<[^>]*>/g, "") // Remove HTML tags
21-
.replace(/&nbsp;/g, " ") // Replace non-breaking spaces
22-
.replace(/\s+/g, " ") // Normalize whitespace
23-
.trim();
20+
return sanitizeHtml(
21+
html
22+
.replace(/<[^>]*>/g, "") // Remove HTML tags
23+
.replace(/&nbsp;/g, " ") // Replace non-breaking spaces
24+
.replace(/\s+/g, " ") // Normalize whitespace
25+
.trim(),
26+
);
2427
};
2528

2629
export const emailNotificationsHandler: SQSHandlerFunction<
2730
AvailableSQSFunctions.EmailNotifications
2831
> = async (payload, metadata, logger) => {
2932
const { to, cc, bcc, content, subject } = payload;
33+
if (to.length + (cc || []).length + (bcc || []).length === 0) {
34+
logger.warn("Found no message recipients. Exiting without calling SES.");
35+
return;
36+
}
3037
const senderEmailAddress = `notifications@${currentEnvironmentConfig.EmailDomain}`;
3138
const senderEmail = `ACM @ UIUC <${senderEmailAddress}>`;
3239
logger.info("Constructing email...");
@@ -59,17 +66,16 @@ export const emailNotificationsHandler: SQSHandlerFunction<
5966
},
6067
},
6168
});
62-
const logPromise = createAuditLogEntry({
69+
const sesClient = new SESClient({ region: genericConfig.AwsRegion });
70+
const response = await sesClient.send(command);
71+
logger.info("Sent!");
72+
await createAuditLogEntry({
6373
entry: {
6474
module: Modules.EMAIL_NOTIFICATION,
6575
actor: metadata.initiator,
66-
target: to.join(";"),
76+
target: [...to, ...(bcc || []), ...(cc || [])].join(";"),
6777
message: `Sent email notification with subject "${subject}".`,
6878
},
6979
});
70-
const sesClient = new SESClient({ region: genericConfig.AwsRegion });
71-
const response = await sesClient.send(command);
72-
logger.info("Sent!");
73-
await logPromise;
7480
return response;
7581
};

0 commit comments

Comments
 (0)