Conversation
|
I've read through the entire diff carefully. Here's my review of PR #66: Security & Correctness Review: PR #66 — Add Risk Checker EndpointCritical: No AuthenticationThe most significant security issue is that the endpoint is completely unauthenticated. Every other mutating or data-serving route in the codebase uses This means anyone on the internet can:
At minimum, it should be behind No Server-Side Rate LimitingThe inter-call delay ( Rate Limit Delay Comment is WrongThe comment says "Add a 20% safety margin" but the actual calculation has no margin at all: With
|
| Priority | Issue |
|---|---|
| P0 | Add authentication middleware (jwt_auth or jwt_admin_auth) |
| P0 | Add server-side rate limiting or concurrency cap for this endpoint |
| P1 | Set HTTP client timeout |
| P1 | Fix is_smart_contract to return Result and use code != "0x" instead of len() > 100 |
| P2 | Fix rate-limit delay calculation to actually include the 20% margin |
| P2 | Unify reqwest versions or use the alloy-pulled version |
| P3 | Improve status "0" error discrimination in fetch_etherscan |
| P3 | Document ENS subdomain limitation |
|
I have addressed the issues. There is intentionally no auth protection because it's for public usage through website. I have added cors and rate limit for the endpoint. |
|
Good, I now have the full picture. Here's the follow-up review. Follow-up Review: PR #66 (after 6 additional commits)What Got Fixed (nice work!)
New Issue: CORS Change is Global and BreakingThis is the biggest concern with the updated PR. The CORS change replaces On The PR changes this to: With the default config being This means any existing client not at
|
| Issue | Impact |
|---|---|
Magic number code.len() > 100 |
A minimal proxy contract with < 100 chars of bytecode will be misclassified as an EOA. Should be code != "0x" or code.len() > 2. |
fetch_etherscan maps all status "0" to AddressNotFound |
An expired API key, invalid module, etc. will surface as "address not found" rather than a proper error. Fragile. |
| Inconsistent reqwest versions | Cargo.toml has reqwest = "0.11" while alloy pulls in 0.13.2. Two versions of reqwest in the binary. |
| ENS subdomain support | is_ens_name rejects pay.vitalik.eth — documented as v1 limitation, acceptable. |
Minor Nit
is_smart_contract can be simplified — the match arms are just a passthrough now:
// Current:
match self.fetch_etherscan(&[...]).await {
Ok(data) => Ok(data.result.as_str().map(|code| code.len() > 100).unwrap_or(false)),
Err(e) => Err(e),
}
// Simpler:
let data = self.fetch_etherscan(&[...]).await?;
Ok(data.result.as_str().map(|code| code.len() > 100).unwrap_or(false))Summary
| Priority | Issue |
|---|---|
| P0 | CORS change is global — breaks all existing endpoints for non-whitelisted origins. Split into separate PR or scope to risk-checker route only. |
| P1 | AllowHeaders::mirror_request() + credentials — explicitly list allowed headers instead. |
| P1 | max_concurrent_requests = 1 is too aggressive for a public endpoint — raise default or add per-IP limiting. |
| P2 | Fix code.len() > 100 to code != "0x" for contract detection. |
| P2 | Unify reqwest versions (0.11 vs 0.13). |
| P3 | Improve status "0" error discrimination in fetch_etherscan. |
The concurrency limiter and timeout additions are solid improvements. The main blocker is the global CORS change — it shouldn't ship as part of this PR without careful coordination.
| .await?; | ||
|
|
||
| Ok(data.result.as_str().map(|code| code.len() > 100).unwrap_or(false)) | ||
| } |
There was a problem hiding this comment.
this should not swallow errors, it should just propagate them
also != 0x might be a better check than code length
There was a problem hiding this comment.
reason is on error this literally returns a wrong result...
Summary