Skip to content

page and components#1

Open
pennythewise wants to merge 29 commits into
monad-developers:mainfrom
wwaiyyee:penny
Open

page and components#1
pennythewise wants to merge 29 commits into
monad-developers:mainfrom
wwaiyyee:penny

Conversation

@pennythewise
Copy link
Copy Markdown

No description provided.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 16, 2026

Greptile Summary

This is the initial commit for Memetaverse, a Next.js dApp that lets users upload memes to IPFS via Pinata and register them on the Monad testnet through a Solidity MemeRegistry contract, with a 3D globe UI for exploring submissions.

  • API key leak: An Alchemy RPC API key is hardcoded as a fallback in pages/index.js and pages/explore.js and committed outright in scratch/check_memes.mjs — it will be visible in the browser bundle and in git history; the key must be rotated and supplied only through a server-side env variable or proxied API route.
  • State mutation in useMemo: onChainMemes.reverse() mutates the React state array in-place before spreading, which can cause the memo to silently stop recomputing and produces non-deterministic display order.
  • Upload API accepts any file type: The Pinata upload route performs no server-side MIME validation, relying solely on the browser accept hint.

Confidence Score: 2/5

Not safe to merge as-is — a live Alchemy API key is committed to the repository in three files and will be shipped in the browser bundle.

An Alchemy API key is hardcoded verbatim in both client-rendered pages and a committed debug script, exposing it to anyone who inspects the bundle or browses the repo. The key is now in git history regardless of future edits. Additionally, onChainMemes.reverse() mutates state inside useMemo, which can silently break meme ordering and memo recomputation.

pages/index.js, pages/explore.js, and scratch/check_memes.mjs all contain the hardcoded API key; pages/index.js also has the state-mutation bug in useMemo.

Security Review

  • Credential exposure (Alchemy API key): The key EuLqamhK_5ymLb8952SSc is hardcoded in pages/index.js, pages/explore.js, and scratch/check_memes.mjs. Because these are Next.js client-rendered pages, the key is shipped verbatim in the browser JavaScript bundle and visible to anyone who inspects network traffic or page source. The key should be rotated immediately in the Alchemy dashboard.
  • Signer/provider exposed as window globals (context/WalletContext.js lines 22–23): If this file is ever activated, window.signer gives any in-page script (XSS, malicious extension) the ability to sign arbitrary transactions without user consent.
  • No server-side file-type validation (pages/api/pinata/upload.js): The upload route accepts and forwards any MIME type to Pinata; the browser-side accept attribute is trivially bypassed.

Important Files Changed

Filename Overview
pages/index.js Main page with globe, search, and upload flow — contains a hardcoded Alchemy API key in the client bundle and directly mutates onChainMemes state inside useMemo via .reverse().
pages/explore.js Meme detail page that reads from on-chain or static data — same hardcoded Alchemy API key fallback as index.js, exposed in the browser bundle.
scratch/check_memes.mjs Ad-hoc debug script committed to the repo with the Alchemy API key and contract address hardcoded — should be removed before merging.
pages/api/pinata/upload.js Pinata upload API route — correctly gates on PINATA_JWT and handles temp-file cleanup, but lacks server-side MIME type validation allowing any file type to be uploaded.
context/WalletContext.js Custom wallet context not wired into _app.js (wagmi/RainbowKit is used instead) — dead code that also exposes the ethers provider and signer as window globals.
components/FileUploadCard.js Multi-file upload card with metadata form and animated progress — well-structured; simulated progress with setInterval is acceptable given fetch limitations.
meme-contract/contracts/MemeRegistry.sol Simple Solidity registry contract with create/read operations — no access control issues; getMeme correctly validates the ID range.
lib/wagmiConfig.js Wagmi/RainbowKit configuration — projectId now correctly falls back to env var before the placeholder string.
components/MemeDropZone.js Drop-zone upload component with proper blob URL cleanup via previewUrlRef and useEffect — previously flagged memory-leak and UX issues appear addressed in this version.

Comments Outside Diff (2)

  1. pages/index.js, line 783 (link)

    P0 security Alchemy API key hardcoded in client-side bundle

    The fallback RPC URL "https://monad-testnet.g.alchemy.com/v2/EuLqamhK_5ymLb8952SSc" embeds an Alchemy API key directly in the Next.js page component. Because pages/index.js is included in the browser bundle, anyone who opens DevTools can extract this key and use your Alchemy quota. The same key also appears verbatim in pages/explore.js and scratch/check_memes.mjs. Rotate the key immediately in the Alchemy dashboard, then supply it only via a server-side environment variable (never NEXT_PUBLIC_) and proxy RPC calls through an API route, or at minimum keep it exclusively in NEXT_PUBLIC_MONAD_RPC_URL so it's not also baked into the source.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pages/index.js
    Line: 783
    
    Comment:
    **Alchemy API key hardcoded in client-side bundle**
    
    The fallback RPC URL `"https://monad-testnet.g.alchemy.com/v2/EuLqamhK_5ymLb8952SSc"` embeds an Alchemy API key directly in the Next.js page component. Because `pages/index.js` is included in the browser bundle, anyone who opens DevTools can extract this key and use your Alchemy quota. The same key also appears verbatim in `pages/explore.js` and `scratch/check_memes.mjs`. Rotate the key immediately in the Alchemy dashboard, then supply it only via a server-side environment variable (never `NEXT_PUBLIC_`) and proxy RPC calls through an API route, or at minimum keep it exclusively in `NEXT_PUBLIC_MONAD_RPC_URL` so it's not also baked into the source.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. pages/index.js, line 674-676 (link)

    P1 onChainMemes.reverse() directly mutates React state

    Array.prototype.reverse() sorts the array in-place, so onChainMemes.reverse() mutates the state array before spreading it. React's useMemo uses a shallow reference comparison for its dependency; after the first call the reference is the same mutated array, so subsequent renders may not recompute the memo correctly and the order of on-chain memes in the UI can become non-deterministic. Copy the array before reversing to avoid the mutation.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pages/index.js
    Line: 674-676
    
    Comment:
    **`onChainMemes.reverse()` directly mutates React state**
    
    `Array.prototype.reverse()` sorts the array in-place, so `onChainMemes.reverse()` mutates the state array before spreading it. React's `useMemo` uses a shallow reference comparison for its dependency; after the first call the reference is the same mutated array, so subsequent renders may not recompute the memo correctly and the order of on-chain memes in the UI can become non-deterministic. Copy the array before reversing to avoid the mutation.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 6 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 6
pages/index.js:783
**Alchemy API key hardcoded in client-side bundle**

The fallback RPC URL `"https://monad-testnet.g.alchemy.com/v2/EuLqamhK_5ymLb8952SSc"` embeds an Alchemy API key directly in the Next.js page component. Because `pages/index.js` is included in the browser bundle, anyone who opens DevTools can extract this key and use your Alchemy quota. The same key also appears verbatim in `pages/explore.js` and `scratch/check_memes.mjs`. Rotate the key immediately in the Alchemy dashboard, then supply it only via a server-side environment variable (never `NEXT_PUBLIC_`) and proxy RPC calls through an API route, or at minimum keep it exclusively in `NEXT_PUBLIC_MONAD_RPC_URL` so it's not also baked into the source.

### Issue 2 of 6
pages/index.js:674-676
**`onChainMemes.reverse()` directly mutates React state**

`Array.prototype.reverse()` sorts the array in-place, so `onChainMemes.reverse()` mutates the state array before spreading it. React's `useMemo` uses a shallow reference comparison for its dependency; after the first call the reference is the same mutated array, so subsequent renders may not recompute the memo correctly and the order of on-chain memes in the UI can become non-deterministic. Copy the array before reversing to avoid the mutation.

```suggestion
  const allMemes = useMemo(() => {
    return [[...onChainMemes].reverse(), ...MEMES];
  }, [onChainMemes]);
```

### Issue 3 of 6
pages/explore.js:506
**Alchemy API key hardcoded in client-side page**

Same hardcoded fallback URL `"https://monad-testnet.g.alchemy.com/v2/EuLqamhK_5ymLb8952SSc"` as in `pages/index.js`. See the comment there for details and remediation steps.

### Issue 4 of 6
scratch/check_memes.mjs:9
**Debug script with hardcoded API key committed to repo**

This one-off helper embeds the same Alchemy API key at line 9 and commits a concrete contract address. Debug/scratch scripts like this should live in a `.gitignore`d directory or be removed before the PR is merged to prevent credential exposure through version history.

### Issue 5 of 6
context/WalletContext.js:22-23
**Provider and signer assigned to `window` globals**

`window.provider` and `window.signer` expose the ethers `BrowserProvider` and `Signer` objects as mutable globals, making them accessible to any third-party script running in the same page. Any XSS payload or malicious browser extension can now call `window.signer.signTransaction(...)` without user interaction. Note that `WalletContext` is not wired into `_app.js` (which uses wagmi/RainbowKit instead), so this file appears to be dead code — consider removing it entirely.

### Issue 6 of 6
pages/api/pinata/upload.js:24-30
**No server-side file-type validation**

The upload handler accepts any MIME type from `formidable` and forwards it to Pinata without checking the content type. A user can submit a `.exe`, `.php`, or any other non-media file with the content type spoofed. While Pinata itself is content-agnostic, the front-end `FileUploadCard` declares `accept="image/*,video/*"` — a trivially bypassed browser hint. Add a server-side allowlist check on `uploaded.mimetype` before reading the file buffer.

Reviews (5): Last reviewed commit: "readme" | Re-trigger Greptile

Comment thread components/MemeDropZone.js Outdated
Comment thread components/MemeSearch.js
Comment thread components/ConnectWallet.js Outdated
Comment thread pages/explore.js Outdated
Comment thread components/MemeDropZone.js Outdated
Comment thread pages/explore.js Outdated
Comment thread pages/explore.js Outdated
Comment thread components/MemeDropZone.js
Comment thread lib/wagmiConfig.js Outdated
Comment thread pages/explore.js Outdated
kowaiikok pushed a commit to kowaiikok/monaddict2 that referenced this pull request May 16, 2026
Comment thread scratch/check_memes.mjs
];

async function main() {
const provider = new ethers.JsonRpcProvider("https://monad-testnet.g.alchemy.com/v2/EuLqamhK_5ymLb8952SSc");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 security Debug script with hardcoded API key committed to repo

This one-off helper embeds the same Alchemy API key at line 9 and commits a concrete contract address. Debug/scratch scripts like this should live in a .gitignored directory or be removed before the PR is merged to prevent credential exposure through version history.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scratch/check_memes.mjs
Line: 9

Comment:
**Debug script with hardcoded API key committed to repo**

This one-off helper embeds the same Alchemy API key at line 9 and commits a concrete contract address. Debug/scratch scripts like this should live in a `.gitignore`d directory or be removed before the PR is merged to prevent credential exposure through version history.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread context/WalletContext.js
Comment on lines +22 to +23
// Expose provider and signer globally or simply know they are available when connected
window.provider = provider;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Provider and signer assigned to window globals

window.provider and window.signer expose the ethers BrowserProvider and Signer objects as mutable globals, making them accessible to any third-party script running in the same page. Any XSS payload or malicious browser extension can now call window.signer.signTransaction(...) without user interaction. Note that WalletContext is not wired into _app.js (which uses wagmi/RainbowKit instead), so this file appears to be dead code — consider removing it entirely.

Prompt To Fix With AI
This is a comment left during a code review.
Path: context/WalletContext.js
Line: 22-23

Comment:
**Provider and signer assigned to `window` globals**

`window.provider` and `window.signer` expose the ethers `BrowserProvider` and `Signer` objects as mutable globals, making them accessible to any third-party script running in the same page. Any XSS payload or malicious browser extension can now call `window.signer.signTransaction(...)` without user interaction. Note that `WalletContext` is not wired into `_app.js` (which uses wagmi/RainbowKit instead), so this file appears to be dead code — consider removing it entirely.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +24 to +30
}

// Extract metadata fields matching MemeRegistry contract
const title = fields.title?.[0] || '';
const description = fields.description?.[0] || '';
const country = fields.country?.[0] || '';
const category = fields.category?.[0] || '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 No server-side file-type validation

The upload handler accepts any MIME type from formidable and forwards it to Pinata without checking the content type. A user can submit a .exe, .php, or any other non-media file with the content type spoofed. While Pinata itself is content-agnostic, the front-end FileUploadCard declares accept="image/*,video/*" — a trivially bypassed browser hint. Add a server-side allowlist check on uploaded.mimetype before reading the file buffer.

Prompt To Fix With AI
This is a comment left during a code review.
Path: pages/api/pinata/upload.js
Line: 24-30

Comment:
**No server-side file-type validation**

The upload handler accepts any MIME type from `formidable` and forwards it to Pinata without checking the content type. A user can submit a `.exe`, `.php`, or any other non-media file with the content type spoofed. While Pinata itself is content-agnostic, the front-end `FileUploadCard` declares `accept="image/*,video/*"` — a trivially bypassed browser hint. Add a server-side allowlist check on `uploaded.mimetype` before reading the file buffer.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants