Skip to content

fix: reject root directory path traversal in builds worker#12460

Draft
abnegate wants to merge 1 commit into
1.9.xfrom
fix-builds-root-directory-traversal
Draft

fix: reject root directory path traversal in builds worker#12460
abnegate wants to merge 1 commit into
1.9.xfrom
fix-builds-root-directory-traversal

Conversation

@abnegate
Copy link
Copy Markdown
Member

@abnegate abnegate commented Jun 2, 2026

Summary

Reapplies the path-traversal protection on build root directories that was reverted in 6ed54706b3 ("Restore original ./ stripping", part of #12450) — this time without the regression that took builds down.

Background

  • The original hardening (8e02beff58, refactor: tighten internal validation paths #12413) replaced rtrim('/')→ltrim('.')→ltrim('/') with trim('/') + a .. rejection.
  • trim($x, '/') strips slashes but not dots, so the leading-dot normalization was lost: ./ resolved to . and ./src stayed ./src. Deployments/templates that store providerRootDirectory: "./" then pointed the build at the wrong directory → empty source → npm ENOENT ... package.json. All Sites/template builds broke and the change was reverted, which also removed the .. rejection — so the traversal weakness is live again on 1.9.x.

This change

Extracts a single Builds::normalizeRootDirectory() used by all three build paths (template, VCS, VCS+template) that:

  1. Keeps the original normalization — rtrim('/'), ltrim('.'), ltrim('/') — so ./'' and ./srcsrc (no build regression).
  2. Adds the .. rejection after normalization, so a single leading ./ collapses before the check while any residual/embedded .. segment is rejected.

Invariant: the helper can never return a value containing a .. segment or an absolute path, so a crafted providerRootDirectory can't escape the build directory when interpolated into the downstream mkdir/rsync/tar/clone paths.

input result
./, ., .. '' (repo root)
./src, /src/, src/ src
../etc etc (single ../ sandboxed in-tree)
../../etc/passwd rejected
foo/../bar, foo/.. rejected

Test plan

  • New unit test tests/unit/Platform/Modules/Functions/Workers/BuildsTest.php (16 cases) covering the normalize/reject matrix — OK
  • composer lint (Pint) — passed
  • PHPStan level 4 on the changed file — no errors
  • CI: Sites + Functions e2e green on 1.9.x

🤖 Generated with Claude Code

Reapplies the ".." rejection that was reverted in 6ed5470 to restore
stable builds. The earlier hardening switched to trim('/'), which dropped
the leading-dot normalization, so "./" roots resolved to "." and broke
every VCS/template build (empty source, npm ENOENT on package.json).

This keeps the original rtrim/ltrim normalization ("./" -> "", "./src" ->
"src") and layers the traversal check on top: a single leading "./" is
collapsed before the check, while deeper or embedded ".." segments are
rejected. Extracts the three duplicated blocks into
Builds::normalizeRootDirectory() and adds unit coverage for the matrix.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

✨ Benchmark results

Comparing 1.9.x (before) to fix-builds-root-directory-traversal (after).

Before

Scenario P50 (ms) P95 (ms) Requests RPS
API total 14.62 136.3 185 33.85
Account 24.77 174.74 35 7
TablesDB 14.19 23.98 35 8.37
Storage 12.37 53.81 75 17.83
Functions 21.02 30.6 40 9.69

After

Scenario P50 (ms) P95 (ms) Requests RPS
API total 14.99 140.98 185 33.02
Account 25.26 196.95 35 6.84
TablesDB 14.23 19.83 35 8.17
Storage 11.89 55.4 75 17.2
Functions 22.88 34.36 40 9.28

Delta

Scenario P95 delta (ms)
API total +4.68
Account +22.21
TablesDB -4.15
Storage +1.59
Functions +3.76
Top API waits
API request Max wait (ms)
account.sessions.email.create 674.71
account.create 196.7
account.password.update 162.63

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🔄 PHP-Retry Summary

Flaky tests detected across commits:

Commit 5593b54 - 2 flaky tests
Test Retries Total Time Details
LegacyConsoleClientTest::testCreatedBetween 1 240.72s Logs
LegacyCustomClientTest::testEnforceCollectionPermissions 1 296ms Logs

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