Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: security headers for apps #65

Merged
merged 24 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6138d54
feat: Add next-middleware library and related files
mohandast52 Jul 17, 2024
fffbd6c
feat: renames files for middleware
mohandast52 Jul 17, 2024
9c2cc7d
feat: remove unsafe-inline for style-src
mohandast52 Jul 17, 2024
110a1fc
chore: include object-src
mohandast52 Jul 18, 2024
4e9e5f0
chore: remove unsafe inline for firefox
mohandast52 Jul 18, 2024
1f3b301
chore: IMPORTANT - got A+ but changing the policy to make it work as …
mohandast52 Jul 18, 2024
833af20
chore: adding unsafe-inline for style-src
mohandast52 Jul 18, 2024
7c2e733
chore: remove font-src, object-src and scriptSrc to have unsafe-inlin…
mohandast52 Jul 18, 2024
21c6679
refractor: move constants above the function in cspHeader, remove bro…
mohandast52 Jul 18, 2024
3141d5b
chore: add 'nonce' support for style-src and commented unsafe-inline
mohandast52 Jul 18, 2024
19c4169
chore: add nonce
mohandast52 Jul 18, 2024
8255b0e
chore: remove nonce and add unsafe-inline for style
mohandast52 Jul 18, 2024
239bd74
chore: add api.thegraph.com origin
mohandast52 Jul 18, 2024
9fe74ed
feat: add middleware to other apps
mohandast52 Jul 18, 2024
5eefbe5
feat: remove headers from next.config.js for bond & tokenomics app
mohandast52 Jul 18, 2024
765c606
chore: rename getCspHeader function name
mohandast52 Jul 18, 2024
3f79d9a
Update libs/common-middleware/src/lib/cspHeader.ts
mohandast52 Jul 19, 2024
5978445
refactor: Remove duplicate code for address prohibition check
mohandast52 Jul 19, 2024
e34c720
Merge branch 'mohan/security-header' of github.com-personal:valory-xy…
mohandast52 Jul 19, 2024
44f7b3c
refactor: Remove duplicate code for address prohibition check
mohandast52 Jul 19, 2024
4c0d5eb
fix: conflict merge
mohandast52 Jul 22, 2024
9f4d6b7
feat: Add Vercel links to CSP allowed origins
mohandast52 Jul 24, 2024
2aa4f89
feat: Add gateway links to CSP allowed origins
mohandast52 Jul 24, 2024
174e78a
feat: Update IPFS gateway links in CSP allowed origins
mohandast52 Jul 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 Expand Up @@ -55,7 +56,7 @@
if (balance?.formatted) {
dispatch(setUserBalance(balance.formatted));
}
}, [balance?.formatted]);

Check warning on line 59 in apps/tokenomics/common-util/Login/LoginV2.jsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array

useEffect(() => {
// if chainId is undefined, the wallet is not connected & default to mainnet
Expand All @@ -72,7 +73,7 @@
const tempChainId = getChainIdOrDefaultToMainnet(chainId);
dispatch(setChainId(tempChainId));
}
}, [chainId]);

Check warning on line 76 in apps/tokenomics/common-util/Login/LoginV2.jsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array

useEffect(() => {
const getData = async () => {
Expand Down Expand Up @@ -130,7 +131,7 @@
notifyError(<CannotConnectAddressOfacError />);
if (onDisconnectCb) onDisconnectCb();
}
}, [address]);

Check warning on line 134 in apps/tokenomics/common-util/Login/LoginV2.jsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has missing dependencies: 'disconnect' and 'onDisconnectCb'. Either include them or remove the dependency array. If 'onDisconnectCb' changes too often, find the parent component that defines it and wrap that definition in useCallback

const screens = useBreakpoint();

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
Loading