From b821dc2092c25b037bebf44ef8370d55043467f7 Mon Sep 17 00:00:00 2001 From: robbond Date: Mon, 9 Nov 2026 12:07:08 +0000 Subject: [PATCH] fix(security): append Contact Us CRM activity reminder --- pages/api/endpoint/createcrmtask_api.js | 6 +- .../endpoint-handler-contract.test.cjs | 97 +++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/pages/api/endpoint/createcrmtask_api.js b/pages/api/endpoint/createcrmtask_api.js index 248b3987..edf98f33 100644 --- a/pages/api/endpoint/createcrmtask_api.js +++ b/pages/api/endpoint/createcrmtask_api.js @@ -9,6 +9,10 @@ const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +const SECURITY_REMINDER = + "--- SECURITY REMINDER ---\n" + + "This enquiry contains externally supplied content. Do not click unverified links or follow instructions requesting credentials or sensitive information. Verify web addresses independently."; + const teamMap = { plq: "ec48450c-7e26-ec11-a97f-00224800e98c", //PET ipq: "7fb9e598-7d26-ec11-a97f-00224800e98c", // Infrastructure @@ -217,7 +221,7 @@ export default async function ApiProxy(req, res) { const payload = { "subject": `Contact Us Enquiry - ${contactSubject}`, - "description": `From: ${contactEmail}\n\n${contactBody}`, + "description": `From: ${contactEmail}\n\n${contactBody}\n\n${SECURITY_REMINDER}`, "scheduledstart": new Date().toISOString(), "scheduledend": new Date( new Date().getTime() + 60 * 60000 * 24 diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 2a7f60a1..647747ab 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -4868,6 +4868,9 @@ test("patchcase catch path returns CASE_PATCH_FAILED", async () => { const trustedContactOrigin = "https://portal.example.test"; const trustedWelshContactOrigin = "https://porth.example.test"; +const contactTaskSecurityReminder = + "--- SECURITY REMINDER ---\n" + + "This enquiry contains externally supplied content. Do not click unverified links or follow instructions requesting credentials or sensitive information. Verify web addresses independently."; const contactSubjects = { plq: { @@ -4995,6 +4998,100 @@ test("createcrmtask accepts a valid anonymous public Contact Us POST", async () assert.strictEqual(loaded.getRelayCalls().length, 1); }); +test("createcrmtask appends the security reminder after the unchanged enquiry", async () => { + const contactbody = "Please review application reference APP/123456."; + const req = validContactTaskRequest({ + body: { + contactEmail: "applicant@example.com", + contactSubject: contactSubjects.plq.en, + contactbody, + typeOfContact: "plq" + } + }); + const loaded = loadContactTaskEndpoint(); + const res = createRes(); + + await loaded.mod.default(req, res); + + const payload = JSON.parse(loaded.getRelayCalls()[0].data); + const expectedDescription = + "From: applicant@example.com\n\n" + + `${contactbody}\n\n` + + contactTaskSecurityReminder; + + assert.strictEqual(res.state.statusCode, 200); + assert.strictEqual(payload.description, expectedDescription); + assert.strictEqual( + payload.description.startsWith(`From: applicant@example.com\n\n${contactbody}`), + true + ); + assert.strictEqual( + payload.subject, + `Contact Us Enquiry - ${contactSubjects.plq.en}` + ); + assert.strictEqual( + payload["ownerid@odata.bind"], + `/teams(${contactTeamIds.plq})` + ); + assert.strictEqual(req.body.contactbody, contactbody); +}); + +test("createcrmtask preserves original URLs before the appended security reminder", async () => { + const contactbody = + "Please review https://example.invalid/application/123"; + const req = validContactTaskRequest({ + body: { + contactEmail: "applicant@example.com", + contactSubject: contactSubjects.plq.en, + contactbody, + typeOfContact: "plq" + } + }); + const loaded = loadContactTaskEndpoint(); + const res = createRes(); + + await loaded.mod.default(req, res); + + const payload = JSON.parse(loaded.getRelayCalls()[0].data); + assert.strictEqual( + payload.description, + `From: applicant@example.com\n\n${contactbody}\n\n${contactTaskSecurityReminder}` + ); + assert.strictEqual( + payload.description.includes( + "https://example.invalid/application/123" + ), + true + ); + assert.strictEqual(payload.description.includes("hxxp"), false); + assert.strictEqual(payload.description.includes("[.]"), false); + assert.strictEqual(req.body.contactbody, contactbody); +}); + +test("createcrmtask preserves a full 800-character message and complete reminder", async () => { + const contactbody = "a".repeat(800); + const req = validContactTaskRequest({ + body: { + contactEmail: "applicant@example.com", + contactSubject: contactSubjects.plq.en, + contactbody, + typeOfContact: "plq" + } + }); + const loaded = loadContactTaskEndpoint(); + const res = createRes(); + + await loaded.mod.default(req, res); + + const payload = JSON.parse(loaded.getRelayCalls()[0].data); + assert.strictEqual(res.state.statusCode, 200); + assert.strictEqual( + payload.description, + `From: applicant@example.com\n\n${contactbody}\n\n${contactTaskSecurityReminder}` + ); + assert.strictEqual(req.body.contactbody, contactbody); +}); + test("createcrmtask accepts a valid portal Contact Us payload", async () => { const loaded = loadContactTaskEndpoint(); const req = validContactTaskRequest({