Skip to content

Commit

Permalink
fix: Handle CORS for map docs URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Jan 18, 2024
1 parent 3a8015b commit 4f77ffa
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 99 deletions.
25 changes: 2 additions & 23 deletions api.planx.uk/modules/ordnanceSurvey/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,16 @@ import { IncomingMessage } from "http";

export const OS_DOMAIN = "https://api.os.uk";

const MAP_ALLOWLIST: RegExp[] = [
// Local development
/^http:\/\/(127\.0\.0\.1|localhost):(3000|5173|6006|7007)\/$/i,
// Documentation
/^https:\/\/.*\.netlify\.app\/$/i,
// PlanX
/^https:\/\/.*planx\.(pizza|dev|uk)\/$/i,
// Custom domains
/^https:\/\/.*(\.gov\.uk\/)$/i,
];

export const useOrdnanceSurveyProxy = async (
req: Request,
res: Response,
next: NextFunction,
) => {
if (!isValid(req))
return next({
status: 401,
message: "Unauthorised",
});

return useProxy({
) =>
useProxy({
target: OS_DOMAIN,
onProxyRes: (proxyRes) => setCORPHeaders(proxyRes),
pathRewrite: (fullPath, req) => appendAPIKey(fullPath, req),
})(req, res, next);
};

const isValid = (req: Request): boolean =>
MAP_ALLOWLIST.some((re) => re.test(req.headers?.referer as string));

const setCORPHeaders = (proxyRes: IncomingMessage): void => {
proxyRes.headers["Cross-Origin-Resource-Policy"] = "cross-origin";
Expand Down
73 changes: 0 additions & 73 deletions api.planx.uk/modules/ordnanceSurvey/ordnanceSurvey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe("Ordnance Survey proxy endpoint", () => {
.reply(200, { test: "returned tile" });

await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://123.planx.pizza/" })
.expect(200)
.then((response) => {
expect(response.body).toEqual({
Expand All @@ -33,7 +32,6 @@ describe("Ordnance Survey proxy endpoint", () => {
.reply(200, { test: "returned tile" });

await get(ENDPOINT + TILE_PATH + "?srs=3857")
.set({ referer: "https://www.planx.dev/" })
.expect(200)
.then((response) => {
expect(response.body).toEqual({
Expand All @@ -49,84 +47,13 @@ describe("Ordnance Survey proxy endpoint", () => {
.reply(401, { test: "failed request" });

await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://www.planx.uk/" })
.expect(401)
.then((response) => {
expect(response.body).toEqual({
test: "failed request",
});
});
});

describe("CORS functionality", () => {
it("blocks requests which are not from a valid referrer", async () => {
await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://www.invalid-site.com/" })
.expect(401)
.then((response) => {
expect(response.body).toEqual({
error: "Unauthorised",
});
});
});

it("allows requests from allow-listed URLs", async () => {
nock(OS_DOMAIN)
.get(TILE_PATH)
.query({ key: process.env.ORDNANCE_SURVEY_API_KEY })
.reply(200, { test: "returned tile" });

await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://oslmap.netlify.app/" })
.expect(200)
.then((response) => {
expect(response.body).toEqual({
test: "returned tile",
});
expect(response.headers["cross-origin-resource-policy"]).toEqual(
"cross-origin",
);
});
});

it("allows requests from PlanX", async () => {
nock(OS_DOMAIN)
.get(TILE_PATH)
.query({ key: process.env.ORDNANCE_SURVEY_API_KEY })
.reply(200, { test: "returned tile" });

await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://www.planx.dev/" })
.expect(200)
.then((response) => {
expect(response.body).toEqual({
test: "returned tile",
});
expect(response.headers["cross-origin-resource-policy"]).toEqual(
"cross-origin",
);
});
});

it("allows requests from custom domains", async () => {
nock(OS_DOMAIN)
.get(TILE_PATH)
.query({ key: process.env.ORDNANCE_SURVEY_API_KEY })
.reply(200, { test: "returned tile" });

await get(ENDPOINT + TILE_PATH)
.set({ referer: "https://planningservices.buckinghamshire.gov.uk/" })
.expect(200)
.then((response) => {
expect(response.body).toEqual({
test: "returned tile",
});
expect(response.headers["cross-origin-resource-policy"]).toEqual(
"cross-origin",
);
});
});
});
});

describe("appendAPIKey helper function", () => {
Expand Down
12 changes: 9 additions & 3 deletions api.planx.uk/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@ useSwaggerDocs(app);
app.set("trust proxy", 1);

const checkAllowedOrigins: CorsOptions["origin"] = (origin, callback) => {
if (!origin) return callback(null, true);

const isTest = process.env.NODE_ENV === "test";
const isDevelopment = process.env.APP_ENVIRONMENT === "development";
const localDevEnvs =
/^http:\/\/(127\.0\.0\.1|localhost):(3000|5173|6006|7007)$/;
const isDevelopment =
process.env.APP_ENVIRONMENT === "development" || localDevEnvs.test(origin);
const allowList = process.env.CORS_ALLOWLIST?.split(", ") || [];
const isAllowed = Boolean(origin && allowList.includes(origin));
const isAllowed = Boolean(allowList.includes(origin));
const isMapDocs = Boolean(origin.endsWith("oslmap.netlify.app"));

!origin || isTest || isDevelopment || isAllowed
isTest || isDevelopment || isAllowed || isMapDocs
? callback(null, true)
: callback(new Error("Not allowed by CORS"));
};
Expand Down

0 comments on commit 4f77ffa

Please sign in to comment.