Skip to content

fix(cli): publish leaves out private files outside the project and says so - #7577

Merged
jrusso1020 merged 6 commits into
mainfrom
fix/publish-skip-private-outside-files
Nov 12, 2026
Merged

jrusso1020 merged 6 commits into
mainfrom
fix/publish-skip-private-outside-files

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

What

hyperframes publish and hyperframes cloud render now leave out a file outside the project when it sits in a hidden folder (~/.ssh, ~/.aws, ~/.config, a dotfile), in the home folder's AppData (Windows) or Library (macOS), except iCloud Drive's Library/Mobile Documents, or under /etc, /proc, /sys or /dev. The same goes for a link inside the project that points at such a file. Whenever an outside reference is left out, for that reason or because it is an env file or Git data, the CLI now says so:

compositions/intro.html references ../../.ssh/id_ed25519, which is outside the project in a private location; it was left out of the upload.

Every other outside reference (../shared/logo.png, /Users/me/Movies/clip.mp4) is still copied into _ext/ as before.

Why

Projects you open may come from someone else, and their markup can point at files outside the project folder. Publish copies those into the upload, so a reference into a hidden or system folder on your machine would be uploaded without you noticing. The existing env/Git skip also happened silently, so a missing asset gave no hint why.

Related work

None.

How

  • isPrivateOutsideFile in privateProjectFile.ts wraps the existing env/Git check and adds the hidden-folder and system-folder check, both on the path as written and after resolving links. Hidden folders the project itself sits in don't count (a project under ~/.work/p can still use ../shared/logo.png); only the segments past where the two paths part do.
  • archiveTargetOf uses it for outside targets only; in-project files take the earlier branch and are unchanged. Missing files are still skipped with no message.
  • stagePublishFiles / buildPublishFileMap take an optional onLeftOut callback, deduplicated per file and reference. publish prints the warnings with clack.log.warn after the spinner stops; cloud render prints them under "Zipping", to stderr with --json.
  • stagePublishFiles also checks each collected project file: a link whose real target is outside the project and private is left out with <file> links to a file outside the project in a private location; it was left out of the upload. Links to ordinary outside files are still uploaded as before.
  • feedback's public repro publish builds its archive the same way and prints the same warnings after its spinner.

Test plan

  • Unit tests added/updated (publishProject.absolutePath.test.ts: an in-project link to a private outside file (fails with the guard disabled), an in-project link to an ordinary outside file still uploaded, the home folder's AppData/Library vs iCloud Drive, a sibling asset folder named library still copied, ../ and absolute refs into hidden folders, a link into one, /etc, outside .env now warns, ordinary outside file still copied, project inside a hidden folder, in-project hidden folder unchanged, missing file silent). With the source change reverted, the 5 new refusal tests fail and the rest pass.
  • Lint, format, typecheck and fallow hooks pass
  • Manual testing performed: hyperframes cloud render <dir> --dry-run on a project referencing ../shared/logo.png and ../.secret/id_ed25519 from two files: the logo goes into _ext/, the key is left out with one warning per file, and --json stdout stays clean.
  • Documentation updated (if applicable): not applicable
  • Comments follow CONTRIBUTING.md "Comments"

— Rames Jusso

🤖 Generated with Claude Code

jrusso1020 and others added 2 commits November 12, 2026 00:06
…ys so

A project you open may come from someone else, and its markup can point at
files outside the project folder. Publish and cloud render now leave out an
outside file that sits in a hidden folder (any path segment starting with a
dot past the folders the project itself sits in) or under /etc, /proc, /sys
or /dev, checked both as written and after resolving links.

Whenever an outside reference is left out, for this reason or because it is
an env file or Git data, the CLI now prints which project file holds the
reference and the reference as written. Other outside references are copied
into _ext/ as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 103 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)

Unstable (2)

  • seqplainkeys-none-pct-r0-nested-z100: tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true
  • seqfastkeys-none-px-r0-root-z100: tracking 0.07, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.07, pressJump 0.05, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

@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 · 0 important · 1 nit · 1b87f245d4

  • blocker (Rule 2, sibling path, already on main): collectProjectFiles (publishProject.ts:324) follows links with statSync. So a link inside the project that points at a private file outside it still gets uploaded, with no warning and without needing any reference. Repro on this head: p/logo.png -> ../.ssh/id_ed25519 plus an index.html that references nothing gives staged keys ["index.html","logo.png"], logo.png = the key bytes, warnings []. A cloned repo can carry exactly this kind of link, and it's the easiest way to hit the risk this PR closes. Fix: for an isSymbolicLink() entry whose real path is outside the project, run isPrivateOutsideFile and leave it out with the same warning. includeReferencedProjectFile reaches the same files through the same walk.

The reference fix checks out: the 5 new tests fail with privateProjectFile.ts and publishProject.ts reverted, and all 14 pass on this head. archiveTargetOf is the only path that adds files under _ext/.

— Genesis

…les, and AppData/Library

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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 resolved, 1 open, 1 new · 1b87f245d4 → 192a629428

  • blocker (still open): an in-project link to a private outside file is now left out only when nothing references it. If something does reference it, it still uploads: p/logo.png -> ../.ssh/id_ed25519 with <img src="logo.png"> stages logo.png = the key bytes, while the warning says it was left out. That reference takes archiveTargetOf → includeReferencedProjectFile, and there staysOut (publishProject.ts:426) doesn't check linksToPrivateOutsideFile. The walk skipped this file, so ctx.fileContents.has(rel) is false, and isPrivateCompositionFile doesn't look at where a link points. Fix: return isPrivateCompositionFile(ctx.absProjectDir, absPath) || (existsSync(absPath) && linksToPrivateOutsideFile(ctx.absProjectDir, absPath));, plus a test like the new one that also references the link from index.html.
  • important (new): the library/appdata match is too broad. ↓ inline

— Genesis

jrusso1020 and others added 2 commits November 12, 2026 00:39
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ut too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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 — 2 resolved, 0 open, 1 new · 192a629428 → b880fa3fdc

The referenced-link leak and the library false positive are both fixed; I confirmed both locally. New blocker: Windows render verification → Tests on windows-latest: engine-cli-1 fails 5 tests in publishProject.absolutePath.test.ts / publishProject.test.ts, and they come from this head. ↓ inline

— Genesis

…en the project sits there

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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 — 3 resolved, 0 open, 1 new (important) · b880fa3fdc → 5a03aed08e. All checks are green, including Windows engine-cli-1/-2. The cli publish suites pass locally (97/97). One important follow-up on the iCloud exemption ↓ inline; it's a one-line fix and can go in this PR or right after.

— Genesis


function inHomeAppData(projectDir: string, path: string): boolean {
const folder = homeAppDataFolder(path);
return folder !== null && !isUnder(projectDir, folder);

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: a project on iCloud Drive (~/Library/Mobile Documents/…) counts as "inside Library", so the Library rule is switched off for it entirely. Repro with HOME pointed at a temp folder: a project at ~/Library/Mobile Documents/com~apple~CloudDocs/p with <img src="../../../Application%20Support/app/token.json"> stages _ext/Application Support/app/token.json with no warning. Compare the project's own folder through the same exclusion:

Suggested change
return folder !== null && !isUnder(projectDir, folder);
return folder !== null && homeAppDataFolder(projectDir) !== folder;

@jrusso1020
jrusso1020 added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit b4bd8b0 Nov 12, 2026
84 checks passed
@jrusso1020
jrusso1020 deleted the fix/publish-skip-private-outside-files branch November 12, 2026 02:16
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