Skip to content

Commit 843c123

Browse files
adds feature flag to protect dev api endpoints (#268)
1 parent 8bca923 commit 843c123

File tree

10 files changed

+63
-21
lines changed

10 files changed

+63
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as v from 'valibot';
2+
3+
import { stringToBooleanSchema } from '../validation/string-to-boolean-schema';
4+
5+
export type DevEndpoints = Readonly<v.InferOutput<typeof devEndpoints>>;
6+
7+
export const defaults = {
8+
DEV_ENDPOINTS_ENABLED: 'false',
9+
} as const;
10+
11+
export const devEndpoints = v.object({
12+
DEV_ENDPOINTS_ENABLED: v.optional(stringToBooleanSchema(), defaults.DEV_ENDPOINTS_ENABLED),
13+
});

frontend/app/.server/environment/server.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as v from 'valibot';
22

33
import { client, defaults as clientDefaults } from '~/.server/environment/client';
4+
import { devEndpoints, defaults as devEndpointsDefaults } from '~/.server/environment/dev-endpoints';
45
import { logging, defaults as loggingDefaults } from '~/.server/environment/logging';
56
import { redis, defaults as redisDefaults } from '~/.server/environment/redis';
67
import { session, defaults as sessionDefaults } from '~/.server/environment/session';
@@ -15,6 +16,7 @@ export const defaults = {
1516
NODE_ENV: 'development',
1617
PORT: '3000',
1718
...clientDefaults,
19+
...devEndpointsDefaults,
1820
...loggingDefaults,
1921
...redisDefaults,
2022
...sessionDefaults,
@@ -28,6 +30,7 @@ export const defaults = {
2830
export const server = v.pipe(
2931
v.object({
3032
...client.entries,
33+
...devEndpoints.entries,
3134
...logging.entries,
3235
...redis.entries,
3336
...session.entries,
+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { Route } from './+types/debug-session';
22

3+
import { serverEnvironment } from '~/.server/environment';
4+
35
export function loader({ context }: Route.LoaderArgs) {
4-
/*if (process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'dev') {
6+
if (!serverEnvironment.DEV_ENDPOINTS_ENABLED) {
57
throw new Response('Forbidden', { status: 403 });
6-
}*/
8+
}
79
return Response.json(context.session);
810
}

frontend/app/routes/dev/error.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
import type { Route } from './+types/error';
2+
3+
import { serverEnvironment } from '~/.server/environment';
14
import { AppError } from '~/errors/app-error';
25
import { ErrorCodes } from '~/errors/error-codes';
36

7+
export function loader({ context }: Route.LoaderArgs) {
8+
if (!serverEnvironment.DEV_ENDPOINTS_ENABLED) {
9+
throw new Response('Forbidden', { status: 403 });
10+
}
11+
return;
12+
}
13+
414
/**
515
* An error route that can be used to test error boundaries.
616
*/
717
export default function Error() {
8-
/*if (process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'dev') {
9-
throw new Response('Forbidden', { status: 403 });
10-
}*/
1118
throw new AppError('This is a test error', ErrorCodes.TEST_ERROR_CODE);
1219
}

frontend/app/routes/dev/stage-session.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { Route } from './+types/stage-session';
22

3+
import { serverEnvironment } from '~/.server/environment';
4+
35
export async function action({ context, request }: Route.ActionArgs) {
4-
/*console.log(process.env.NODE_ENV);
5-
if (process.env.NODE_ENV !== 'test' && process.env.NODE_ENV !== 'dev') {
6+
if (!serverEnvironment.DEV_ENDPOINTS_ENABLED) {
67
throw new Response('Forbidden', { status: 403 });
7-
}*/
8+
}
89

910
const json = await request.json();
1011

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Page } from '@playwright/test';
22

33
export async function seedSessionData(page: Page, data: object) {
4-
await page.request.post('/stage-session', { data: data });
4+
return await page.request.post('/stage-session', { data: data });
55
}

frontend/e2e/estimator/results.spec.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,27 @@ const stagedSession = {
2626
};
2727

2828
test('Navigating to /en/results renders the english results page', async ({ page }) => {
29-
await seedSessionData(page, stagedSession);
29+
const resp = await seedSessionData(page, stagedSession);
30+
expect(resp.status()).toBe(200);
3031

3132
await page.goto('/en/results');
3233

3334
expect(await formatHtml(await page.locator('main').innerHTML())).toMatchSnapshot();
3435
});
3536

3637
test('Navigating to /fr/resultats renders the french results page', async ({ page }) => {
37-
await seedSessionData(page, stagedSession);
38+
const resp = await seedSessionData(page, stagedSession);
39+
expect(resp.status()).toBe(200);
40+
3841
await page.goto('/fr/resultats');
3942

4043
expect(await formatHtml(await page.locator('main').innerHTML())).toMatchSnapshot();
4144
});
4245

4346
test('/en/results passes a11y checks', async ({ page }) => {
44-
await seedSessionData(page, stagedSession);
47+
const resp = await seedSessionData(page, stagedSession);
48+
expect(resp.status()).toBe(200);
49+
4550
await page.goto('/en/results');
4651
await page.locator('main').waitFor();
4752

@@ -51,7 +56,9 @@ test('/en/results passes a11y checks', async ({ page }) => {
5156
});
5257

5358
test('/fr/resultats passes a11y checks', async ({ page }) => {
54-
await seedSessionData(page, stagedSession);
59+
const resp = await seedSessionData(page, stagedSession);
60+
expect(resp.status()).toBe(200);
61+
5562
await page.goto('/fr/resultats');
5663
await page.locator('main').waitFor();
5764

frontend/e2e/estimator/step-income.spec.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const stagedSession = {
1111
};
1212

1313
test('Navigating to /en/income renders the english income page', async ({ page }) => {
14-
await seedSessionData(page, stagedSession);
14+
const resp = await seedSessionData(page, stagedSession);
15+
expect(resp.status()).toBe(200);
1516

1617
await page.goto('/en/income');
1718
expect(page.url()).toContain('/en/income');
@@ -20,14 +21,18 @@ test('Navigating to /en/income renders the english income page', async ({ page }
2021
});
2122

2223
test('Navigating to /fr/revenus renders the french income page', async ({ page }) => {
23-
await seedSessionData(page, stagedSession);
24+
const resp = await seedSessionData(page, stagedSession);
25+
expect(resp.status()).toBe(200);
26+
2427
await page.goto('/fr/revenus');
2528
expect(page.url()).toContain('/fr/revenus');
2629
expect(await formatHtml(await page.locator('main').innerHTML())).toMatchSnapshot();
2730
});
2831

2932
test('/en/income passes a11y checks', async ({ page }) => {
30-
await seedSessionData(page, stagedSession);
33+
const resp = await seedSessionData(page, stagedSession);
34+
expect(resp.status()).toBe(200);
35+
3136
await page.goto('/en/income');
3237
expect(page.url()).toContain('/en/income');
3338
await page.locator('main').waitFor();
@@ -38,7 +43,9 @@ test('/en/income passes a11y checks', async ({ page }) => {
3843
});
3944

4045
test('/fr/revenus passes a11y checks', async ({ page }) => {
41-
await seedSessionData(page, stagedSession);
46+
const resp = await seedSessionData(page, stagedSession);
47+
expect(resp.status()).toBe(200);
48+
4249
await page.goto('/fr/revenus');
4350
expect(page.url()).toContain('/fr/revenus');
4451
await page.locator('main').waitFor();

frontend/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
"build": "npm run build:application && npm run build:server",
99
"build:application": "react-router build",
1010
"build:server": "vite build --config ./vite.server.config.ts",
11-
"dev": "nodemon --config nodemon.json",
11+
"dev": "cross-env DEV_ENDPOINTS_ENABLED=true nodemon --config nodemon.json",
1212
"preview": "npm run build && cross-env NODE_ENV=production SESSION_COOKIE_SECURE=false node --env-file-if-exists=.env --import ./build/server/telemetry.js ./build/server/server.js",
1313
"start": "cross-env NODE_ENV=production node --import ./build/server/telemetry.js ./build/server/server.js",
1414
"checks": "node --experimental-strip-types ./scripts/checks-runner.ts",
15-
"test": "vitest",
16-
"test:e2e": "playwright test",
15+
"test": "cross-env DEV_ENDPOINTS_ENABLED=true vitest ",
16+
"test:e2e": "cross-env DEV_ENDPOINTS_ENABLED=true playwright test",
1717
"format:check": "prettier --cache --check .",
1818
"format:write": "prettier --write .",
1919
"lint:check": "eslint --cache ./",

gitops/overlays/dev/configs/cdb-estimator-frontend/config.conf

+3-1
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,6 @@ OTEL_TRACES_ENDPOINT=https://dynatrace.dev-dp.admin.dts-stn.com/e/21a07aef-852b-
134134
OTEL_USE_CONSOLE_METRIC_EXPORTER=
135135

136136
# Enable the console trace exporter (default: false).
137-
OTEL_USE_CONSOLE_TRACE_EXPORTER=
137+
OTEL_USE_CONSOLE_TRACE_EXPORTER=
138+
139+
DEV_ENDPOINTS_ENABLED=true

0 commit comments

Comments
 (0)