Skip to content

Commit

Permalink
feat: security headers for apps (#65)
Browse files Browse the repository at this point in the history
* feat: Add next-middleware library and related files

* feat: renames files for middleware

* feat: remove unsafe-inline for style-src

* chore: include object-src

* chore: remove unsafe inline for firefox

* chore: IMPORTANT - got A+ but changing the policy to make it work as it is broken now

* chore: adding unsafe-inline for style-src

* chore: remove font-src, object-src and scriptSrc to have unsafe-inline (for firefox)

* refractor: move constants above the function in cspHeader, remove browerName usage as it works on firefox

* chore: add 'nonce' support for style-src and commented unsafe-inline

* chore: add nonce

* chore: remove nonce and add unsafe-inline for style

* chore: add api.thegraph.com origin

* feat: add middleware to other apps

* feat: remove headers from next.config.js for bond & tokenomics app

* chore: rename getCspHeader function name

* Update libs/common-middleware/src/lib/cspHeader.ts

Co-authored-by: Josh Miller <[email protected]>

* refactor: Remove duplicate code for address prohibition check

* refactor: Remove duplicate code for address prohibition check

* feat: Add Vercel links to CSP allowed origins

* feat: Add gateway links to CSP allowed origins

* feat: Update IPFS gateway links in CSP allowed origins

---------

Co-authored-by: Josh Miller <[email protected]>
  • Loading branch information
mohandast52 and truemiller authored Jul 25, 2024
1 parent 895b96b commit 37375b3
Show file tree
Hide file tree
Showing 29 changed files with 323 additions and 247 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Form, Input, Select } from 'antd';
import { Button, Flex, Form, Input, Select } from 'antd';
import isNil from 'lodash/isNil';
import PropTypes from 'prop-types';
import React, { Fragment, useState } from 'react';
Expand Down Expand Up @@ -220,7 +220,21 @@ export const IpfsHashGenerationModal = ({
<Form.Item
label="NFT Image URL"
name="image"
extra="Represents your NFT on marketplaces such as OpenSea"
extra={
<Flex vertical>
<span>
Represents your NFT on marketplaces such as OpenSea. Current supported domains are:
</span>
<span>
{/* TODO: fetch from middleware constant */}
<ul style={{ margin: 0 }}>
<li>https://gateway.autonolas.tech/ipfs/*</li>
<li>https://gateway.pinata.cloud/ipfs/*</li>
<li>https://*.arweave.net/</li>
</ul>
</span>
</Flex>
}
>
<Input />
</Form.Item>
Expand Down
3 changes: 2 additions & 1 deletion apps/autonolas-registry/common-util/Login/LoginV2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import { useAccount, useBalance, useDisconnect, useSwitchChain } from 'wagmi';

import { CannotConnectAddressOfacError, notifyError, useScreen } from '@autonolas/frontend-library';

import { isAddressProhibited } from 'libs/util-prohibited-data/src/index';

import { setUserBalance } from 'store/setup';

import { YellowButton } from '../YellowButton';
import { isAddressProhibited } from '../functions';
import { useHelpers } from '../hooks';
import { SolanaWallet } from './SolanaWallet';

Expand Down
9 changes: 1 addition & 8 deletions apps/autonolas-registry/common-util/functions/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PublicKey } from '@solana/web3.js';
import { ethers } from 'ethers';
import { isString } from 'lodash';

import {
getChainIdOrDefaultToMainnet as getChainIdOrDefaultToMainnetFn,
Expand All @@ -10,9 +11,6 @@ import {
sendTransaction as sendTransactionFn,
} from '@autonolas/frontend-library';

import prohibitedAddresses from 'libs/util-prohibited-data/src/lib/prohibited-addresses.json';
import { isString, toLower } from 'lodash';

import { VM_TYPE } from '../../util/constants';
import { RPC_URLS } from '../Contracts';
import { SUPPORTED_CHAINS } from '../Login';
Expand Down Expand Up @@ -178,11 +176,6 @@ export const checkIfGnosisSafe = async (account, provider) => {
*/
export const doesNetworkHaveValidServiceManagerTokenFn = (chainId) => !!chainId;

export const isAddressProhibited = (address) => {
const addresses = prohibitedAddresses.map((e) => toLower(e));
return addresses.includes(toLower(address));
};

const doesPathIncludesComponents = (path) => !!path?.includes('components');
const doesPathIncludesAgents = (path) => !!path?.includes('agents');
export const doesPathIncludesServices = (path) => !!path?.includes('services');
Expand Down
152 changes: 3 additions & 149 deletions apps/autonolas-registry/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,150 +1,4 @@
import nextSafe from 'next-safe';
import { NextRequest, NextResponse, userAgent } from 'next/server';
import { config, middleware } from 'libs/common-middleware/src';

import prohibitedCountries from 'libs/util-prohibited-data/src/lib/prohibited-countries.json';

const prohibitedCountriesCode = Object.values(prohibitedCountries);

const isDev = process.env.NODE_ENV !== 'production';

const getCspHeader = (browserName?: string) => {
if (!process.env.NEXT_PUBLIC_AUTONOLAS_SUB_GRAPH_URL) return [];

const walletconnectSrc = ['https://verify.walletconnect.org', 'https://verify.walletconnect.com'];

const connectSrc: CSPDirective = [
"'self'",
...walletconnectSrc,
'https://*.olas.network/',
'https://*.autonolas.tech/',
'https://rpc.walletconnect.com/',
'wss://relay.walletconnect.org/',
'wss://relay.walletconnect.com/',
'https://explorer-api.walletconnect.com/',
'https://eth-mainnet.g.alchemy.com/v2/',
'https://eth-goerli.g.alchemy.com/v2/',
'https://gno.getblock.io/',
'https://polygon-mainnet.g.alchemy.com/v2/',
'https://polygon-mumbai-bor.publicnode.com/',
'https://rpc.chiado.gnosis.gateway.fm/',
'https://safe-transaction-mainnet.safe.global/api/v1/',
'https://safe-transaction-goerli.safe.global/api/',
'https://safe-transaction-gnosis-chain.safe.global/api/',
'https://safe-transaction-polygon.safe.global/api/',
'https://vercel.live/',
'https://api.devnet.solana.com/',
'wss://api.devnet.solana.com/',
'https://api.mainnet-beta.solana.com/',
'wss://api.mainnet-beta.solana.com/',
'https://holy-convincing-bird.solana-mainnet.quiknode.pro/',
'wss://holy-convincing-bird.solana-mainnet.quiknode.pro/',
'https://arb1.arbitrum.io/rpc/',
'https://sepolia-rollup.arbitrum.io/rpc',
'https://rpc.gnosischain.com/',
'https://mainnet.base.org/',
'https://sepolia.base.org/',
'https://mainnet.optimism.io',
'https://sepolia.optimism.io/',
'https://forno.celo.org',
'https://alfajores-forno.celo-testnet.org',
'https://api.web3modal.com/',
'wss://www.walletlink.org/rpc',
'wss://*.pusher.com/',
process.env.NEXT_PUBLIC_AUTONOLAS_SUB_GRAPH_URL,
];

if (isDev) {
connectSrc.push('http://localhost');
connectSrc.push('ws://localhost');
}

const scriptSrc = ["'self'", 'https://vercel.live/', 'https://fonts.googleapis.com/'];

// Firefox blocks inline scripts by default and it's an issue with Metamask
// reference: https://github.com/MetaMask/metamask-extension/issues/3133
if (browserName === 'Firefox') {
scriptSrc.push("'unsafe-inline'");
}

const nextSafeHeaders =
typeof nextSafe === 'function'
? // TODO
// @ts-expect-error: For some reason, TypeScript is not recognizing the function
nextSafe({
isDev,
/**
* Content Security Policy
* @see https://content-security-policy.com/
*/
contentSecurityPolicy: {
'default-src': "'none'",
'script-src': scriptSrc,
'connect-src': connectSrc,
'img-src': [
"'self'",
'blob:',
'data:',
'https://*.autonolas.tech/',
'https://explorer-api.walletconnect.com/w3m/',
...walletconnectSrc,
],
'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com/'],
'frame-src': ["'self'", 'https://vercel.live/', ...walletconnectSrc],
},
permissionsPolicyDirectiveSupport: ['standard'],
})
: [];

const headers = [
...nextSafeHeaders,
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains',
},
];

return headers;
};

const getRedirectUrl = (pathName: string, countryName?: string) => {
const isProhibited = countryName ? prohibitedCountriesCode.includes(countryName) : false;

if (pathName === '/not-legal') {
return isProhibited ? null : '/';
}
return isProhibited ? '/not-legal' : null;
};

export default async function middleware(request: NextRequest) {
const country = request.geo?.country;
const redirectUrl = getRedirectUrl(request.nextUrl.pathname, country);

const response = redirectUrl
? NextResponse.redirect(new URL(redirectUrl, request.nextUrl))
: NextResponse.next();

const browserName = userAgent(request)?.browser.name;
const cspHeaders = getCspHeader(browserName);

// apply CSP headers
// https://nextjs.org/docs/app/building-your-application/routing/middleware#setting-headers
cspHeaders.forEach((header) => {
const { key, value } = header;
response.headers.set(key, value);
});

return response;
}

export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};
export default middleware;
export { config };
3 changes: 2 additions & 1 deletion apps/bond/common-util/Login/LoginV2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
notifyError,
} from '@autonolas/frontend-library';

import { isAddressProhibited } from 'common-util/functions/addresses';
import { isAddressProhibited } from 'libs/util-prohibited-data/src/index';

import { getChainId, getChainIdOrDefaultToMainnet } from 'common-util/functions/frontend-library';
import { setChainId, setUserBalance } from 'store/setup';

Expand Down
7 changes: 0 additions & 7 deletions apps/bond/common-util/functions/addresses.js

This file was deleted.

1 change: 0 additions & 1 deletion apps/bond/common-util/functions/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './addresses';
export * from './chains';
export * from './errors';
export * from './ethers';
Expand Down
4 changes: 4 additions & 0 deletions apps/bond/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config, middleware } from 'libs/common-middleware/src';

export default middleware;
export { config };
35 changes: 0 additions & 35 deletions apps/bond/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,6 @@ const nextConfig = {
};
return config;
},
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: "frame-ancestors 'none';",
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains',
},
],
},
{
source: '/:all*(svg|jpg|jpeg|png|gif|ico|css|js|mov|mp4)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, must-revalidate',
},
],
},
];
},

};

const plugins = [
Expand Down
8 changes: 0 additions & 8 deletions apps/govern/common-util/functions/addresses.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { ethers } from 'ethers';
import { toLower } from 'lodash';
import { Address } from 'viem';

import prohibitedAddresses from 'libs/util-prohibited-data/src/lib/prohibited-addresses.json';

export const isAddressProhibited = (address: Address | undefined) => {
const addresses = prohibitedAddresses.map((e) => toLower(e));
return addresses.includes(toLower(address));
};

export const getAddressFromBytes32 = (address: Address | string) => {
return ('0x' + address.slice(-40)) as Address;
};
Expand Down
3 changes: 2 additions & 1 deletion apps/govern/components/Login/LoginV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { useCallback, useEffect } from 'react';
import styled from 'styled-components';
import { useAccountEffect, useConfig, useDisconnect } from 'wagmi';

import { isAddressProhibited } from 'libs/util-prohibited-data/src/index';

import { INVALIDATE_AFTER_ACCOUNT_CHANGE } from 'common-util/constants/scopeKeys';
import { isAddressProhibited } from 'common-util/functions';
import { queryClient } from 'context/Web3ModalProvider';
import { clearUserState } from 'store/govern';
import { useAppDispatch } from 'store/index';
Expand Down
4 changes: 4 additions & 0 deletions apps/govern/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config, middleware } from 'libs/common-middleware/src';

export default middleware;
export { config };
4 changes: 4 additions & 0 deletions apps/launch/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config, middleware } from 'libs/common-middleware/src';

export default middleware;
export { config };
3 changes: 2 additions & 1 deletion apps/tokenomics/common-util/Login/LoginV2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
notifyError,
} from '@autonolas/frontend-library';

import { isAddressProhibited } from 'common-util/functions/addresses';
import { isAddressProhibited } from 'libs/util-prohibited-data/src/index';

import { getChainId, getChainIdOrDefaultToMainnet } from 'common-util/functions/frontend-library';
import { setChainId, setUserBalance } from 'store/setup';

Expand Down
7 changes: 0 additions & 7 deletions apps/tokenomics/common-util/functions/addresses.js

This file was deleted.

1 change: 0 additions & 1 deletion apps/tokenomics/common-util/functions/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './addresses';
export * from './errors';
export * from './ethers';
export * from './time';
Expand Down
4 changes: 4 additions & 0 deletions apps/tokenomics/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config, middleware } from 'libs/common-middleware/src';

export default middleware;
export { config };
25 changes: 0 additions & 25 deletions apps/tokenomics/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,6 @@ const nextConfig = {
},
];
},
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: "frame-ancestors 'none';",
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains',
},
],
},
];
},
};
const plugins = [
// Add more Next.js plugins to this list if needed.
Expand Down
Loading

0 comments on commit 37375b3

Please sign in to comment.