-
Notifications
You must be signed in to change notification settings - Fork 629
fix(ui): server-side request forgery api x-endpoint #2920
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,32 @@ import fetchFactory from 'nextjs/utils/fetchProxy'; | |
|
||
import appConfig from 'configs/app'; | ||
|
||
// Define an allow-list of permitted endpoints | ||
const ALLOWED_ENDPOINTS = [ | ||
appConfig.apis.general.endpoint, | ||
// Add other allowed endpoints here, e.g.: | ||
// "https://api.example.com", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This resource is not used in production, so the changes are not necessary. If we want to keep them, please add all available API endpoints to the list. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And I don't see how this resource can be requested using the endpoint from user input. |
||
]; | ||
|
||
const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { | ||
if (!nextReq.url) { | ||
nextRes.status(500).json({ error: 'no url provided' }); | ||
return; | ||
} | ||
|
||
// Validate x-endpoint header against allow-list | ||
let baseEndpoint = appConfig.apis.general.endpoint; | ||
const requestedEndpoint = nextReq.headers['x-endpoint']?.toString(); | ||
if (requestedEndpoint && ALLOWED_ENDPOINTS.includes(requestedEndpoint)) { | ||
baseEndpoint = requestedEndpoint; | ||
} else if (requestedEndpoint) { | ||
nextRes.status(400).json({ error: 'Invalid endpoint' }); | ||
return; | ||
} | ||
|
||
const url = new URL( | ||
nextReq.url.replace(/^\/node-api\/proxy/, ''), | ||
nextReq.headers['x-endpoint']?.toString() || appConfig.apis.general.endpoint, | ||
baseEndpoint, | ||
); | ||
const apiRes = await fetchFactory(nextReq)( | ||
url.toString(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,12 +25,18 @@ const AddressSaveOnGas = ({ gasUsed, address }: Props) => { | |
|
||
const gasUsedNumber = Number(gasUsed); | ||
|
||
// Simple Ethereum address validation (42 chars, starts with 0x, hex) | ||
const isValidAddress = /^0x[a-fA-F0-9]{40}$/.test(address); | ||
|
||
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that if the address is invalid, the page will throw a 422 error, so this code will never execute. |
||
const query = useQuery({ | ||
queryKey: [ 'gas_hawk_saving_potential', { address } ], | ||
queryFn: async() => { | ||
if (!feature.isEnabled) { | ||
return; | ||
} | ||
if (!isValidAddress) { | ||
throw new Error('Invalid address format'); | ||
} | ||
|
||
const response = await fetch(feature.apiUrlTemplate.replace('<address>', address)); | ||
const data = await response.json(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main characteristic of this resource is that we do not know the domain of the server that serves the asset in advance. Therefore, we cannot determine the allowed hostname array at all.