Conversation
WalkthroughAdds interactive external-media support (IPFS/Arweave) for meme submissions: new media/security constants and validator, hook and mutation updates, UI for Upload vs Interactive HTML with provider/mime controls and sandboxed iframe previews, media-display integration, many tests, and CSP header adjustments. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant UI as MemesArtSubmissionFile
participant Form as useArtworkSubmissionForm
participant Validator as validateInteractivePreview
participant Container as MemesArtSubmissionContainer
participant Mutation as useArtworkSubmissionMutation
User->>UI: Switch tab (Upload / Interactive)
UI->>Form: setMediaSource(mode)
alt Upload
User->>UI: Select file
UI->>Form: handleFileSelect(file)
Form-->>UI: artworkUrl / preview
else Interactive
User->>UI: Enter hash/provider/mime
UI->>Form: setExternalMediaHash/provider/mime
Form->>Validator: validateInteractivePreview(provider, path)
Validator-->>Form: {ok, finalUrl, contentType} or {ok:false, reason}
Form-->>UI: externalValidationStatus / previewUrl
UI->>UI: Render SandboxedExternalIframe when valid
end
User->>Container: Submit
Container->>Form: getSubmissionData()
alt file present
Container->>Mutation: submitArtwork({imageFile, ...})
else external media valid
Container->>Mutation: submitArtwork({externalMedia:{url,mimeType}, ...})
end
Mutation-->>Container: success / error
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used📓 Path-based instructions (4)**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursorrules)
Files:
**/*.tsx📄 CodeRabbit inference engine (.cursorrules)
Files:
__tests__/**📄 CodeRabbit inference engine (tests/AGENTS.md)
Files:
__tests__/components/**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (tests/AGENTS.md)
Files:
🧠 Learnings (12)📚 Learning: 2025-09-28T12:29:11.651ZApplied to files:
📚 Learning: 2025-09-28T12:33:56.329ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:30.950ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
📚 Learning: 2025-09-28T12:33:07.561ZApplied to files:
🧬 Code graph analysis (3)components/common/SandboxedExternalIframe.tsx (1)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx (3)
components/waves/memes/submission/constants/security.ts (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx (1)
265-275: Include the new external-media props in every render.
MemesArtSubmissionFilePropsnow requires the external-media fields. The renders in this block (and the other similar ones below) still instantiate<MemesArtSubmissionFile>without those props, so the test file no longer type-checks and the suite won’t compile. Please spreadbaseProps(or go throughrenderComponent) and override only the fields each test needs.- <MemesArtSubmissionFile - artworkUploaded={false} - artworkUrl="url" - setArtworkUploaded={mockSetArtworkUploaded} - handleFileSelect={mockHandleFileSelect} - /> + <MemesArtSubmissionFile + {...baseProps} + artworkUploaded={false} + artworkUrl="url" + />Apply the same fix to the other occurrences so every render continues to satisfy
MemesArtSubmissionFileProps.
🧹 Nitpick comments (1)
components/waves/memes/submission/constants/media.ts (1)
1-8: Remove inline comments to satisfy TS/TSX guidelines.The coding guidelines for this project call out “Do not include any comments in the code” for
*.ts/*.tsxfiles. Please drop the header comment (and any other newly added comments) so the file adheres to that rule. As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx(5 hunks)__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx(3 hunks)__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx(5 hunks)components/waves/memes/MemesArtSubmissionFile.tsx(7 hunks)components/waves/memes/file-upload/reducers/types.ts(3 hunks)components/waves/memes/submission/MemesArtSubmissionContainer.tsx(4 hunks)components/waves/memes/submission/constants/media.ts(1 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionMutation.ts(3 hunks)components/waves/memes/submission/steps/ArtworkStep.tsx(4 hunks)next.config.mjs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsxcomponents/waves/memes/submission/hooks/useArtworkSubmissionMutation.ts__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsxcomponents/waves/memes/submission/constants/media.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsxcomponents/waves/memes/MemesArtSubmissionFile.tsxcomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.tscomponents/waves/memes/file-upload/reducers/types.tscomponents/waves/memes/submission/steps/ArtworkStep.tsxcomponents/waves/memes/submission/MemesArtSubmissionContainer.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsxcomponents/waves/memes/MemesArtSubmissionFile.tsxcomponents/waves/memes/submission/steps/ArtworkStep.tsxcomponents/waves/memes/submission/MemesArtSubmissionContainer.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
🧠 Learnings (10)
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Applies to __tests__/components/**/*.{ts,tsx,js,jsx} : Use `testing-library/react` and `testing-library/user-event` for React component tests
Applied to files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios
Applied to files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Document non-obvious expected behaviour directly in the mock file
Applied to files:
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Mock only external dependencies or heavy functionality; avoid over-mocking internal logic
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mocks up to date with the real implementations they represent
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Review mocks periodically and remove unused mock modules
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: All changes must pass npm run test, npm run lint, and npm run type-check (or test:cov:changed when appropriate)
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/*.{test,spec}.{js,jsx,ts,tsx} : In tests, use jest.mock('module') with a bare module specifier to load the corresponding manual mock
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Organise mocks to mirror the real module structure so import paths remain consistent
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:29:11.651Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.651Z
Learning: Applies to **/*.tsx : Use FontAwesome for icons
Applied to files:
components/waves/memes/submission/MemesArtSubmissionContainer.tsx
🧬 Code graph analysis (8)
__tests__/components/waves/memes/submission/steps/ArtworkStep.test.tsx (1)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)
components/waves/memes/submission/hooks/useArtworkSubmissionMutation.ts (2)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)generated/models/ApiDropMedia.ts (1)
ApiDropMedia(15-41)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx (2)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)components/waves/memes/file-upload/reducers/types.ts (1)
MemesArtSubmissionFileProps(71-110)
__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx (1)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)
components/waves/memes/MemesArtSubmissionFile.tsx (2)
components/waves/memes/submission/constants/media.ts (4)
DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)INTERACTIVE_MEDIA_PROVIDERS(26-29)InteractiveMediaProvider(31-32)ALLOWED_INTERACTIVE_MEDIA_MIME_TYPES(2-8)components/common/TabToggle.tsx (1)
TabToggle(17-53)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (3)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)
components/waves/memes/file-upload/reducers/types.ts (1)
components/waves/memes/submission/constants/media.ts (2)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)
components/waves/memes/submission/steps/ArtworkStep.tsx (1)
components/waves/memes/submission/constants/media.ts (2)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/waves/memes/file-upload/reducers/types.ts (1)
1-190: Remove comments per coding guidelines.The file contains extensive comments throughout (lines 6-190), but the coding guidelines specify: "Do not include any comments in the code" for TypeScript files.
Apply this pattern to remove comments from the file:
-/** - * Type definitions for the file upload system - * - * This file contains all types and interfaces used throughout the file upload components. - */ - -/** - * Result of file validation check - */ export interface FileValidationResult {Continue this pattern for all remaining comments in the file.
♻️ Duplicate comments (1)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (1)
186-206: Persist the uploaded artwork URL when switching tabsWe still overwrite the uploaded file URL when changing the media source. After selecting a file, flipping to “Interactive HTML” sets
artworkUrlto the external URL (or empty string); coming back to “Upload File” leavesartworkUrlpointing at that external value even thoughselectedFileis still present.getSubmissionData()then submits the wrong payload. We need to store the upload URL separately and restore it when returning to the upload tab—this is the same regression that was flagged earlier.A minimal fix follows the earlier guidance:
interface FormState { ... artworkUrl: string; + uploadArtworkUrl: string; mediaSource: MediaSource; selectedFile: File | null; ... } const initialState: FormState = { ... artworkUrl: "", + uploadArtworkUrl: "", ... }; case "SET_MEDIA_SOURCE": { const nextSource = action.payload; if (nextSource === "upload") { const hasFile = state.selectedFile !== null; return { ...state, mediaSource: nextSource, artworkUploaded: hasFile, - artworkUrl: hasFile ? state.artworkUrl : "", + artworkUrl: hasFile ? state.uploadArtworkUrl : "", }; } ... } case "SET_UPLOAD_MEDIA": return { ...state, selectedFile: action.payload.file, artworkUrl: action.payload.artworkUrl, + uploadArtworkUrl: action.payload.artworkUrl, artworkUploaded: true, mediaSource: "upload", }; case "RESET_UPLOAD_MEDIA": return { ...state, selectedFile: null, + uploadArtworkUrl: "", artworkUrl: shouldFallbackToExternal ? state.externalMedia.url : "", artworkUploaded: shouldFallbackToExternal, };Please reintroduce the dedicated upload URL field (or an equivalent approach) so toggling tabs no longer corrupts the upload flow.
🧹 Nitpick comments (2)
components/common/SandboxedExternalIframe.tsx (2)
9-9: Document sandbox policy and consider security implications.The default sandbox permissions are quite permissive:
allow-scripts: Enables JavaScript (necessary for interactive content but high-risk)allow-pointer-lock: Enables full-screen pointer captureallow-popups: Allows opening new windows/tabsWhile these permissions may be necessary for rich interactive artworks, they create potential security and UX concerns:
- Scripts can be malicious or resource-intensive
- Pointer lock can create confusing UX
- Popups can be used for phishing or annoyance
Consider adding a comment documenting the security model:
+// Sandbox policy for external interactive media: +// - allow-scripts: Required for interactive HTML content +// - allow-pointer-lock: Enables immersive 3D/game experiences +// - allow-popups: Allows legitimate multi-window applications +// - Notably EXCLUDES allow-same-origin (critical for isolation) const DEFAULT_SANDBOX = "allow-scripts allow-pointer-lock allow-popups";Additionally, consider whether all three permissions are truly needed for the use case, or if a more restrictive default with opt-in for special cases would be safer.
60-86: LGTM with minor security enhancements to consider.The component correctly validates URLs and extensions before rendering, with fail-safe defaults. The iframe attributes include good security and performance practices (
referrerPolicy="no-referrer",loading="lazy").Consider adding the
allowattribute for fine-grained feature control if certain capabilities (like autoplay, camera, microphone) should be blocked:<iframe src={src} title={title} className={className} sandbox={sandbox} + allow="accelerometer; gyroscope" referrerPolicy="no-referrer" loading="lazy" />This is optional and depends on your security requirements for interactive artworks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx(6 hunks)__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx(3 hunks)__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts(1 hunks)__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts(3 hunks)components/common/SandboxedExternalIframe.tsx(1 hunks)components/drops/view/item/content/media/DropListItemContentMedia.tsx(3 hunks)components/drops/view/item/content/media/MediaDisplay.tsx(3 hunks)components/waves/memes/MemesArtSubmissionFile.tsx(7 hunks)components/waves/memes/file-upload/reducers/types.ts(3 hunks)components/waves/memes/submission/MemesArtSubmissionContainer.tsx(4 hunks)components/waves/memes/submission/actions/validateInteractivePreview.ts(1 hunks)components/waves/memes/submission/constants/security.ts(1 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)components/waves/memes/submission/steps/ArtworkStep.tsx(4 hunks)next.config.mjs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/waves/memes/submission/steps/ArtworkStep.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
components/common/SandboxedExternalIframe.tsxcomponents/waves/memes/submission/actions/validateInteractivePreview.tscomponents/drops/view/item/content/media/DropListItemContentMedia.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsxcomponents/waves/memes/submission/constants/security.ts__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.tscomponents/waves/memes/MemesArtSubmissionFile.tsx__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsxcomponents/waves/memes/submission/MemesArtSubmissionContainer.tsxcomponents/drops/view/item/content/media/MediaDisplay.tsxcomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.tscomponents/waves/memes/file-upload/reducers/types.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/common/SandboxedExternalIframe.tsxcomponents/drops/view/item/content/media/DropListItemContentMedia.tsx__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsxcomponents/waves/memes/MemesArtSubmissionFile.tsx__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsxcomponents/waves/memes/submission/MemesArtSubmissionContainer.tsxcomponents/drops/view/item/content/media/MediaDisplay.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
🧠 Learnings (9)
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Document non-obvious expected behaviour directly in the mock file
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Applies to __tests__/components/**/*.{ts,tsx,js,jsx} : Use `testing-library/react` and `testing-library/user-event` for React component tests
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Mock only external dependencies or heavy functionality; avoid over-mocking internal logic
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mocks up to date with the real implementations they represent
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/*.{test,spec}.{js,jsx,ts,tsx} : In tests, use jest.mock('module') with a bare module specifier to load the corresponding manual mock
Applied to files:
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Review mocks periodically and remove unused mock modules
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Organise mocks to mirror the real module structure so import paths remain consistent
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx
📚 Learning: 2025-09-28T12:29:11.651Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.651Z
Learning: Applies to **/*.tsx : Use FontAwesome for icons
Applied to files:
components/waves/memes/submission/MemesArtSubmissionContainer.tsx
🧬 Code graph analysis (11)
components/common/SandboxedExternalIframe.tsx (1)
components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)isInteractiveMediaAllowedHost(12-21)
components/waves/memes/submission/actions/validateInteractivePreview.ts (2)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaProvider(31-32)components/waves/memes/submission/constants/security.ts (4)
INTERACTIVE_MEDIA_ALLOWED_CONTENT_TYPES(37-40)isInteractiveMediaAllowedHost(12-21)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)InteractiveMediaValidationResult(42-47)
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx (3)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)components/waves/memes/file-upload/reducers/types.ts (1)
MemesArtSubmissionFileProps(71-112)components/auth/Auth.tsx (1)
AuthContext(83-93)
components/waves/memes/submission/constants/security.ts (1)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaProvider(31-32)
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (2)
components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(66-144)
components/waves/memes/MemesArtSubmissionFile.tsx (3)
components/waves/memes/submission/constants/media.ts (4)
DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)INTERACTIVE_MEDIA_PROVIDERS(26-29)InteractiveMediaProvider(31-32)ALLOWED_INTERACTIVE_MEDIA_MIME_TYPES(2-8)components/common/TabToggle.tsx (1)
TabToggle(17-53)components/waves/memes/file-upload/utils/constants.ts (1)
FILE_INPUT_ACCEPT(35-35)
__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts (1)
components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(66-144)
__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx (1)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaMimeType(13-14)
components/drops/view/item/content/media/MediaDisplay.tsx (1)
components/drops/view/item/content/media/MediaDisplayGLB.tsx (1)
MediaDisplayGLB(14-178)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (5)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(66-144)
components/waves/memes/file-upload/reducers/types.ts (1)
components/waves/memes/submission/constants/media.ts (2)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
components/drops/view/item/content/media/DropListItemContentMedia.tsx (1)
55-57: LGTM!The HTML media type detection follows the same pattern as other media types in the function and correctly identifies
text/htmlMIME type.components/waves/memes/submission/MemesArtSubmissionContainer.tsx (3)
71-94: LGTM!The upload path correctly validates the presence of a selected file and submits with appropriate parameters including the phase change callback.
96-116: LGTM!The external media path properly validates the external media state before submission and constructs the appropriate payload with url and mimeType.
118-123: LGTM!The fileInfo derivation correctly extracts name and size from the selected file when present.
__tests__/components/waves/memes/MemesArtSubmissionFile.test.tsx (3)
164-188: LGTM!The introduction of
basePropsand therenderComponenthelper function significantly improves test maintainability and reduces duplication. This is a solid testing pattern.
461-498: LGTM!The security tests effectively validate that sandboxed iframes render correctly for approved IPFS and Arweave domains, including subdomain variants. The checks for sandbox attributes and correct src values are essential for security.
500-536: LGTM!The negative security tests are crucial and properly validate that:
- Unapproved domains are blocked
- Unsafe file extensions are blocked even from approved hosts
- Validation state is handled correctly before rendering
These tests provide essential coverage for the security model.
components/common/SandboxedExternalIframe.tsx (2)
19-37: Review extensionless URL handling for security implications.The function returns
true(allows) for URLs without extensions (lines 31-32) and for paths without a last segment (lines 27-29). This permissive behavior could allow non-HTML content to be loaded in the iframe.While this might be intentional for IPFS/Arweave directories that serve
index.htmlby default, it weakens the extension-based security filtering.Consider whether this permissive behavior is necessary. If not, apply this diff to be more restrictive:
const hasAllowedHtmlExtension = (url: URL): boolean => { const path = url.pathname ?? ""; const segments = path.split("/").filter(Boolean); if (segments.some((segment) => segment === "..")) { return false; } const lastSegment = segments[segments.length - 1] ?? ""; if (!lastSegment) { - return true; + return false; } if (!lastSegment.includes(".")) { - return true; + return false; } const extension = lastSegment.slice(lastSegment.lastIndexOf(".") + 1).toLowerCase(); return INTERACTIVE_MEDIA_HTML_EXTENSIONS.has(extension); };Alternatively, if serving directory indexes is required, document this explicitly as a design decision and ensure the server-side validation in
validateInteractivePreviewenforces stricter Content-Type checks.
46-48: Blocking search and hash parameters may be overly restrictive.The code rejects URLs with query strings (
?...) or hash fragments (#...). While this may be intended as a security measure, it could break legitimate use cases:
- Many single-page applications use hash-based routing (e.g.,
#/gallery)- Query parameters might be legitimately used by interactive artworks
- This restriction is not mentioned in the security documentation
Please verify whether this restriction is intentional and necessary. If the goal is to prevent parameter injection attacks, consider:
- Documenting this restriction in the security constants
- Allowing hash fragments since they don't reach the server:
if (parsedUrl.search) { return null; }- Server-side validation should be the primary defense against malicious parameters
If these must be blocked, document the rationale in a comment:
+ // Block search/hash to prevent parameter injection and ensure content determinism if (parsedUrl.search || parsedUrl.hash) { return null; }__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx (2)
70-109: LGTM!The mock implementation of external media handlers correctly simulates the behavior of the real form hook, including:
- Provider-specific URL construction (IPFS vs Arweave)
- Validation state management
- Dependency handling (provider change triggers URL reconstruction)
This comprehensive mock enables meaningful integration testing without over-mocking implementation details.
111-120: LGTM!The
getMediaSelectionhelper correctly consolidates all media-related state into the structure expected by the submission logic, maintaining consistency with the actual implementation.components/waves/memes/file-upload/reducers/types.ts (1)
80-111: LGTM: External media properties are well-typed.The new external media properties follow proper TypeScript conventions with readonly modifiers and use the correct imported types from the media constants module.
components/waves/memes/submission/constants/security.ts (3)
42-47: LGTM: Validation result interface is well-defined.The interface properly uses readonly properties and provides appropriate fields for validation results.
12-21: Verify intent behind permissive arweave.net subdomain matching.Most arweave.net subdomains are auto-generated from transaction IDs and not user-assignable, and named subdomains require ArNS registration. The assumption that "arbitrary parties" can register arweave.net subdomains appears incorrect.
However, the code accepts all arweave.net subdomains while restricting ipfs.io to exact matches. Confirm whether this asymmetry is intentional—e.g., to support ArNS-registered names and transaction-based subdomains—or if the suffix matching should be narrower.
29-35: Gateway URLs are current and official—no changes needed.The hardcoded URLs match the official gateways: IPFS's https://ipfs.io/ (with /ipfs/ path) is maintained by Protocol Labs, and Arweave's https://arweave.net/ is the primary official gateway. Both are secure and operational as of November 2025.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
134-139: Reconsider including 400 Bad Request in HEAD→GET fallback.Status 400 indicates a malformed request from the client's perspective. Retrying with GET is unlikely to succeed if the server returned 400 for HEAD. Consider removing it from the fallback trigger, keeping only 405, 403, and 501, which are method-specific errors.
Apply this diff:
if ( response.status === 405 || response.status === 403 || - response.status === 400 || response.status === 501 ) {components/waves/memes/MemesArtSubmissionFile.tsx (1)
229-237: Consider removing unnecessary memoization.
renderPreviewMessageis a simple two-parameter function with no closures. The memoization overhead likely exceeds any benefit. Consider defining it as a plain function outside the component.Apply this diff:
+const renderPreviewMessage = (primary: string, secondary: string) => ( + <div className="tw-h-full tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-3 tw-text-center tw-text-sm tw-text-iron-400 tw-px-4"> + <span>{primary}</span> + <span className="tw-text-iron-500">{secondary}</span> + </div> +); + const MemesArtSubmissionFile: React.FC<MemesArtSubmissionFileProps> = ({ ... - const renderPreviewMessage = useCallback( - (primary: string, secondary: string) => ( - <div className="tw-h-full tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-3 tw-text-center tw-text-sm tw-text-iron-400 tw-px-4"> - <span>{primary}</span> - <span className="tw-text-iron-500">{secondary}</span> - </div> - ), - [], - );components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (1)
136-142: Simplify query/fragment removal logic.The current approach using
Math.minis correct but verbose. Consider using a more idiomatic approach.Apply this diff:
- const queryIndex = sanitizedHash.indexOf("?"); - const fragmentIndex = sanitizedHash.indexOf("#"); - const cutIndex = Math.min( - queryIndex === -1 ? sanitizedHash.length : queryIndex, - fragmentIndex === -1 ? sanitizedHash.length : fragmentIndex - ); - const hashWithoutQuery = sanitizedHash.slice(0, cutIndex); + const hashWithoutQuery = sanitizedHash.split(/[?#]/)[0] ?? sanitizedHash;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
components/common/SandboxedExternalIframe.tsx(1 hunks)components/waves/memes/MemesArtSubmissionFile.tsx(7 hunks)components/waves/memes/submission/actions/validateInteractivePreview.ts(1 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/common/SandboxedExternalIframe.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
components/waves/memes/submission/actions/validateInteractivePreview.tscomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.tscomponents/waves/memes/MemesArtSubmissionFile.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/waves/memes/MemesArtSubmissionFile.tsx
🧬 Code graph analysis (3)
components/waves/memes/submission/actions/validateInteractivePreview.ts (2)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaProvider(31-32)components/waves/memes/submission/constants/security.ts (4)
INTERACTIVE_MEDIA_ALLOWED_CONTENT_TYPES(37-40)isInteractiveMediaAllowedHost(12-21)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)InteractiveMediaValidationResult(42-47)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (4)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(98-200)
components/waves/memes/MemesArtSubmissionFile.tsx (3)
components/waves/memes/submission/constants/media.ts (3)
DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)INTERACTIVE_MEDIA_PROVIDERS(26-29)ALLOWED_INTERACTIVE_MEDIA_MIME_TYPES(2-8)components/common/TabToggle.tsx (1)
TabToggle(17-53)components/waves/memes/file-upload/utils/constants.ts (1)
FILE_INPUT_ACCEPT(35-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (2)
411-496: LGTM! Solid validation with race condition prevention.The validation effect properly handles:
- Deduplication via
validationRequestKeyRef- Cancellation to prevent stale updates
- Error handling with detailed logging
The omission of
dispatchfrom the dependency array is correct since it's stable fromuseReducer.
225-245: ****The concern is based on incorrect assumptions about submission logic. Upload submissions use
media.selectedFile(the File object fromgetMediaSelection), notartworkUrl. TheSET_MEDIA_SOURCEcase does not modifyselectedFile, so it persists across tab switches. When switching from upload to URL and back,selectedFileremains intact and the submission proceeds correctly using the original file object. TheartworkUrlfield is used only for display purposes and does not affect the actual submission of uploaded files.Likely an incorrect or invalid review comment.
components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
21-50: Remove the path traversal encoding concern—URL decoding does not occur in this flow.The
isSafeRelativePathvalidation is sufficient for the actual data flow. The path parameter originates from a form field (viasanitizeInteractiveHash), which only strips protocol prefixes—it never URL-decodes input. IPFS hashes and Arweave IDs are alphanumeric identifiers that don't use URL encoding. The concern about encoded sequences like%2e%2ebypassing the..check is not applicable here since no URL decoding happens upstream.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts(1 hunks)__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts(3 hunks)components/common/SandboxedExternalIframe.tsx(1 hunks)components/waves/memes/MemesArtSubmissionFile.tsx(7 hunks)components/waves/memes/submission/actions/validateInteractivePreview.ts(1 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- components/waves/memes/submission/actions/validateInteractivePreview.ts
- tests/components/waves/memes/submission/actions/validateInteractivePreview.test.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.tscomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.tscomponents/waves/memes/MemesArtSubmissionFile.tsxcomponents/common/SandboxedExternalIframe.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/waves/memes/MemesArtSubmissionFile.tsxcomponents/common/SandboxedExternalIframe.tsx
🧠 Learnings (7)
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Document non-obvious expected behaviour directly in the mock file
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Mock only external dependencies or heavy functionality; avoid over-mocking internal logic
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Review mocks periodically and remove unused mock modules
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Organise mocks to mirror the real module structure so import paths remain consistent
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mocks up to date with the real implementations they represent
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Applies to __tests__/components/**/*.{ts,tsx,js,jsx} : Use `testing-library/react` and `testing-library/user-event` for React component tests
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
🧬 Code graph analysis (4)
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (3)
components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(106-215)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (1)
useArtworkSubmissionForm(313-602)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (5)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(106-215)
components/waves/memes/MemesArtSubmissionFile.tsx (3)
components/waves/memes/submission/constants/media.ts (3)
DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)INTERACTIVE_MEDIA_PROVIDERS(26-29)ALLOWED_INTERACTIVE_MEDIA_MIME_TYPES(2-8)components/common/TabToggle.tsx (1)
TabToggle(17-53)components/waves/memes/file-upload/utils/constants.ts (1)
FILE_INPUT_ACCEPT(35-35)
components/common/SandboxedExternalIframe.tsx (1)
components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)isInteractiveMediaAllowedHost(12-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (26)
components/common/SandboxedExternalIframe.tsx (4)
9-13: LGTM! Strong security posture for untrusted content.The sandbox policy appropriately restricts external interactive media by allowing only scripts and pointer-lock while blocking same-origin access and popups.
15-21: LGTM! Props follow coding guidelines.The interface correctly uses
readonlyfor all props as required by the coding guidelines.
50-52: Verify rejection of URL hash fragments is intentional.Hash fragments (
#section) are client-side navigation aids that don't affect server requests or security. Rejecting them (line 50-52) may unnecessarily limit legitimate use cases, such as linking to specific sections within an interactive HTML document.If hash fragments should be allowed, update the validation:
const getAllowedUrl = (src: string): URL | null => { try { const parsedUrl = new URL(src); if (parsedUrl.protocol !== "https:") { return null; } - if (parsedUrl.search || parsedUrl.hash) { + if (parsedUrl.search) { return null; } if (!isInteractiveMediaAllowedHost(parsedUrl.hostname)) { return null; } return parsedUrl; } catch { return null; } };
64-91: Strong security implementation.The iframe configuration properly isolates external content with sandbox restrictions, feature policy denial (
allow=""), no-referrer policy, and lazy loading.components/waves/memes/MemesArtSubmissionFile.tsx (11)
34-39: LGTM! Clean utility function.
50-69: LGTM! Comprehensive external media props.The expanded prop surface appropriately handles both upload and external media flows.
102-138: LGTM! Upload handlers properly gated to upload mode.All file upload interactions correctly check
mediaSource === "upload", preventing conflicts with external media mode.
140-166: LGTM! Tab switching logic is correct.The handler appropriately sets the default MIME type when switching to interactive HTML mode.
168-189: LGTM! Provider switching with proper type guards.The handler correctly validates the provider key before dispatching changes.
215-232: LGTM! Proper blob URL lifecycle management.The effect correctly creates and cleans up blob URLs, satisfying the CSP requirement for
blob:instead ofdata:URIs.
234-262: LGTM! Well-memoized computed values.The preview fallback messages and media type label are appropriately cached with correct dependencies.
264-273: LGTM! Clean tab toggle integration.
275-342: LGTM! Upload panel with strong accessibility.The panel correctly handles drag-and-drop, keyboard navigation, and ARIA attributes.
343-429: LGTM! Comprehensive interactive media panel.The panel correctly handles provider selection, hash input validation, and sandboxed preview with appropriate fallback states.
435-435: LGTM! Appropriate use of React.memo.Memoization is beneficial given the component's complex prop surface and rendering logic.
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (5)
1-28: LGTM! Proper test setup with async utilities.The mock configuration appropriately defaults to successful validation, allowing individual tests to override as needed.
Based on learnings.
51-62: LGTM! Validates client-side extension filtering.The test correctly verifies that non-HTML extensions are rejected before triggering server validation.
64-81: LGTM! Validates async server validation flow.The test correctly uses
waitForto handle the async validation lifecycle and verifies both the server call and resulting state.
83-101: LGTM! Validates error propagation from server.The test correctly verifies that gateway validation failures surface appropriate error messages.
103-139: LGTM! Validates uploaded artwork URL preservation.This test confirms the fix from the previous review comment - uploaded artwork URLs are correctly preserved and restored when switching between tabs.
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (6)
1-67: LGTM! Well-structured type definitions.The type system comprehensively models both upload and external media flows, including the
uploadArtworkUrlfield that preserves uploaded URLs during tab switching.
69-118: LGTM! Robust input sanitization and validation.The functions effectively guard against path traversal, protocol injection, and other malicious inputs.
120-192: LGTM! Comprehensive state builder with layered validation.The function correctly performs client-side checks for common issues while deferring Content-Type validation to the server, ensuring a robust validation flow.
194-311: LGTM! Reducer correctly preserves upload state.All cases properly manage the
uploadArtworkUrlfield, ensuring uploaded artwork URLs persist across tab switches.
313-407: LGTM! Well-structured hook initialization.The callbacks are properly memoized and the dual URL strategy (data URLs for storage, blob URLs for preview) correctly handles CSP requirements.
409-602: LGTM! Robust async validation with proper cleanup.The validation effect correctly handles race conditions through cancellation flags and deduplication keys, while the comprehensive return API supports both upload and external media workflows.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/common/SandboxedExternalIframe.tsx (1)
6-9: Remove inline comments per coding guidelines.The coding guidelines specify no comments in
*.{ts,tsx}files. These security policy comments should be moved to external documentation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
components/common/SandboxedExternalIframe.tsx(1 hunks)components/waves/memes/submission/actions/validateInteractivePreview.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/waves/memes/submission/actions/validateInteractivePreview.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
components/common/SandboxedExternalIframe.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/common/SandboxedExternalIframe.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-28T12:29:11.651Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.651Z
Learning: Applies to **/*.{ts,tsx} : Do not include any comments in the code
Applied to files:
components/common/SandboxedExternalIframe.tsx
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: Applies to app/api/**/*.{ts,tsx,js,jsx} : Never call fetch directly with user-controlled or scraped URLs in API code; use @/lib/security/urlGuard helpers (parsePublicUrl, assertPublicUrl, fetchPublicUrl, fetchPublicJson) so every hop is validated
Applied to files:
components/common/SandboxedExternalIframe.tsx
🧬 Code graph analysis (1)
components/common/SandboxedExternalIframe.tsx (1)
components/waves/memes/submission/constants/security.ts (1)
isInteractiveMediaAllowedHost(12-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
components/common/SandboxedExternalIframe.tsx (2)
41-60: LGTM: Solid security-focused URL validation.The function correctly enforces HTTPS-only, rejects search/hash fragments to prevent injection, validates hostnames against the allowlist, and handles parse errors gracefully.
62-89: LGTM: Well-secured iframe component with defense-in-depth.The component properly follows the
readonlyprops guideline and implements multiple security layers: URL validation, sandboxing withoutallow-same-origin/allow-popups, emptyallow=""to block all feature permissions, andno-referrerpolicy. The lazy loading is a good performance touch.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/waves/memes/MemesArtSubmissionFile.tsx (1)
40-48: Strip block comment per TSX guidelinesPlease remove this JSDoc block; the repo forbids comments inside *.ts/tsx files, so document this context externally if needed. As per coding guidelines.
♻️ Duplicate comments (1)
components/common/SandboxedExternalIframe.tsx (1)
6-9: Remove inline sandbox policy commentsThe repo rule forbids comments in *.ts/tsx files; please move this guidance to external documentation instead of inline. As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
components/common/SandboxedExternalIframe.tsx(1 hunks)components/waves/memes/MemesArtSubmissionFile.tsx(7 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
components/common/SandboxedExternalIframe.tsxcomponents/waves/memes/MemesArtSubmissionFile.tsxcomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/common/SandboxedExternalIframe.tsxcomponents/waves/memes/MemesArtSubmissionFile.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-28T12:29:11.651Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.651Z
Learning: Applies to **/*.{ts,tsx} : Do not include any comments in the code
Applied to files:
components/common/SandboxedExternalIframe.tsx
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: Applies to app/api/**/*.{ts,tsx,js,jsx} : Never call fetch directly with user-controlled or scraped URLs in API code; use @/lib/security/urlGuard helpers (parsePublicUrl, assertPublicUrl, fetchPublicUrl, fetchPublicJson) so every hop is validated
Applied to files:
components/common/SandboxedExternalIframe.tsx
🧬 Code graph analysis (3)
components/common/SandboxedExternalIframe.tsx (1)
components/waves/memes/submission/constants/security.ts (1)
isInteractiveMediaAllowedHost(12-21)
components/waves/memes/MemesArtSubmissionFile.tsx (2)
components/waves/memes/submission/constants/media.ts (3)
DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)INTERACTIVE_MEDIA_PROVIDERS(26-29)ALLOWED_INTERACTIVE_MEDIA_MIME_TYPES(2-8)components/common/TabToggle.tsx (1)
TabToggle(17-53)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (5)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/waves/memes/submission/constants/security.ts (2)
INTERACTIVE_MEDIA_HTML_EXTENSIONS(23-27)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(29-35)components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(112-221)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
components/common/SandboxedExternalIframe.tsx (2)
6-9: Remove inline comments per coding guidelines.The coding guidelines for
**/*.{ts,tsx}specify: "Do not include any comments in the code". Lines 6-9 contain security policy comments that should be moved to external documentation.As per coding guidelines.
18-26: Remove documentation comments per coding guidelines.Lines 18-26 contain function documentation that violates the "Do not include any comments in the code" guideline for
**/*.{ts,tsx}files. Move this security documentation to a separate README or SECURITY.md file.As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
__tests__/components/waves/memes/submission/actions/validateInteractivePreview.test.ts(1 hunks)__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts(3 hunks)components/common/SandboxedExternalIframe.tsx(1 hunks)components/waves/memes/submission/actions/validateInteractivePreview.ts(1 hunks)components/waves/memes/submission/constants/security.ts(1 hunks)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts(3 hunks)next.config.mjs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/components/waves/memes/submission/actions/validateInteractivePreview.test.ts
- next.config.mjs
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Do not include any comments in the code
Use react-query for data fetching
Always add readonly before props
Files:
components/common/SandboxedExternalIframe.tsx__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.tscomponents/waves/memes/submission/actions/validateInteractivePreview.tscomponents/waves/memes/submission/constants/security.tscomponents/waves/memes/submission/hooks/useArtworkSubmissionForm.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursorrules)
**/*.tsx: Use FontAwesome for icons
Use TailwindCSS for styling
Files:
components/common/SandboxedExternalIframe.tsx
__tests__/**
📄 CodeRabbit inference engine (tests/AGENTS.md)
Place Jest test suites under the
__tests__directory mirroring source folders (e.g., components, contexts, hooks, utils)
Files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
__tests__/components/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (tests/AGENTS.md)
Use
@testing-library/reactand@testing-library/user-eventfor React component tests
Files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
🧠 Learnings (7)
📚 Learning: 2025-09-28T12:29:11.651Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-09-28T12:29:11.651Z
Learning: Applies to **/*.{ts,tsx} : Do not include any comments in the code
Applied to files:
components/common/SandboxedExternalIframe.tsx
📚 Learning: 2025-09-28T12:33:56.329Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: app/api/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:56.329Z
Learning: Applies to app/api/**/*.{ts,tsx,js,jsx} : Never call fetch directly with user-controlled or scraped URLs in API code; use @/lib/security/urlGuard helpers (parsePublicUrl, assertPublicUrl, fetchPublicUrl, fetchPublicJson) so every hop is validated
Applied to files:
components/common/SandboxedExternalIframe.tsxcomponents/waves/memes/submission/actions/validateInteractivePreview.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Document non-obvious expected behaviour directly in the mock file
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Review mocks periodically and remove unused mock modules
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Keep mock implementations minimal—only what’s necessary for the test scenarios
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:07.561Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __mocks__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:07.561Z
Learning: Applies to __mocks__/**/__mocks__/**/*.{js,jsx,ts,tsx} : Mock only external dependencies or heavy functionality; avoid over-mocking internal logic
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
📚 Learning: 2025-09-28T12:33:30.950Z
Learnt from: CR
Repo: 6529-Collections/6529seize-frontend PR: 0
File: __tests__/AGENTS.md:0-0
Timestamp: 2025-09-28T12:33:30.950Z
Learning: Applies to __tests__/components/**/*.{ts,tsx,js,jsx} : Use `testing-library/react` and `testing-library/user-event` for React component tests
Applied to files:
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts
🧬 Code graph analysis (5)
components/common/SandboxedExternalIframe.tsx (1)
components/waves/memes/submission/constants/security.ts (1)
canonicalizeInteractiveMediaUrl(134-192)
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (3)
components/auth/Auth.tsx (1)
useAuth(95-97)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(104-243)components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (1)
useArtworkSubmissionForm(320-609)
components/waves/memes/submission/actions/validateInteractivePreview.ts (2)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaProvider(31-32)components/waves/memes/submission/constants/security.ts (7)
INTERACTIVE_MEDIA_ALLOWED_CONTENT_TYPES(202-205)canonicalizeInteractiveMediaHostname(19-27)isInteractiveMediaAllowedHost(63-64)isInteractiveMediaContentPathAllowed(96-132)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(194-200)InteractiveMediaValidationResult(207-212)isInteractiveMediaContentIdentifier(72-94)
components/waves/memes/submission/constants/security.ts (1)
components/waves/memes/submission/constants/media.ts (1)
InteractiveMediaProvider(31-32)
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (4)
components/waves/memes/submission/constants/media.ts (3)
InteractiveMediaProvider(31-32)InteractiveMediaMimeType(13-14)DEFAULT_INTERACTIVE_MEDIA_MIME_TYPE(10-11)components/waves/memes/submission/constants/security.ts (2)
isInteractiveMediaContentIdentifier(72-94)INTERACTIVE_MEDIA_GATEWAY_BASE_URL(194-200)components/waves/memes/traits/schema.ts (1)
getInitialTraitsValues(498-522)components/waves/memes/submission/actions/validateInteractivePreview.ts (1)
validateInteractivePreview(104-243)
🪛 Biome (2.1.2)
components/common/SandboxedExternalIframe.tsx
[error] 86-86: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 94-94: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
components/waves/memes/submission/constants/security.ts (1)
134-192: LGTM: Comprehensive URL validation and canonicalization.The function properly validates and canonicalizes URLs with multiple security layers: protocol enforcement, credential rejection, port normalization, hostname canonicalization, and path validation. The defensive re-stripping of credentials and fragments at lines 186-189 is good practice even though they're already validated.
__tests__/components/waves/memes/submission/hooks/useArtworkSubmissionForm.test.ts (1)
1-142: LGTM: Comprehensive test coverage.The test suite provides good coverage of the hook's functionality:
- Trait initialization and updates
- File upload flow with FileReader
- Client-side validation rejection (subpaths)
- Server-side validation success and failure
- Media source switching and URL restoration
Tests use @testing-library/react correctly and follow testing best practices with proper mocking and isolation.
components/waves/memes/submission/actions/validateInteractivePreview.ts (3)
70-93: LGTM: Timeout correctly implemented.The timeout implementation using AbortController with a 10-second limit is correct. The timeout is properly cleared in the finally block to prevent memory leaks, and the Range header efficiently limits GET requests to 1024 bytes.
209-221: LGTM: Excellent redirect validation.The validation of the final URL after redirects (lines 209-221) is critical security defense against SSRF attacks via redirect chains. This ensures that even if the initial gateway URL redirects, the final destination is still within approved hosts.
104-243: LGTM: Comprehensive and secure validation implementation.The validation function implements multiple layers of security:
- Thorough input sanitization rejecting path traversal and injection attempts
- Content identifier validation per provider
- Defense-in-depth with host/path validation at multiple points
- Redirect chain validation preventing SSRF
- Content-Type enforcement
- Proper error handling with generic user-facing messages
components/waves/memes/submission/hooks/useArtworkSubmissionForm.ts (4)
120-199: LGTM: Comprehensive client-side validation.The
buildExternalMediaStatefunction implements thorough client-side validation with multiple security checks:
- Whitespace and sanitization
- Path safety validation
- Query/fragment rejection
- Subpath rejection
- Path traversal prevention
- Content identifier format validation
This provides fast-fail feedback to users before server-side validation, improving UX and reducing unnecessary server requests.
227-313: LGTM: Uploaded URL preservation correctly implemented.The reducer correctly addresses the previous review concern by:
- Adding
uploadArtworkUrlfield to persist the uploaded file URL separately (line 298)- Restoring
uploadArtworkUrlwhen switching back to "upload" mode (line 235)- Not clobbering the uploaded URL when external media state changes
This ensures users don't lose their uploaded file URL when exploring the external media tab.
416-501: LGTM: Robust validation with deduplication.The validation effect correctly handles:
- Deduplication of validation requests using
validationRequestKeyRef(lines 428-429, 440-442, 475-477) preventing race conditions when users rapidly change inputs- Proper cleanup with cancellation flag (line 431, 492-494)
- Error handling with user-friendly messages
- Correct effect dependencies
320-609: LGTM: Well-structured hook with comprehensive API.The hook implementation is well-organized with:
- Clean separation of concerns (sanitization, validation, state management)
- Robust validation with deduplication and error handling
- Complete API surface for consumers
- Proper React patterns (useCallback, useEffect with correct dependencies)
- Fix for uploaded URL preservation addressing previous review feedback
|

Summary by CodeRabbit
New Features
Security / Behavior
Tests
Chores