Repository navigation
fix(cli): publish leaves out private files outside the project and says so - #7577
Conversation
…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>
Edit accuracy: accurate 267 (base branch 267), smooth 103 of thoseThe gate passes. Quarantined, measured but not gated (8)
Unstable (2)
|
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 0 important · 1 nit · 1b87f245d4
- blocker (Rule 2, sibling path, already on main):
collectProjectFiles(publishProject.ts:324) follows links withstatSync. 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_ed25519plus anindex.htmlthat 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 anisSymbolicLink()entry whose real path is outside the project, runisPrivateOutsideFileand leave it out with the same warning.includeReferencedProjectFilereaches 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>
There was a problem hiding this comment.
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_ed25519with<img src="logo.png">stageslogo.png= the key bytes, while the warning says it was left out. That reference takesarchiveTargetOf→includeReferencedProjectFile, and therestaysOut(publishProject.ts:426) doesn't checklinksToPrivateOutsideFile. The walk skipped this file, soctx.fileContents.has(rel)is false, andisPrivateCompositionFiledoesn'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 fromindex.html. - important (new): the
library/appdatamatch is too broad. ↓ inline
— Genesis
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ut too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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:
| return folder !== null && !isUnder(projectDir, folder); | |
| return folder !== null && homeAppDataFolder(projectDir) !== folder; |
What
hyperframes publishandhyperframes cloud rendernow leave out a file outside the project when it sits in a hidden folder (~/.ssh,~/.aws,~/.config, a dotfile), in the home folder'sAppData(Windows) orLibrary(macOS), except iCloud Drive'sLibrary/Mobile Documents, or under/etc,/proc,/sysor/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: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
isPrivateOutsideFileinprivateProjectFile.tswraps 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/pcan still use../shared/logo.png); only the segments past where the two paths part do.archiveTargetOfuses it for outside targets only; in-project files take the earlier branch and are unchanged. Missing files are still skipped with no message.stagePublishFiles/buildPublishFileMaptake an optionalonLeftOutcallback, deduplicated per file and reference.publishprints the warnings withclack.log.warnafter the spinner stops;cloud renderprints them under "Zipping", to stderr with--json.stagePublishFilesalso 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
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'sAppData/Libraryvs iCloud Drive, a sibling asset folder namedlibrarystill copied,../and absolute refs into hidden folders, a link into one,/etc, outside.envnow 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.hyperframes cloud render <dir> --dry-runon a project referencing../shared/logo.pngand../.secret/id_ed25519from two files: the logo goes into_ext/, the key is left out with one warning per file, and--jsonstdout stays clean.— Rames Jusso
🤖 Generated with Claude Code