Commit 166641d
committed
fix(@angular/build): reject outputPath that escapes workspace root
Prevent a malicious angular.json from setting outputPath outside the
workspace root (e.g. ".."), which caused the default application builder
to recursively delete the workspace parent directory and sibling content
before writing build output there.
**Current behavior:**
normalizeOptions() resolves the user-supplied outputPath (e.g. "..") relative
to workspaceRoot without validating that the result stays inside the workspace.
deleteOutputDir() only rejects a path that is exactly equal to the workspace
root; it accepts any other path, including ancestors or siblings.
A malicious angular.json with build.options.outputPath set to ".." causes a
default ng build to:
1. Resolve the output base to the workspace's parent directory.
2. Recursively delete every entry under that parent (including the workspace
itself and any sibling files/directories) as part of normal pre-build cleanup.
3. Write browser build artefacts into the parent directory.
4. Crash with ENOENT when it later tries to read assets from the now-deleted
workspace.
Severity: High - a single ng build can destroy the victim workspace and
writable sibling content, then write build output into an attacker-chosen
parent directory.
**New behavior:**
Two defence-in-depth guards are added:
1. Early validation in normalizeOptions()
(packages/angular/build/src/builders/application/options.ts)
After resolving the output base, the function now checks that it is a strict
descendant of workspaceRoot. If not, it throws immediately before any
filesystem work begins:
Error: Output path '<resolved>' must be inside the project root
directory '<workspaceRoot>'.
2. Boundary check in deleteOutputDir()
(packages/angular/build/src/utils/delete-output-dir.ts)
The deletion helper now rejects any outputPath whose resolved form is not a
strict descendant of the workspace root (previously it only rejected an exact
match). This acts as a last-resort guard even if upstream validation is
bypassed:
Error: Output path '<resolved>' MUST be inside the project root '<root>'.
**Breaking change:** None.
outputPath values that already point inside the workspace are unaffected. Only
values that resolve to a path at or above workspaceRoot are now rejected with a
clear error, which was never a supported or intended configuration.
Validated with the PoC in
security-reports/002-output-path-outside-workspace-deletion/. Before the fix
the workspace was deleted and the build wrote output into the parent directory.
After the fix the build aborts immediately with the new error message and
nothing outside the workspace is touched.
Fixes security report 002-output-path-outside-workspace-deletion.1 parent 0f2342c commit 166641d
File tree
2 files changed
+22
-6
lines changed- packages/angular/build/src
- builders/application
- utils
2 files changed
+22
-6
lines changedLines changed: 13 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
308 | 308 | | |
309 | 309 | | |
310 | 310 | | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
311 | 323 | | |
312 | 324 | | |
313 | 325 | | |
314 | 326 | | |
315 | 327 | | |
316 | | - | |
317 | | - | |
318 | | - | |
| 328 | + | |
319 | 329 | | |
320 | 330 | | |
321 | 331 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | | - | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
24 | 30 | | |
25 | 31 | | |
26 | 32 | | |
| |||
0 commit comments