Skip to content

[Blueprints] Keep directory theme writes inside the target folder - #3857

Merged
adamziel merged 1 commit into
trunkfrom
add/theme-directory-path-safety
Jul 1, 2026
Merged

[Blueprints] Keep directory theme writes inside the target folder#3857
adamziel merged 1 commit into
trunkfrom
add/theme-directory-path-safety

Conversation

@adamziel

@adamziel adamziel commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What it does

Prevents directory-based theme installs from writing outside the intended theme folder.

Rejected examples:

{ "themeData": { "name": "../escape" } }
{ "options": { "targetFolderName": "nested/theme" } }
{ "themeData": { "files": { "../escape.php": "..." } } }

Rationale

Zip-based theme installs go through installAsset(), which moves one resolved asset folder into wp-content/themes.

Directory resources skip installAsset() and pass caller-provided names and file keys directly to writeFiles(). Without an explicit boundary check, ../ segments can resolve outside the theme directory before the files are written.

Blueprint v2 lowering emits directory resources, so this needs to be enforced before the v2 runner depends on that path.

Implementation

Uses the existing POSIX path utilities instead of local string parsing:

  • installTheme() requires the resolved directory theme target to be a single directory name by checking basename(assetFolderName) === assetFolderName.
  • writeFiles() computes each destination with joinPaths(root, relativePath) and rejects it unless isParentOf(root, filePath) is true.

The writeFiles() guard is intentionally shared: any FileTree write now stays inside the root passed by the caller.

This does not add symlink hardening or change zip install behavior.

Testing instructions

npm run format:uncommitted
npm exec nx run playground-blueprints:test:vite -- --testFiles=packages/playground/blueprints/src/tests/steps/install-theme.spec.ts
npm exec nx run php-wasm-node:test-group-5-asyncify -- --testFiles=write-files.spec.ts
npm exec nx run php-wasm-universal:typecheck
npm exec nx run playground-blueprints:typecheck
npm exec nx run php-wasm-node:typecheck
npm exec nx run playground-blueprints:lint
npm exec nx run php-wasm-universal:lint
npm exec nx run php-wasm-node:lint
git diff --check
@adamziel
adamziel force-pushed the add/theme-directory-path-safety branch from a902371 to cfd6731 Compare July 1, 2026 18:50
Base automatically changed from add/theme-directory-if-already-installed to trunk July 1, 2026 18:54
@adamziel
adamziel force-pushed the add/theme-directory-path-safety branch 2 times, most recently from 4c0b117 to ba288f1 Compare July 1, 2026 19:06
@adamziel adamziel changed the title [Blueprints] Reject unsafe directory theme paths Jul 1, 2026
@adamziel
adamziel force-pushed the add/theme-directory-path-safety branch from ba288f1 to 471a241 Compare July 1, 2026 19:13
@adamziel
adamziel force-pushed the add/theme-directory-path-safety branch from 471a241 to ee700b7 Compare July 1, 2026 19:23
@adamziel
adamziel marked this pull request as ready for review July 1, 2026 22:06
@adamziel
adamziel requested review from a team, bgrgicak and Copilot July 1, 2026 22:06

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Hardens directory-based theme installs and generic file-tree writes to prevent path traversal outside the intended target folder.

Changes:

  • Validates directory-theme target folder names to be a single directory segment.
  • Adds a root-boundary guard in writeFiles() to reject writes outside the provided root.
  • Adds regression tests for theme install and writeFiles() traversal cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/playground/blueprints/src/lib/steps/install-theme.ts Adds validation to keep directory theme installs confined to a single folder name.
packages/php-wasm/universal/src/lib/write-files.ts Enforces that each written path stays inside the caller-provided root directory.
packages/playground/blueprints/src/tests/steps/install-theme.spec.ts Adds tests covering invalid theme folder names and file paths.
packages/php-wasm/node/src/test/write-files.spec.ts Adds a test ensuring traversal paths are rejected by writeFiles().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 124 to +132
assetFolderName = targetFolderName || assetNiceName;
if (
!assetFolderName ||
basename(assetFolderName) !== assetFolderName
) {
throw new Error(
'Theme folder name must be a single directory name.'
);
}
Comment on lines +48 to +52
if (!isParentOf(root, filePath)) {
throw new Error(
'File paths must stay inside the target directory.'
);
}
expect(php.fileExists(expectedThemeIndexPhpPath)).toBe(true);
});

it('should reject directory theme names outside the themes directory', async () => {
@adamziel
adamziel merged commit fb36ebf into trunk Jul 1, 2026
55 checks passed
@adamziel
adamziel deleted the add/theme-directory-path-safety branch July 1, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment