Conversation
| Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThese changes update streaming platform integration by reformatting SSE encoding logic, extending iframe sandbox permissions for script and form execution, and refactoring event-stream parsing logic including updated field references (streaming_url), event types (PROGRESS replacing STATUS), and status-based completion branching. Changes
Sequence DiagramsequenceDiagram participant API as Check-Platform API participant Hook as useAnimeSearch Hook participant UI as Platform Component participant Browser as Browser/Iframe API->>Hook: SSE Stream (STREAMING_URL) Note over Hook: Check data.type === 'STREAMING_URL'<br/>Read data.streaming_url Hook->>Hook: Set streamingUrl state API->>Hook: SSE Stream (PROGRESS) Note over Hook: Check data.type === 'PROGRESS'<br/>Read data.purpose Hook->>Hook: Update statusMessage API->>Hook: SSE Stream (COMPLETE) alt data.status === 'failed' Hook->>Hook: Set status='error'<br/>Set statusMessage from error<br/>Clear streamingUrl else success Hook->>Hook: Set status='complete'<br/>Set result from data.result<br/>Clear streamingUrl & statusMessage end Hook->>UI: Provide streamingUrl & status UI->>Browser: Render iframe with<br/>sandbox="allow-same-origin<br/>allow-scripts allow-forms" Browser->>Browser: Execute scripts & forms Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly Related Issues
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
| @coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed. Inline comments: In `@anime-watch-hub/components/platform-card.tsx`: - Around line 69-73: The iframe is rendering agent.streamingUrl directly and uses a permissive sandbox ("allow-same-origin allow-scripts allow-forms"); validate and gate the URL before rendering by enforcing a strict HTTPS origin allowlist and a safe URL format (e.g., hostname check + https scheme) where the SSE assigns state (the useAnimeSearch hook / the code that sets agent.streamingUrl), and remove dangerous sandbox flags (drop allow-same-origin and allow-scripts/allow-forms unless explicitly needed); only render the <iframe> in PlatformCard when the streamingUrl has passed the allowlist/validation and use a minimal sandbox (no same-origin/scripts/forms) to follow least privilege. In `@anime-watch-hub/hooks/use-anime-search.ts`: - Around line 60-83: The SSE parser in use-anime-search.ts is using the wrong event shape; update the handling in the event dispatching (the code that currently checks for data.type === 'STREAMING_URL' / 'PROGRESS' / 'COMPLETE') to match the documented Mino schema: treat streamingUrl as a top-level field on events (if data.streamingUrl exists, call updateAgent(platform.id, { status: 'browsing', streamingUrl: data.streamingUrl, statusMessage: 'Browsing platform...' })), treat progress updates as type === 'STATUS' and use data.message for statusMessage (updateAgent(platform.id, { statusMessage: data.message })), and for COMPLETE events read data.resultJson (string) and JSON.parse it into result before calling updateAgent(platform.id, { status: 'complete', result: parsedResult, statusMessage: undefined }); also preserve the error branch for COMPLETE when data.status === 'failed' but read error message from data.error?.message as before. Ensure you reference these symbols: the SSE parsing block in use-anime-search.ts and the updateAgent(platform.id, ...) calls. ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a24c2676-0d6d-477b-9955-8c741a29917d
📒 Files selected for processing (3)
anime-watch-hub/app/api/check-platform/route.tsanime-watch-hub/components/platform-card.tsxanime-watch-hub/hooks/use-anime-search.ts
| src={agent.streamingUrl} | ||
| className="h-full w-full" | ||
| title={`${agent.platformName} live view`} | ||
| sandbox="allow-same-origin" | ||
| sandbox="allow-same-origin allow-scripts allow-forms" | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail # Verify unvalidated streaming_url -> streamingUrl data flow rg -nP --type=ts --type=tsx '\bstreaming_url\b|\bstreamingUrl\b' -C2 # Verify iframe sandbox usage and current permissions rg -nP --type=tsx '<iframe|sandbox=' -C2Repository: tinyfish-io/tinyfish-cookbook
Length of output: 100
🏁 Script executed:
# List repository structure to understand the codebase git ls-files | head -30Repository: tinyfish-io/tinyfish-cookbook
Length of output: 1292
🏁 Script executed:
# Search for platform-card.tsx regardless of reported type fd "platform-card" -t fRepository: tinyfish-io/tinyfish-cookbook
Length of output: 117
🏁 Script executed:
# Also search for anime-watch-hub or similar directory patterns fd -t d "anime" 2>/dev/null || trueRepository: tinyfish-io/tinyfish-cookbook
Length of output: 89
🏁 Script executed:
# Read the platform-card.tsx file to verify the actual code cat -n anime-watch-hub/components/platform-card.tsxRepository: tinyfish-io/tinyfish-cookbook
Length of output: 5057
🏁 Script executed:
# Check file sizes to decide on full vs partial read wc -l anime-watch-hub/hooks/use-anime-search.ts anime-watch-hub/lib/types.ts 2>/dev/null || trueRepository: tinyfish-io/tinyfish-cookbook
Length of output: 164
🏁 Script executed:
# Read use-anime-search.ts around lines 56-66 to verify data flow sed -n '45,100p' anime-watch-hub/hooks/use-anime-search.tsRepository: tinyfish-io/tinyfish-cookbook
Length of output: 2016
🏁 Script executed:
# Read types.ts to check MinoAgentState definition and streamingUrl type cat -n anime-watch-hub/lib/types.tsRepository: tinyfish-io/tinyfish-cookbook
Length of output: 1761
🏁 Script executed:
# Check the start of use-anime-search.ts to see where the SSE endpoint comes from head -50 anime-watch-hub/hooks/use-anime-search.tsRepository: tinyfish-io/tinyfish-cookbook
Length of output: 1619
Harden iframe sandbox + validate streaming URL before render
Line 72 enables scripts and forms while line 69 renders agent.streamingUrl directly. The streamingUrl originates from external SSE payloads via /api/check-platform (anime-watch-hub/hooks/use-anime-search.ts, line 64) with no origin validation or format checking before assignment to state. This creates a high-risk trust boundary: an attacker controlling the backend or intercepting the SSE stream could inject arbitrary URLs that execute scripts inside your app frame. Gate the URL with a strict HTTPS origin allowlist and remove allow-same-origin unless absolutely required.
🔒 Suggested fix (least-privilege + allowlist)
+const TRUSTED_STREAM_ORIGINS = new Set([ + 'https://www.crunchyroll.com', + 'https://www.netflix.com', + 'https://www.amazon.com/Prime-Video', + 'https://www.hulu.com', + 'https://www.funimation.com', + 'https://www.hidive.com', + 'https://www.disneyplus.com', + 'https://www.max.com', +]) + +function getTrustedStreamingUrl(raw?: string): string | null { + if (!raw) return null + try { + const u = new URL(raw) + if (u.protocol !== 'https:') return null + return TRUSTED_STREAM_ORIGINS.has(u.origin) ? u.toString() : null + } catch { + return null + } +} + export function PlatformCard({ agent }: PlatformCardProps) { + const trustedStreamingUrl = getTrustedStreamingUrl(agent.streamingUrl) ... - {agent.streamingUrl && (agent.status === 'connecting' || agent.status === 'browsing') && ( + {trustedStreamingUrl && (agent.status === 'connecting' || agent.status === 'browsing') && ( <div className="relative aspect-video w-full overflow-hidden rounded-md border bg-muted"> <iframe - src={agent.streamingUrl} + src={trustedStreamingUrl} className="h-full w-full" title={`${agent.platformName} live view`} - sandbox="allow-same-origin allow-scripts allow-forms" + sandbox="allow-scripts allow-forms" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@anime-watch-hub/components/platform-card.tsx` around lines 69 - 73, The iframe is rendering agent.streamingUrl directly and uses a permissive sandbox ("allow-same-origin allow-scripts allow-forms"); validate and gate the URL before rendering by enforcing a strict HTTPS origin allowlist and a safe URL format (e.g., hostname check + https scheme) where the SSE assigns state (the useAnimeSearch hook / the code that sets agent.streamingUrl), and remove dangerous sandbox flags (drop allow-same-origin and allow-scripts/allow-forms unless explicitly needed); only render the <iframe> in PlatformCard when the streamingUrl has passed the allowlist/validation and use a minimal sandbox (no same-origin/scripts/forms) to follow least privilege. | if (data.type === 'STREAMING_URL' && data.streaming_url) { | ||
| updateAgent(platform.id, { | ||
| status: 'browsing', | ||
| streamingUrl: data.streamingUrl, | ||
| streamingUrl: data.streaming_url, | ||
| statusMessage: 'Browsing platform...', | ||
| }) | ||
| } | ||
| | ||
| if (data.type === 'STATUS' && data.message) { | ||
| updateAgent(platform.id, { statusMessage: data.message }) | ||
| if (data.type === 'PROGRESS' && data.purpose) { | ||
| updateAgent(platform.id, { statusMessage: data.purpose }) | ||
| } | ||
| | ||
| if (data.type === 'COMPLETE') { | ||
| let result = { | ||
| available: false, | ||
| message: 'Check completed', | ||
| if (data.status === 'failed') { | ||
| updateAgent(platform.id, { | ||
| status: 'error', | ||
| statusMessage: data.error?.message ?? 'Automation failed', | ||
| streamingUrl: undefined, | ||
| }) | ||
| } else { | ||
| updateAgent(platform.id, { | ||
| status: 'complete', | ||
| result: data.result, | ||
| statusMessage: undefined, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== Documented Mino SSE fields ==" rg -n -C2 'streamingUrl|type":"STATUS"|resultJson' anime-watch-hub/docs/mino-api-integration.md echo echo "== Current client parser ==" sed -n '56,85p' anime-watch-hub/hooks/use-anime-search.ts echo echo "== Server proxy behavior ==" sed -n '88,97p' anime-watch-hub/app/api/check-platform/route.ts echo echo "== Downstream UI dependence on agent.result.available ==" rg -n -C2 'result\?\.available' anime-watch-hub/components/platform-card.tsxRepository: tinyfish-io/tinyfish-cookbook
Length of output: 4228
Fix schema mismatch between Mino SSE and client parser.
The documented Mino SSE schema (lines 256–265 in anime-watch-hub/docs/mino-api-integration.md and concrete examples at lines 395–407) sends:
streamingUrlas a direct field (not undertype: 'STREAMING_URL')type: 'STATUS'withmessagefor progress updatesresultJson(stringified) inCOMPLETEevents
The hook parser (lines 60–83 in anime-watch-hub/hooks/use-anime-search.ts) currently looks for:
type: 'STREAMING_URL'withstreaming_url(field structure mismatch)type: 'PROGRESS'withpurpose(type mismatch)resultfield (schema hasresultJson)
Since /api/check-platform/route.ts proxies the upstream stream unchanged, the parser drops all streaming URL events and progress updates, and sets result: undefined on completion. This causes anime-watch-hub/components/platform-card.tsx (lines 22, 42) to render "Not Found" regardless of the actual search result.
🔧 Proposed compatibility fix
- if (data.type === 'STREAMING_URL' && data.streaming_url) { + const streamingUrl = data.streamingUrl ?? data.streaming_url + if (streamingUrl) { updateAgent(platform.id, { status: 'browsing', - streamingUrl: data.streaming_url, + streamingUrl, statusMessage: 'Browsing platform...', }) } - if (data.type === 'PROGRESS' && data.purpose) { - updateAgent(platform.id, { statusMessage: data.purpose }) + const progressMessage = + data.type === 'STATUS' + ? data.message + : data.type === 'PROGRESS' + ? data.purpose + : undefined + if (progressMessage) { + updateAgent(platform.id, { statusMessage: progressMessage }) } if (data.type === 'COMPLETE') { if (data.status === 'failed') { updateAgent(platform.id, { @@ } else { + const result = + data.result ?? + (typeof data.resultJson === 'string' + ? JSON.parse(data.resultJson) + : data.resultJson) + updateAgent(platform.id, { status: 'complete', - result: data.result, + result, statusMessage: undefined, streamingUrl: undefined, }) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@anime-watch-hub/hooks/use-anime-search.ts` around lines 60 - 83, The SSE parser in use-anime-search.ts is using the wrong event shape; update the handling in the event dispatching (the code that currently checks for data.type === 'STREAMING_URL' / 'PROGRESS' / 'COMPLETE') to match the documented Mino schema: treat streamingUrl as a top-level field on events (if data.streamingUrl exists, call updateAgent(platform.id, { status: 'browsing', streamingUrl: data.streamingUrl, statusMessage: 'Browsing platform...' })), treat progress updates as type === 'STATUS' and use data.message for statusMessage (updateAgent(platform.id, { statusMessage: data.message })), and for COMPLETE events read data.resultJson (string) and JSON.parse it into result before calling updateAgent(platform.id, { status: 'complete', result: parsedResult, statusMessage: undefined }); also preserve the error branch for COMPLETE when data.status === 'failed' but read error message from data.error?.message as before. Ensure you reference these symbols: the SSE parsing block in use-anime-search.ts and the updateAgent(platform.id, ...) calls.
I fixed the issues of #86
After
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes