Skip to content

fix(producer): isPathInside accepts children whose names start with two dots - #5834

Merged
jrusso1020 merged 1 commit into
heygen-com:mainfrom
kevin9327:fix/is-path-inside-dot-dot-names
Oct 28, 2026
Merged

jrusso1020 merged 1 commit into
heygen-com:mainfrom
kevin9327:fix/is-path-inside-dot-dot-names

Conversation

@kevin9327

@kevin9327 kevin9327 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

isPathInside (both the shared @hyperframes/parsers/asset-paths helper and producer/src/utils/paths.ts) now treats only a whole .. segment as "outside the parent". A child whose own name starts with two dots, such as ..assets/logo.png or ..intro.mp4, counts as inside again.

Why

Both helpers reject any relative path where rel.startsWith(".."), which matches ordinary names as well as parent-dir segments:

isPathInside("/project/..assets/logo.png", "/project"); // false before, true now
isPathInside("/project/../secret.txt", "/project");     // false before and after

Callers then treat in-project files as outside the project: collectExternalAssets / publish copy them as hf-ext/... external assets, the font-face embedder skips local fonts under such a folder, and hyperframes media-treatment --file ..draft.html fails with "--file must be an HTML file inside the project". Other containment checks in the repo (cli/src/capture/captureFile.ts, cli/src/registry/publication.ts) already compare against the whole segment.

Related work

Tiny self-contained correction. I searched issues/PRs for isPathInside and found no overlapping work.

How

Replaced !rel.startsWith("..") with a LEADING_PARENT_SEGMENT_RE (/^\.\.(?:[\\/]|$)/) check in both helpers. Matching either separator keeps the producer helper's injected pathModule (e.g. win32 in tests) working without widening its PathModuleLike type. Equal paths, paths above the parent, and different-drive absolute results are still rejected.

Test plan

  • Unit tests added/updated: new packages/parsers/src/assetPaths.test.ts, plus new cases in packages/producer/src/utils/paths.test.ts (POSIX and win32 ..fonts children, and a win32 path above the parent). The new dot-dot-name cases fail on main and pass with the fix.
  • packages/parsers: vitest run gives 46 files, 1288 tests passing.
  • packages/producer: bun run test:unit has the same failing set with and without this change. Those failures are environment-only here (missing browser/ffmpeg/bun:test deps), and the change adds 2 passing tests.
  • oxfmt --check, oxlint and scripts/comment-ratchet.mjs are clean on the changed files.
  • Manual testing performed
  • Documentation updated (if applicable)
  • Comments follow CONTRIBUTING.md "Comments"

AI assistance: I prepared this change with an AI coding assistant, then reviewed it and verified the tests and lint locally myself.

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at d6b2a26c. Approving. This doesn't loosen containment. Both inputs go through resolve() first, so relative() only emits .. as whole leading segments. It can't produce ..foo as a parent reference. The new check rejects .. followed by a separator or the end of the string, which is exactly that set, and the absolute-path check still rejects a different drive. Matching either separator keeps win32 correct. On POSIX it only gets stricter, for a name that contains a literal backslash. The tests keep ../secret.txt, /, the sibling-prefix case and win32 ..\, and the new ..assets cases fail on main.

One small thing inline: the new constant sits between the JSDoc and isPathInside in assetPaths.ts, so the doc comment now documents the constant.

Not for this PR, but worth knowing: about 18 other sites repeat the same rel.startsWith("..") check, for example cli/src/utils/pathArgs.ts, core/src/projects.ts, engine/src/services/audioMixer.ts and studio-server/src/helpers/projectSignature.ts. They all have the same ..name false negative, and a follow-up could point them at this helper.

Need: yes, the bug is still on main · Size: ok · Reuse: fixes the two existing helpers instead of adding a third · Simpler: already minimal; the shared cleanup belongs in a follow-up

— Rames

* Cross-platform containment check: is `childPath` inside `parentPath`?
* Equality counts as "inside".
*/
const LEADING_PARENT_SEGMENT_RE = /^\.\.(?:[\\/]|$)/;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

With the constant here, the JSDoc above now attaches to LEADING_PARENT_SEGMENT_RE instead of isPathInside. Moving the constant above the comment block keeps the doc on the function. The producer copy already puts it in a spot that doesn't split them.

@jrusso1020

Copy link
Copy Markdown
Collaborator

Thanks, @kevin9327. This one is approved, and it'll go into the merge queue once these are sorted:

Once it's green and signed, we'll queue it at your new head. If you push code changes beyond a rebase, we'll take another look first.

— Rames

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

+1 to Rames' approval at this head. I also walked the callers, including the producer file server: files under a folder like ..assets inside the project now count as inside (which is the fix), and real .. traversal is still rejected because both paths are resolved before the relative check. CI is green apart from a cancelled studio capture check this change doesn't touch.

The two things already on the thread still stand: moving the constant above the JSDoc in assetPaths.ts, and signing the two commits so the merge queue can take it.

Verdict: APPROVE
Reasoning: Correct, minimal containment fix with tests that fail on main; checks green; only the existing nit and commit signing remain.

auto-merge was automatically disabled October 25, 2026 12:17

Head branch was pushed to by a user without write access

@kevin9327
kevin9327 force-pushed the fix/is-path-inside-dot-dot-names branch from d6b2a26 to 922eeaf Compare October 25, 2026 12:17
@kevin9327

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews. I squashed the branch into one signed commit (922eeaff, shows as Verified) on top of current main. The code is the same as the approved head, no changes beyond the rebase.

@jrusso1020

Copy link
Copy Markdown
Collaborator

Thanks for re-signing. It conflicts with main again, so could you rebase and push once more? We'll re-approve at the new head.

@kevin9327 kevin9327 closed this Oct 26, 2026
@kevin9327
kevin9327 force-pushed the fix/is-path-inside-dot-dot-names branch from 922eeaf to 64c7bbf Compare October 26, 2026 00:14
@kevin9327

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (64c7bbf2) as one signed commit, 43789f80 (Verified). No code changes; it applied cleanly.

@kevin9327 kevin9327 reopened this Oct 26, 2026
@jrusso1020
jrusso1020 added this pull request to the merge queue Oct 28, 2026
Merged via the queue into heygen-com:main with commit c8052fb Oct 28, 2026
77 checks passed
@heygengenesis heygengenesis Bot mentioned this pull request Oct 28, 2026
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.

3 participants