Repository navigation
fix(producer): local renders bring in only media, font, CSS and JS from outside the project - #7647
Merged
Merged
Conversation
…om outside the project Local renders (`render`, range render, `benchmark`, the Studio server and the Vite Studio adapter) run with allowOutsideProjectAssets, which copied every existing file that markup named outside the project into compiled/hf-ext, of any type and from any folder, and served project links that lead outside without looking at where they lead. Publish already applies a policy here: outside references are kept, private locations are left out, and only image, video, audio, font, CSS and JS files are copied. Local renders now follow the same policy. The publish predicates (private location, copied file type, left-out wording) move from the CLI to a new `@hyperframes/studio-server/outside-file` subpath, since the producer cannot import the CLI and already depends on studio-server. The CLI re-exports them, so publish behaves as before. The media extension sets move with them because the copied-type list includes them. The producer decides with one predicate, `outsideAssetRefusal` in utils/paths.ts, and applies it wherever allowOutsideProjectAssets lets an outside file in, so no entry point can skip it: - collectExternalAssets: src/href/url() references, which covers <a href> and <link href> - dropMediaOutsideProject: video/audio/image sources in any spelling the media resolver accepts (file: URLs, percent-encoding), so nothing probes or extracts them - the render file server: project links that lead outside - the distributed plan's project copy: the same links (range render) - GIF/HEIC prep and sub-composition links: private locations only, since those inputs are converted or compiled rather than copied as they are Each left-out file logs the publish wording with "left out of the render". To reproduce on main: a project whose index.html references ../../.ssh/id_key through <link href>, <a href> and url(), with a project link to a file in .ssh, rendered locally, gets those files copied into compiled/hf-ext and served. Comment lines: the four in studio-server's outsideFile.ts moved verbatim from the CLI files they replace. Two are new, in utils/paths.ts. The one on outsideAssetRefusal states the policy once, so its six call sites carry no comment. The one on refusesOutsideLink says its boolean means "refused, and already warned", which the name alone does not say. Comment ratchet is clean. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Contributor
The base repository's deletion guard rejects removing a file that exists on main unless git pairs it as a rename. outsideFileType.ts moved into studio-server/outside-file with enough added around it that git no longer pairs the two, so CI's "Reject accidental file deletions" step failed. The file stays as a two-name re-export and publish keeps importing from it, so publishProject.ts and its file-type test are back to main. Co-Authored-By: Claude Opus 5.5 <[email protected]>
… policy CI's packed-manifest check measured @hyperframes/producer at 3,244,665 bytes unpacked (budget 3,210,000) and @hyperframes/studio-server at 653,506 (budget 646,000), both just past the 1% tolerance. Both packages were already at the edge on main. Measured locally with npm pack --dry-run, main is 3,237,162 for producer and 652,502 for studio-server, so studio-server on main is already at its ceiling. This branch adds about 7.9 KB unpacked to producer, because the new checks sit in the compiler, render stages and file server, which each producer entry bundles. It adds about 5.5 KB to studio-server for the new outside-file entry and its declarations. No source maps, tests or assets were added to either tarball. The budgets go to 3,245,000 and 654,000, just above CI's measurements, the same way #7579 raised producer's budget for the project-folder checks. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Contributor
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head 09324191
- blocker —
packages/producer/src/services/htmlCompiler.ts:2001(embedLocalFontFaces, not in the diff): a trusted render still reads a project file that is a link to a private location and puts its bytes intocompiled/index.html, with no warning. In trusted modeassetRootisundefined, so the only check is the lexicalisPathInside. Repro, run with vitest at this head:project/brand.ttf→<root>/.ssh/id_keyand<style>@font-face { src: url("brand.ttf") }</style>, thenrunCompileStagewithallowOutsideProjectAssets: true. Result:compiled/index.htmlcontainsurl("data:font/ttf;base64,UFJJVkFURS1LRVktQ09OVEVOVFM="), which is the key's contents, andwarningsis[]. The file server and plan copy refuse that same link, but this path reads the file before either one runs, and on a range render the compiled HTML goes into the plan. This contradicts the PR's "every place whereallowOutsideProjectAssetslets an outside file in now calls it" and the manual check that the private file's contents "appeared nowhere in the work directory". It also embeds afile_typerefusal, such as a.ttflink tonotes.txt, because the extension falls back tottf. Fix: after theisPathInsidecheck,if (outsideAssetRefusal(projectDir, absPath)) continue;, warned vialeftOutOfRender, and add a font case to the private-outside fixture.
Scope
Read the full diff. Audited every allowOutsideProjectAssets/assetRoot gate in producer: compile/probe/plan/fileServer/GIF+HEIC prep/sub-compositions are covered. The remaining assetRoot readers (duration probe, VFR/keyframe advisories) only run ffprobe and copy no bytes. Ran outsideProjectAssets.test.ts locally: 7/7 pass. CI on head is green, and the cancelled runs were superseded.
A project font file that links outside the project is now left out of font embedding with the same warning the file server gives, instead of being inlined into the compiled HTML. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merged
1 of 2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Local renders now apply publish's outside-file policy. Files outside the project are still brought in, but only media, font, CSS and JS files, and never from private folders such as hidden folders, env files, Git data, system folders or per-user app data. Each file that is left out is named in a warning that uses publish's wording, ending "left out of the render".
Claude Opus 5.5
What
hyperframes render, range renders,hyperframes benchmark, the Studio server's render and the Vite Studio adapter all render withallowOutsideProjectAssets. Until now, that copied every existing file that markup named outside the project intocompiled/hf-ext/, whatever its type or folder, and served project links that lead outside the project without checking where they lead. This PR makes local renders follow the policy publish already uses:Why
Publish and local render should agree on what a project may pull in from around it. Until now a local render brought in any outside file a project named, of any type and from any folder, while publish already limited both.
Related work
Applies the publish policy from
packages/cli/src/utils/privateProjectFile.tsandoutsideFileType.tsto local renders.How
Shared policy. The private-location predicate, the copied-type list and the left-out wording move from the CLI to a new
@hyperframes/studio-server/outside-filesubpath. The producer cannot import the CLI, and it already depends on studio-server. The media extension sets move with them, unchanged (100% rename), because the copied-type list includes them. The CLI imports or re-exports everything from the new subpath, so publish behaves exactly as before.cli/src/utils/outsideFileType.tsstays as a two-name re-export, so publish's imports are unchanged.leftOutWarninggained afromargument ("upload"by default).One enforcement point in the producer.
outsideAssetRefusal(projectDir, path)inproducer/src/utils/paths.tsreturnsblocklisted,file_typeornullfor a path whose real location is outside the project. Every place whereallowOutsideProjectAssetslets an outside file in now calls it. Callers only passallowOutsideProjectAssets: true, so no entry point can skip the check.collectExternalAssets(htmlCompiler)src/href/url()reference, including<a href>and<link href>, spelled as../or an absolute or drive pathdropMediaOutsideProject(now also called in trusted mode, from compile and probe)file:URLs, percent-encoding), so nothing extracts frames from them@font-faceembeddingrenderSpan→plan)Entry points:
render,benchmark, the Studio server and the Vite adapter all go throughexecuteRenderJob, which runs compileStage, probeStage and the file server. Range render goes throughrenderSpan, which runsplanand then compileStage.Test plan
New tests in
packages/producer/src/services/outsideProjectAssets.test.ts:.sshfolder two levels up, using../, an absolute path, afile:URL, a percent-encoded path,<link href>,<a href>, CSSurl()and inline-styleurl(). It also has a video, an animated GIF, a project link into.ssh,@font-facefiles that link to a.sshfile and tonotes.txt,../shared/notes.txt,../shared/logo.pngand a project link to that logo. The test checks thatcompiled/holds onlyindex.htmland the logo, that the composition has no videos, that no ffmpeg extraction runs on the.sshfiles, that each skip is a named warning, that the file server returns 404 for the private copies and the link and 200 for the logo and its link, and that the plan copy for range render contains nothing from.ssh, nonotes.txtand no private link.Results:
render-symlinked-assets. No left-out warnings and no 404s. Locally, its pixel comparison fails by the same 100 frames on main and on this branch, which points to my box rather than this change.hyperframes render --debugon a project like the test fixture. The render succeeded, every left-out file was named in a warning,compiled/hf-ext/held onlyshared/logo.png, and the private file's contents appeared nowhere in the work directory.Size budgets.
scripts/package-size-budgets.jsonraises the unpacked budgets: producer goes from 3,210,000 to 3,245,000, and studio-server from 646,000 to 654,000. Both were already at the 1% edge on main. Measured locally withnpm pack --dry-run, main is 3,237,162 for producer and 652,502 for studio-server. This branch adds about 7.9 KB to producer, because the checks sit in code that every producer entry bundles. It adds about 5.5 KB to studio-server for the newoutside-fileentry and its declarations. No source maps, tests or assets were added. #7579 raised producer's budget the same way.No visible change
This change is in the producer, studio-server and CLI only. No UI changes.
— Rames Jusso
🤖 Generated with Claude Code