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