diff --git a/README.md b/README.md index f6d9930baa..f7523d8075 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Our public-facing live services were last audited by the [Digital Accessibility ### Security -Our whole stack was last assessed by [Jumpsec](https://www.jumpsec.com/) between the 21st and 30th November 2023. JUMPSEC then performed a retest of the issues identified in the initial test on the 8th of February 2024. This included verifying that fixes had been successfully applied and that no further risks were introduced as a result of the remediation work carried out. Their penetration test concluded that - "the security posture of PlanX was strong, and following industry best practices. JUMPSEC commend the PlanX team on their dedication to security and ability to both maintain and mitigate issues in a responsible and timely manner". +Our whole stack was last assessed by [Jumpsec](https://www.jumpsec.com/) between the 21st and 30th November 2023. JUMPSEC then performed a retest of the issues identified in the initial test on the 8th of February 2024. This included verifying that fixes had been successfully applied and that no further risks were introduced as a result of the remediation work carried out. Their penetration test concluded that - "the security posture of PlanX was strong, and following industry best practices. JUMPSEC commend the PlanX team on their dedication to security and ability to both maintain and mitigate issues in a responsible and timely manner". You can [review our report here](https://file.notion.so/f/f/d2306134-7ae0-417c-8db3-cdd87c524efa/ab940248-ca60-49a3-bfae-0b6faa916b1e/2024-02-13_JUMPSEC_Lambeth_PlanX_Web_Application_Assessment_Report_v2.0.pdf?id=aa4ed144-4b48-4a88-9693-6a3644bfd6cf&table=block&spaceId=d2306134-7ae0-417c-8db3-cdd87c524efa&expirationTimestamp=1707998400000&signature=76AfnXjSTzw8O5TEW9Ao0mOmWTG4WzE8rm-Rfa54wGU&downloadName=Penetration+test+%28Jumpsec%29+13%2F02%2F24.pdf). ## Related packages diff --git a/api.planx.uk/modules/auth/middleware.ts b/api.planx.uk/modules/auth/middleware.ts index bf0f412e13..b1695d5fe1 100644 --- a/api.planx.uk/modules/auth/middleware.ts +++ b/api.planx.uk/modules/auth/middleware.ts @@ -207,3 +207,13 @@ export const useLoginAuth: RequestHandler = (req, res, next) => }); } }); + +export const useNoCache: RequestHandler = (_req, res, next) => { + res.setHeader("Surrogate-Control", "no-store"); + res.setHeader( + "Cache-Control", + "no-store, no-cache, must-revalidate, proxy-revalidate", + ); + res.setHeader("Expires", "0"); + next(); +}; diff --git a/api.planx.uk/modules/file/routes.ts b/api.planx.uk/modules/file/routes.ts index 5603d962f7..9f301b4ea2 100644 --- a/api.planx.uk/modules/file/routes.ts +++ b/api.planx.uk/modules/file/routes.ts @@ -1,7 +1,11 @@ import { Router } from "express"; import multer from "multer"; -import { useFilePermission, useTeamEditorAuth } from "../auth/middleware"; +import { + useNoCache, + useFilePermission, + useTeamEditorAuth, +} from "../auth/middleware"; import { downloadFileSchema, privateDownloadController, @@ -37,6 +41,7 @@ router.get( router.get( "/file/private/:fileKey/:fileName", + useNoCache, useFilePermission, validate(downloadFileSchema), privateDownloadController, diff --git a/api.planx.uk/modules/send/utils/helpers.ts b/api.planx.uk/modules/send/utils/helpers.ts index d527360434..06db45b327 100644 --- a/api.planx.uk/modules/send/utils/helpers.ts +++ b/api.planx.uk/modules/send/utils/helpers.ts @@ -21,8 +21,7 @@ export async function logPaymentStatus({ }): Promise { if (!flowId || !sessionId) { reportError({ - message: - "Could not log the payment status due to missing context value(s)", + error: "Could not log the payment status due to missing context value(s)", context: { sessionId, flowId, teamSlug }, }); } else { @@ -38,21 +37,20 @@ export async function logPaymentStatus({ }); } catch (e) { reportError({ - message: "Failed to insert a payment status", - error: e, - govUkResponse, + error: `Failed to insert a payment status: ${e}`, + context: { govUkResponse }, }); } } } // tmp explicit error handling -export function reportError(obj: object) { +export function reportError(report: { error: any; context: object }) { if (airbrake) { - airbrake.notify(obj); + airbrake.notify(report); return; } - log(obj); + log(report); } // tmp logger diff --git a/api.planx.uk/modules/webhooks/service/validateInput/utils.ts b/api.planx.uk/modules/webhooks/service/validateInput/utils.ts index 628aee405d..3f8b772598 100644 --- a/api.planx.uk/modules/webhooks/service/validateInput/utils.ts +++ b/api.planx.uk/modules/webhooks/service/validateInput/utils.ts @@ -59,8 +59,10 @@ export const isCleanHTML = (input: unknown): boolean => { */ const logUncleanHTMLError = (input: string, cleanHTML: string) => { reportError({ - message: `Warning: Unclean HTML submitted!`, - input, - cleanHTML, + error: `Warning: Unclean HTML submitted!`, + context: { + input, + cleanHTML, + }, }); }; diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index bb8b3aba97..cead9db90d 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -145,7 +145,45 @@ test.describe("Navigation", () => { await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); }); - test("Preview a created flow", async ({ browser }: { browser: Browser }) => { + test("Cannot preview an unpublished flow", async ({ + browser, + }: { + browser: Browser; + }) => { + const page = await createAuthenticatedSession({ + browser, + userId: context.user!.id!, + }); + + await page.goto( + `/${context.team.slug}/${serviceProps.slug}/preview?analytics=false`, + ); + + await expect(page.getByText("Not Found")).toBeVisible(); + }); + + test("Publish a flow", async ({ browser }) => { + const page = await createAuthenticatedSession({ + browser, + userId: context.user!.id!, + }); + + await page.goto(`/${context.team.slug}/${serviceProps.slug}`); + + page.getByRole("button", { name: "CHECK FOR CHANGES TO PUBLISH" }).click(); + page.getByRole("button", { name: "PUBLISH", exact: true }).click(); + + const previewLink = page.getByRole("link", { + name: "Open published service", + }); + await expect(previewLink).toBeVisible(); + }); + + test("Can preview a published flow", async ({ + browser, + }: { + browser: Browser; + }) => { const page = await createAuthenticatedSession({ browser, userId: context.user!.id!, diff --git a/editor.planx.uk/src/@planx/components/PropertyInformation/Editor.tsx b/editor.planx.uk/src/@planx/components/PropertyInformation/Editor.tsx index e35fe88c36..98c5a24d45 100644 --- a/editor.planx.uk/src/@planx/components/PropertyInformation/Editor.tsx +++ b/editor.planx.uk/src/@planx/components/PropertyInformation/Editor.tsx @@ -35,7 +35,7 @@ function PropertyInformationComponent(props: Props) {