Skip to content

fix(producer): render reads a composition src like the preview, refuses paths outside - #7317

Merged
miguel-heygen merged 2 commits into
mainfrom
drenderq/subcomp-src-paths
Nov 6, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
drenderq/subcomp-src-paths

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

The render's compile step now resolves a data-composition-src through one helper, resolveCompositionSrc (utils/paths.ts), at its three sites in htmlCompiler.ts (the pre-flight check, the sub-composition reader and the inliner's reader). It decodes %xx, treats a leading / as the project root (the stylesheet reader already does both), keeps a name that exists only as written, and returns null when the decoded path is not inside the project. Each data-composition-src is also rewritten once, in the entry html and in every sub-composition file the compiler reads, to the decoded project-relative path, so the asset rewriting (rewriteAssetPath) sees the real folder and an encoded folder's sibling images are re-pointed. A null is reported by the pre-flight as "it is outside the project folder" and read as missing elsewhere.

Why

A compositions/my%20scene.html or /compositions/scene.html source is read by the Studio preview but rendered as "the file does not exist" (composition file referenced by data-composition-src cannot be rendered). Reproduced on GitHub runners for 0.9.16 and 0.9.19: plain, ./ and (on Windows) backslash and wrong-case spellings render; the encoded name and the leading slash fail on both Windows and Linux with that text.

The guard matters because decoding opens a way out: before this change resolve(projectDir, src) already followed ../ and an absolute path anywhere on the disk, and %2e%2e%2f would have followed once decoded. A decoded path that leaves the project is now refused.

What I measured

  • fails without the fix: packages/producer/src/services/htmlCompiler.test.ts, six new cases: "compileForRender reads a data-composition-src written with a %20-encoded name" / "...a leading slash for the project root", and "compileForRender refuses a data-composition-src that leaves the project through" dot-dot segments, an encoded ../, a leading slash and dot-dot, an encoded leading slash and dot-dot. With htmlCompiler.ts and paths.ts at main: 6 fail, 2 pass; with the change that file and utils/paths.test.ts give 202 pass, 0 fail (bun test, Linux box). Also "compileForRender re-points a sibling asset of a sub-composition whose folder is %20-encoded": fails (1 fail) with the previous head's htmlCompiler.ts, passes with this one.
  • The GitHub runner repro above.

What I did NOT exercise

  • Studio-server and the core bundler's own source resolution (the preview path) are not changed or audited here.
  • Windows drive-letter paths and UNC sources go through isPathInside but have no test of their own.
  • No full render of a project with an encoded source; the compile step is what the tests run.

Size

About 60 changed lines in 3 files, 40 of them tests: a lone fix on one resolver; nothing open on it to carry it.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · head 6e1404cb

  • blocker: Preflight (lint + format) fails (oxfmt --check: htmlCompiler.test.ts). Because of that, regression-shards was skipped and regression is red, so this compile-path change has not been through the render regression suite. ↓ inline
  • important: the encoded-directory case is only half supported. ↓ inline
Scope

Read all 3 files. Checked every data-composition-src resolution in producer: the three sites changed here, plus recompileWithResolutions, which runs after the pre-flight. Refusing paths outside the project matches the preview bundler (resolveEntryPath → isSafeProjectPath). The tests named on the 'fails without the fix:' line fail at main as claimed: dot-dot used to read the outside file, and a leading slash resolved to the filesystem root. Studio-server resolution was not audited, as the PR says. Windows render verification also fails on main at 410a516c.

["an encoded ../", "%2e%2e%2foutside-scene.html"],
["a leading slash and dot-dot", "/../outside-scene.html"],
["an encoded leading slash and dot-dot", "%2f%2e%2e%2foutside-scene.html"],
])("compileForRender refuses a data-composition-src that leaves the project through %s", async (_label, src) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocker (CI): oxfmt --check flags this file, most likely this over-long it.each(...)(...) line. Preflight fails, so regression-shards is skipped and regression fails with "Regression shards did not succeed." Run bun run format and push, so the render regression suite actually runs on a producer change.

}

/** The project file a data-composition-src names, as the preview reads it; null when it leaves the project. */
export function resolveCompositionSrc(projectDir: string, src: string): string | null {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important: this decodes the src to find the file, but the inliner and the gsap rewrite still pass the raw srcPath to rewriteAssetPath (parsers/src/rewriteSubCompPaths.ts:79-86). There, compDir keeps %20, so assetExists("my%20dir/img.png") is false. As a result, data-composition-src="my%20dir/scene.html" now renders, but its sibling <img src="img.png"> is not re-pointed and loads from the project root (missing). The new tests only encode the file name. Pass the decoded project-relative path as compSrcPath, or add a case with the encoding in the directory part.

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 120 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Head 9469790: oxfmt applied (Preflight/Format blocker); the encoded-folder finding is fixed at the root: every data-composition-src is rewritten once to its decoded project-relative path (entry html and each sub-composition file read), so rewriteAssetPath and the gsap rewrite see the real folder. New test: an encoded folder with a sibling image fails with the previous head's htmlCompiler.ts and passes now; compiler and paths tests 202 pass, 0 fail.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 2 resolved, 0 open, 0 new · 6e1404cb → 9469790c

Formatting passes, and the regression shards ran and passed on this head (run 42228938094, shards 1–10). The data-composition-src values are normalized in both the entry and the sub-composition HTML. The subCompositions keys and the inliner's lookups therefore agree, and rewriteAssetPath now gets the decoded folder; the new encoded-folder test covers this. Windows render verification was still running; it also fails on main.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 4cf26b5 Nov 6, 2026
140 of 142 checks passed
@miguel-heygen
miguel-heygen deleted the drenderq/subcomp-src-paths branch November 6, 2026 15:04
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view to heygen-com#7250

Adopts heygen-com#7310 (MPEG-4 Part 2 / MJPEG get browser-playable copies), heygen-com#7316
(check_report counts the copies a check waited for) and heygen-com#7317 (render reads
a composition src like the preview and refuses paths outside the project).
Defers heygen-com#7309 (needs heygen-com#6912); heygen-com#7314 does not apply. PR axis advances to heygen-com#7250
(heygen-com#7243 closed unmerged, heygen-com#7249 open and pinned as monitored; monitored 96->97).

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant