diff --git a/docker-compose.yml b/docker-compose.yml index 1abdaa7b54..0eaab3a265 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,8 +40,9 @@ services: - MYINFO_CLIENT_SECRET=mockClientSecret - WEBHOOK_SQS_URL=http://localhost:4566/000000000000/local-webhooks-sqs-main - INTRANET_IP_LIST_PATH + - SENTRY_CONFIG_URL=https://random@sentry.io/123456 + - CSP_REPORT_URI=https://random@sentry.io/123456 - GA_TRACKING_ID - - SENTRY_CONFIG_URL - TWILIO_ACCOUNT_SID - TWILIO_API_KEY - TWILIO_API_SECRET diff --git a/src/app/config/feature-manager/index.ts b/src/app/config/feature-manager/index.ts index 9ba8ba6d66..5bec5e8760 100644 --- a/src/app/config/feature-manager/index.ts +++ b/src/app/config/feature-manager/index.ts @@ -1,7 +1,6 @@ import FeatureManager from './util/FeatureManager.class' import captcha from './captcha.config' import googleAnalytics from './google-analytics.config' -import sentry from './sentry.config' import sms from './sms.config' import spcpMyInfo from './spcp-myinfo.config' import verifiedFields from './verified-fields.config' @@ -13,7 +12,6 @@ const featureManager = new FeatureManager() // Register features and associated middleware/fallbacks featureManager.register(captcha) -featureManager.register(sentry) featureManager.register(googleAnalytics) featureManager.register(spcpMyInfo) featureManager.register(webhookVerifiedContent) diff --git a/src/app/config/feature-manager/sentry.config.ts b/src/app/config/feature-manager/sentry.config.ts index a9bfc6e30d..efa4f4cd75 100644 --- a/src/app/config/feature-manager/sentry.config.ts +++ b/src/app/config/feature-manager/sentry.config.ts @@ -1,21 +1,28 @@ -import { FeatureNames, RegisterableFeature } from './types' +import convict, { Schema } from 'convict' +import { url } from 'convict-format-with-validator' -const sentryFeature: RegisterableFeature = { - name: FeatureNames.Sentry, - schema: { - sentryConfigUrl: { - doc: 'Sentry.io URL for configuring the Sentry SDK', - format: 'url', - default: null, - env: 'SENTRY_CONFIG_URL', - }, - cspReportUri: { - doc: 'Endpoint for content security policy reporting', - format: 'url', - default: null, - env: 'CSP_REPORT_URI', - }, +export interface ISentry { + sentryConfigUrl: string + cspReportUri: string +} + +convict.addFormat(url) + +const sentryFeature: Schema = { + sentryConfigUrl: { + doc: 'Sentry.io URL for configuring the Sentry SDK', + format: 'url', + default: null, + env: 'SENTRY_CONFIG_URL', + }, + cspReportUri: { + doc: 'Endpoint for content security policy reporting', + format: 'url', + default: null, + env: 'CSP_REPORT_URI', }, } -export default sentryFeature +export const sentryConfig = convict(sentryFeature) + .validate({ allowed: 'strict' }) + .getProperties() diff --git a/src/app/config/feature-manager/types.ts b/src/app/config/feature-manager/types.ts index 91321e4e1d..653da4bad0 100644 --- a/src/app/config/feature-manager/types.ts +++ b/src/app/config/feature-manager/types.ts @@ -4,7 +4,6 @@ import { Schema } from 'convict' export enum FeatureNames { Captcha = 'captcha', GoogleAnalytics = 'google-analytics', - Sentry = 'sentry', Sms = 'sms', SpcpMyInfo = 'spcp-myinfo', VerifiedFields = 'verified-fields', @@ -20,11 +19,6 @@ export interface IGoogleAnalytics { GATrackingID: string } -export interface ISentry { - sentryConfigUrl: string - cspReportUri: string -} - export interface ISms { twilioAccountSid: string twilioApiKey: string @@ -79,7 +73,6 @@ export interface IWebhookVerifiedContent { export interface IFeatureManager { [FeatureNames.Captcha]: ICaptcha [FeatureNames.GoogleAnalytics]: IGoogleAnalytics - [FeatureNames.Sentry]: ISentry [FeatureNames.Sms]: ISms [FeatureNames.SpcpMyInfo]: ISpcpMyInfo [FeatureNames.VerifiedFields]: IVerifiedFields diff --git a/src/app/loaders/express/__tests__/helmet.spec.ts b/src/app/loaders/express/__tests__/helmet.spec.ts index e41cf98247..c3b91117a9 100644 --- a/src/app/loaders/express/__tests__/helmet.spec.ts +++ b/src/app/loaders/express/__tests__/helmet.spec.ts @@ -2,7 +2,7 @@ import helmet from 'helmet' import { mocked } from 'ts-jest/utils' import config from 'src/app/config/config' -import featureManager from 'src/app/config/feature-manager' +import { sentryConfig } from 'src/app/config/feature-manager/sentry.config' import expressHandler from 'tests/unit/backend/helpers/jest-express' @@ -11,10 +11,10 @@ import helmetMiddlewares from '../helmet' describe('helmetMiddlewares', () => { jest.mock('helmet') const mockHelmet = mocked(helmet, true) - jest.mock('src/app/config/feature-manager') - const mockFeatureManager = mocked(featureManager, true) jest.mock('src/app/config/config') const mockConfig = mocked(config, true) + jest.mock('src/app/config/feature-manager/sentry.config') + const mockSentryConfig = mocked(sentryConfig, true) const cspCoreDirectives = { defaultSrc: ["'self'"], @@ -136,9 +136,7 @@ describe('helmetMiddlewares', () => { }) it('should call helmet.contentSecurityPolicy() with the correct directives if cspReportUri and !isDev', () => { - mockFeatureManager.props = jest - .fn() - .mockReturnValue({ cspReportUri: 'value' }) + mockSentryConfig.cspReportUri = 'value' mockConfig.isDev = false helmetMiddlewares() expect(mockHelmet.contentSecurityPolicy).toHaveBeenCalledWith({ @@ -151,7 +149,7 @@ describe('helmetMiddlewares', () => { }) it('should call helmet.contentSecurityPolicy() with the correct directives if !cspReportUri and isDev', () => { - mockFeatureManager.props = jest.fn() + mockSentryConfig.cspReportUri = '' mockConfig.isDev = true helmetMiddlewares() expect(mockHelmet.contentSecurityPolicy).toHaveBeenCalledWith({ diff --git a/src/app/loaders/express/helmet.ts b/src/app/loaders/express/helmet.ts index 05e6da84a6..7baef9a361 100644 --- a/src/app/loaders/express/helmet.ts +++ b/src/app/loaders/express/helmet.ts @@ -1,10 +1,9 @@ import { RequestHandler } from 'express' import helmet from 'helmet' import { ContentSecurityPolicyOptions } from 'helmet/dist/middlewares/content-security-policy' -import { get } from 'lodash' import config from '../../config/config' -import featureManager, { FeatureNames } from '../../config/feature-manager' +import { sentryConfig } from '../../config/feature-manager/sentry.config' const helmetMiddlewares = () => { // Only add the "Strict-Transport-Security" header if request is https. @@ -79,11 +78,7 @@ const helmetMiddlewares = () => { formAction: ["'self'"], } - const reportUri = get( - featureManager.props(FeatureNames.Sentry), - 'cspReportUri', - undefined, - ) + const reportUri = sentryConfig.cspReportUri const cspOptionalDirectives: ContentSecurityPolicyOptions['directives'] = {} diff --git a/src/app/loaders/express/locals.ts b/src/app/loaders/express/locals.ts index a3e5fe77f2..d8ca10d4e3 100644 --- a/src/app/loaders/express/locals.ts +++ b/src/app/loaders/express/locals.ts @@ -3,6 +3,7 @@ import { get } from 'lodash' import config from '../../config/config' import featureManager, { FeatureNames } from '../../config/feature-manager' +import { sentryConfig } from '../../config/feature-manager/sentry.config' // Construct js with environment variables needed by frontend const frontendVars = { @@ -17,11 +18,7 @@ const frontendVars = { 'captchaPublicKey', null, ), // Recaptcha - sentryConfigUrl: get( - featureManager.props(FeatureNames.Sentry), - 'sentryConfigUrl', - null, - ), // Sentry.IO + sentryConfigUrl: sentryConfig.sentryConfigUrl, // Sentry.IO isSPMaintenance: get( featureManager.props(FeatureNames.SpcpMyInfo), 'isSPMaintenance',