Commit 9b51e2a1 authored by tom's avatar tom

refactor csp policy generator

parent 56654f59
import _mergeWith from 'lodash/mergeWith';
import * as descriptors from './policies';
import { makePolicyString } from './utils';
const concat = (one: Array<unknown> | undefined, two: Array<unknown> | undefined) =>
one ? one.concat(two || []) : two;
function generateCspPolicy() {
const policyDescriptor = _mergeWith(
_mergeWith(
descriptors.app(),
descriptors.ad(),
descriptors.googleAnalytics(),
descriptors.googleFonts(),
concat,
),
_mergeWith(
descriptors.googleReCaptcha(),
descriptors.sentry(),
descriptors.walletConnect(),
concat,
),
concat,
);
return makePolicyString(policyDescriptor);
}
export default generateCspPolicy;
import type CspDev from 'csp-dev';
import isSelfHosted from 'lib/isSelfHosted';
export default function generateAdDescriptor(): CspDev.DirectiveDescriptor {
if (!isSelfHosted()) {
return {};
}
return {
'connect-src': [
'coinzilla.com',
'*.coinzilla.com',
'request-global.czilladx.com',
],
'frame-src': [
'request-global.czilladx.com',
],
'script-src': [
'coinzillatag.com',
'servedbyadbutler.com',
// what hash is this?
'\'sha256-wMOeDjJaOTjCfNjluteV+tSqHW547T89sgxd8W6tQJM=\'',
// what hash is this?
'\'sha256-FcyIn1h7zra8TVnnRhYrwrplxJW7dpD5TV7kP2AG/kI=\'',
],
'img-src': [
'servedbyadbutler.com',
'cdn.coinzilla.io',
],
'font-src': [
'request-global.czilladx.com',
],
};
}
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
import featuredNetworks from 'lib/networks/featuredNetworks';
const KEY_WORDS = {
BLOB: 'blob:',
DATA: 'data:',
NONE: '\'none\'',
REPORT_SAMPLE: `'report-sample'`,
SELF: '\'self\'',
STRICT_DYNAMIC: `'strict-dynamic'`,
UNSAFE_INLINE: '\'unsafe-inline\'',
UNSAFE_EVAL: '\'unsafe-eval\'',
};
import { KEY_WORDS } from '../utils';
const MAIN_DOMAINS = [
`*.${ appConfig.host }`,
......@@ -37,25 +30,12 @@ function getMarketplaceAppsHosts() {
};
}
// we cannot use lodash/uniq in middleware code since it calls new Set() and it'is causing an error in Nextjs
// "Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime"
function unique(array: Array<string | undefined>) {
const set: Record<string, boolean> = {};
for (const item of array) {
item && (set[item] = true);
}
return Object.keys(set);
}
function makePolicyMap() {
export default function generateAppDescriptor(): CspDev.DirectiveDescriptor {
const marketplaceAppsHosts = getMarketplaceAppsHosts();
return {
'default-src': [
// KEY_WORDS.NONE,
// temporarily, see if warnings for "/_next/static/chunks/8861-ad3efb7f624b7bc1.js" go away
...MAIN_DOMAINS,
KEY_WORDS.NONE,
],
'connect-src': [
......@@ -65,9 +45,6 @@ function makePolicyMap() {
// webpack hmr in safari doesn't recognize localhost as 'self' for some reason
appConfig.isDev ? 'ws://localhost:3000/_next/webpack-hmr' : '',
// client error monitoring
'sentry.io', '*.sentry.io',
// API
appConfig.api.endpoint,
appConfig.api.socket,
......@@ -75,67 +52,28 @@ function makePolicyMap() {
// chain RPC server
appConfig.network.rpcUrl,
// ad
'request-global.czilladx.com',
// walletconnect
'*.walletconnect.com',
'wss://*.bridge.walletconnect.org',
'wss://www.walletlink.org',
// RPC providers
'https://infragrid.v.network',
'https://infragrid.v.network', // RPC providers
// github (spec for api-docs page)
'raw.githubusercontent.com',
// google analytics
'https://www.googletagmanager.com',
'https://www.google-analytics.com',
'https://stats.g.doubleclick.net',
],
].filter(Boolean),
'script-src': [
KEY_WORDS.SELF,
...MAIN_DOMAINS,
// next.js generates and rebuilds source maps in dev using eval()
// https://github.com/vercel/next.js/issues/14221#issuecomment-657258278
appConfig.isDev ? KEY_WORDS.UNSAFE_EVAL : '',
...MAIN_DOMAINS,
// hash of ColorModeScript
'\'sha256-e7MRMmTzLsLQvIy1iizO1lXf7VWYoQ6ysj5fuUzvRwE=\'',
// ad
'coinzillatag.com',
'servedbyadbutler.com',
'\'sha256-wMOeDjJaOTjCfNjluteV+tSqHW547T89sgxd8W6tQJM=\'',
'\'sha256-FcyIn1h7zra8TVnnRhYrwrplxJW7dpD5TV7kP2AG/kI=\'',
// reCAPTCHA from google
'https://www.google.com/recaptcha/api.js',
'https://www.gstatic.com',
'https://translate.google.com',
'\'sha256-FDyPg8CqqIpPAfGVKx1YeKduyLs0ghNYWII21wL+7HM=\'',
// google analytics
'\'sha256-NTmEg2dBnojQfTYrYJEmp3nG7V66756qPbQMCIBrctk=\'',
'https://www.googletagmanager.com',
'https://www.google-analytics.com',
],
'style-src': [
KEY_WORDS.SELF,
...MAIN_DOMAINS,
// google fonts
'fonts.googleapis.com',
// reCAPTCHA from google
'https://www.gstatic.com',
// yes, it is unsafe as it stands, but
// - we cannot use hashes because all styles are generated dynamically
// - we cannot use nonces since we are not following along SSR path
......@@ -147,7 +85,6 @@ function makePolicyMap() {
'img-src': [
KEY_WORDS.SELF,
KEY_WORDS.DATA,
...MAIN_DOMAINS,
// github assets (e.g trustwallet token icons)
......@@ -165,34 +102,12 @@ function makePolicyMap() {
// marketplace apps logos
...marketplaceAppsHosts.logos,
// ad
'servedbyadbutler.com',
'cdn.coinzilla.io',
// walletconnect
'*.walletconnect.com',
// token's media
'ipfs.io',
// reCAPTCHA from google
'https://translate.google.com',
'https://www.gstatic.com',
// google analytics
'https://www.google-analytics.com',
],
'font-src': [
KEY_WORDS.DATA,
// google fonts
'fonts.gstatic.com',
'fonts.googleapis.com',
],
'prefetch-src': [
...MAIN_DOMAINS,
],
'object-src': [
......@@ -205,40 +120,12 @@ function makePolicyMap() {
'frame-src': [
...marketplaceAppsHosts.frames,
// ad
'request-global.czilladx.com',
// reCAPTCHA from google
// 'https://www.google.com/',
'https://www.google.com/recaptcha/api2/anchor',
'https://www.google.com/recaptcha/api2/bframe',
],
...(REPORT_URI ? {
...(REPORT_URI && !appConfig.isDev ? {
'report-uri': [
REPORT_URI,
],
} : {}),
};
}
function getCspPolicy() {
const policyMap = makePolicyMap();
const policyString = Object.entries(policyMap)
.map(([ key, value ]) => {
if (!value || value.length === 0) {
return;
}
const uniqueValues = unique(value);
return [ key, uniqueValues.join(' ') ].join(' ');
})
.filter(Boolean)
.join(';');
return policyString;
}
export default getCspPolicy;
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
export default function generateGoogleAnalyticsDescriptor(): CspDev.DirectiveDescriptor {
if (!appConfig.googleAnalytics.propertyId) {
return {};
}
return {
'connect-src': [
'https://www.googletagmanager.com',
'https://www.google-analytics.com',
'https://stats.g.doubleclick.net',
],
'script-src': [
// inline script hash, see ui/shared/GoogleAnalytics.tsx
'\'sha256-NTmEg2dBnojQfTYrYJEmp3nG7V66756qPbQMCIBrctk=\'',
'https://www.googletagmanager.com',
'https://www.google-analytics.com',
],
'img-src': [
'https://www.google-analytics.com',
],
};
}
import type CspDev from 'csp-dev';
export default function generateGoogleFontsDescriptor(): CspDev.DirectiveDescriptor {
// we use Inter and Poppins in the app
return {
'style-src': [
'fonts.googleapis.com',
],
'font-src': [
'fonts.gstatic.com',
'fonts.googleapis.com',
],
};
}
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
export default function generateGoogleReCaptchaDescriptor(): CspDev.DirectiveDescriptor {
if (!appConfig.reCaptcha.siteKey) {
return {};
}
return {
'script-src': [
'https://www.google.com/recaptcha/api.js',
'https://www.gstatic.com',
'https://translate.google.com',
'\'sha256-FDyPg8CqqIpPAfGVKx1YeKduyLs0ghNYWII21wL+7HM=\'',
],
'style-src': [
'https://www.gstatic.com',
],
'img-src': [
'https://translate.google.com',
'https://www.gstatic.com',
],
'frame-src': [
'https://www.google.com/recaptcha/api2/anchor',
'https://www.google.com/recaptcha/api2/bframe',
],
};
}
import ad from './ad';
import app from './app';
import googleAnalytics from './googleAnalytics';
import googleFonts from './googleFonts';
import googleReCaptcha from './googleReCaptcha';
import sentry from './sentry';
import walletConnect from './walletConnect';
export {
ad,
app,
googleAnalytics,
googleFonts,
googleReCaptcha,
sentry,
walletConnect,
};
import type CspDev from 'csp-dev';
export default function generateSentryDescriptor(): CspDev.DirectiveDescriptor {
return {
'connect-src': [
'sentry.io',
'*.sentry.io',
],
};
}
import type CspDev from 'csp-dev';
import appConfig from 'configs/app/config';
export default function generateWalletConnectDescriptor(): CspDev.DirectiveDescriptor {
if (!appConfig.walletConnect.projectId || !appConfig.network.rpcUrl) {
return {};
}
return {
'connect-src': [
'*.walletconnect.com',
'wss://*.bridge.walletconnect.org',
'wss://www.walletlink.org',
],
'img-src': [
'*.walletconnect.com',
],
};
}
import type CspDev from 'csp-dev';
export const KEY_WORDS = {
BLOB: 'blob:',
DATA: 'data:',
NONE: '\'none\'',
REPORT_SAMPLE: `'report-sample'`,
SELF: '\'self\'',
STRICT_DYNAMIC: `'strict-dynamic'`,
UNSAFE_INLINE: '\'unsafe-inline\'',
UNSAFE_EVAL: '\'unsafe-eval\'',
};
// we cannot use lodash/uniq in middleware code since it calls new Set() and it'is causing an error in Nextjs
// "Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime"
export function unique(array: Array<string | undefined>) {
const set: Record<string, boolean> = {};
for (const item of array) {
item && (set[item] = true);
}
return Object.keys(set);
}
export function makePolicyString(policyDescriptor: CspDev.DirectiveDescriptor) {
return Object.entries(policyDescriptor)
.map(([ key, value ]) => {
if (!value || value.length === 0) {
return;
}
const uniqueValues = unique(value);
return [ key, uniqueValues.join(' ') ].join(' ');
})
.filter(Boolean)
.join(';');
}
......@@ -4,9 +4,9 @@ import { route } from 'nextjs-routes';
import appConfig from 'configs/app/config';
import { NAMES } from 'lib/cookies';
import getCspPolicy from 'lib/csp/getCspPolicy';
import generateCspPolicy from 'lib/csp/generateCspPolicy';
const cspPolicy = getCspPolicy();
const cspPolicy = generateCspPolicy();
export function middleware(req: NextRequest) {
const isPageRequest = req.headers.get('accept')?.includes('text/html');
......
......@@ -3897,6 +3897,11 @@
dependencies:
"@types/node" "*"
"@types/csp-dev@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/csp-dev/-/csp-dev-1.0.0.tgz#59e2fd69f276988b349765c2f6a39ea0a4a1a161"
integrity sha512-OTHJTGqXvgFu1AE4Eo8cu+jgkQChpzTOHpMDrVkxAmNORVz7/11Zjj2NXZyWQcxibf18C966bU1JWtANrR8uJQ==
"@types/d3-array@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.3.tgz#87d990bf504d14ad6b16766979d04e943c046dac"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment