Repository navigation
fix(wolves-playlist): gate video ids and thumbnail hosts before writing artwork - #955
hivecommons-hive[bot] wants to merge 1 commit into
Conversation
…ng artwork
normalizePlaylistEntries accepted any non-empty string as a video id and
used it directly in the public/wolves-artwork/<id>.jpg path, and
readThumbnailUrl fell back to the raw URL for any host or scheme, so
downloadArtwork would fetch arbitrary origins and write the body into
public/. Require ids to match the same /^[\w-]{1,64}$/ pattern the
back-catalogue script uses, and accept only https thumbnails on YouTube's
image hosts, failing closed otherwise.
Fixes #954
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sec-check <280983584+hivecommons-hive[bot]@users.noreply.github.com>
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "sec-check" agent while Hive policy required a human checkpoint for that agent. Agent PRs are held at ACMM L3–L5; an Hive will keep the |
There was a problem hiding this comment.
Read the full script at f8f5d68 plus the callers (normalizePlaylistEntries → downloadArtwork → writeFile(join(PUBLIC_DIR, track.artwork))), and ran vitest run src/tests/wolvesPlaylistScript.test.ts on this head: 6/6 pass.
This looks correct to me and does what the description says:
scripts/update-wolves-playlist.js:30—VIDEO_ID_PATTERNis byte-identical toPLAYLIST_ID_PATTERNatscripts/update-back-catalogue.js:45, andrequireVideoId(:46-53) is wired in at:99, so thewolves-artwork/${id}.jpgpath at:111can no longer contain/,.or an empty segment.readThumbnailUrl(:74-87) now fails closed instead of returning the raw URL: non-parsable → throws, non-https:or host not inTHUMBNAIL_HOSTS→ throws. The oldcatch {}fallthrough that letfile:/ arbitrary origins reachfetch()at:170is gone.- The test change swapping
img.exampleforyt3.ggpht.comis the right adjustment, not a loosening — the old fixture only passed because of the fallthrough.
One behavioural note for whoever merges (not a defect): the allowlist is i.ytimg.com, i9.ytimg.com, yt3.ggpht.com only. If yt-dlp ever returns a thumbnail on another ytimg mirror the update:wolves-playlist run will now abort rather than silently pass the URL through. That is the intended fail-closed behaviour; just expect the error message to point here if it happens.
No findings. Not approving — that is a maintainer's signature.
Confidence: 5/5 (safe)
— hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88
|
Hive filed review findings that do not block this PR as backlog items:
Cap: 10 backlog item(s) per PR per day. |
Summary: The maintainer-run script that refreshes cover art for the "Wolves" soundtrack playlist now checks what it is told by the YouTube listing tool before acting on it. Video ids must look like real YouTube ids before they are used to name a file, and cover images are only downloaded from YouTube's own image servers over an encrypted connection; anything else stops the run with a clear error. This closes the gap reported in the playlist cover-art finding (#954) and brings the script in line with the back-catalogue refresh script, which already does both checks.
What I need from you: Review and approve; hold-gated, no automerge.
Security Fix
Files:
scripts/update-wolves-playlist.js,src/tests/wolvesPlaylistScript.test.tsFunctions:
normalizePlaylistEntries, newrequireVideoId,readThumbnailUrlVIDEO_ID_PATTERN = /^[\w-]{1,64}$/(same shape asPLAYLIST_ID_PATTERNinupdate-back-catalogue.js) and enforce it inrequireVideoId()before the id becomeswolves-artwork/${id}.jpgand reachesjoin(PUBLIC_DIR, …)indownloadArtwork().readThumbnailUrl()now throws unless the thumbnail parses as a URL withprotocol === 'https:'and a hostname inTHUMBNAIL_HOSTS(i.ytimg.com,i9.ytimg.com,yt3.ggpht.com). The previous code fell back to returning the raw URL for any other host or scheme, whichfetch()then downloaded intopublic/.Tests: fixture that used a non-YouTube host (
img.example) moved toyt3.ggpht.com; added rejection cases for traversal/dotted/over-long/empty ids and forhttp:, foreign-host, look-alike-host,file:and unparseable thumbnails.npx vitest run src/tests/wolvesPlaylistScript.test.ts→ 6 passed;npx eslinton both files clean.Behaviour change: a playlist entry with a non-YouTube thumbnail now aborts
npm run update:wolves-playlistinstead of silently importing it. That is intended — the script's only legitimate source is YouTube.Fixes #954
Filed by sec-check agent (ACMM L4/L5 — hold-gated mode). Hold-gated: human review required.
— hive: agent=sec-check backend=copilot model=claude-fable-5.1 copilot=1.0.88