-
Notifications
You must be signed in to change notification settings - Fork 11.9k
fix(@angular/build): assert that asset input paths are within workspace root #33222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| import path from 'node:path'; | ||
| import { glob } from 'tinyglobby'; | ||
| import { isSubDirectory } from './path'; | ||
|
|
||
| export async function resolveAssets( | ||
| entries: { | ||
|
|
@@ -25,7 +26,12 @@ export async function resolveAssets( | |
| const outputFiles: { source: string; destination: string }[] = []; | ||
|
|
||
| for (const entry of entries) { | ||
| if (!isSubDirectory(root, entry.input)) { | ||
| throw new Error(`The ${entry.input} asset path must be within the workspace root.`); | ||
| } | ||
|
|
||
|
Comment on lines
+29
to
+32
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be moved so that it can be checked on a file-by-file basis in the loop below. A glob pattern can bypass this check. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @alan-agius4, pardon my intrusion but it seems to me this change is not retro compatible and I don't see an option to disable these checks in normalize-asset-patterns.js and resolve-assets.js and this suddenly breaks a build. I have to pin version |
||
| const cwd = path.resolve(root, entry.input); | ||
|
|
||
| const files = await glob(entry.glob, { | ||
| cwd, | ||
| dot: true, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,59 +1,31 @@ | ||
| import assert from 'node:assert'; | ||
| import { randomUUID } from 'node:crypto'; | ||
| import { mkdir, rm, writeFile } from 'node:fs/promises'; | ||
| import { ngServe, updateJsonFile } from '../../../utils/project'; | ||
| import { ngServe } from '../../../utils/project'; | ||
| import { getGlobalVariable } from '../../../utils/env'; | ||
|
|
||
| export default async function () { | ||
| const outsideDirectoryName = `../outside-${randomUUID()}`; | ||
| const port = await ngServe(); | ||
|
|
||
| await updateJsonFile('angular.json', (json) => { | ||
| // Ensure assets located outside the workspace root work with the dev server | ||
| json.projects['test-project'].architect.build.options.assets.push({ | ||
| 'input': outsideDirectoryName, | ||
| 'glob': '**/*', | ||
| 'output': './outside', | ||
| }); | ||
| }); | ||
|
|
||
| await mkdir(outsideDirectoryName); | ||
| try { | ||
| await writeFile(`${outsideDirectoryName}/some-asset.xyz`, 'XYZ'); | ||
|
|
||
| const port = await ngServe(); | ||
|
|
||
| let response = await fetch(`http://localhost:${port}/favicon.ico`); | ||
| assert.strictEqual(response.status, 200, 'favicon.ico response should be ok'); | ||
|
|
||
| response = await fetch(`http://localhost:${port}/outside/some-asset.xyz`); | ||
| assert.strictEqual(response.status, 200, 'outside/some-asset.xyz response should be ok'); | ||
| assert.strictEqual(await response.text(), 'XYZ', 'outside/some-asset.xyz content is wrong'); | ||
| let response = await fetch(`http://localhost:${port}/favicon.ico`); | ||
| assert.strictEqual(response.status, 200, 'favicon.ico response should be ok'); | ||
|
|
||
| // A non-existent HTML file request with accept header should fallback to the index HTML | ||
| response = await fetch(`http://localhost:${port}/does-not-exist.html`, { | ||
| headers: { accept: 'text/html' }, | ||
| }); | ||
| assert.strictEqual( | ||
| response.status, | ||
| 200, | ||
| 'non-existent file response should fallback and be ok', | ||
| ); | ||
| assert.match( | ||
| await response.text(), | ||
| /<app-root/, | ||
| 'non-existent file response should fallback and contain html', | ||
| ); | ||
|
|
||
| // Vite will incorrectly fallback in all non-existent cases so skip last test case | ||
| // TODO: Remove conditional when Vite handles this case | ||
| if (getGlobalVariable('argv')['esbuild']) { | ||
| return; | ||
| } | ||
|
|
||
| // A non-existent file without an html accept header should not be found. | ||
| response = await fetch(`http://localhost:${port}/does-not-exist.png`); | ||
| assert.strictEqual(response.status, 404, 'non-existent file response should be not found'); | ||
| } finally { | ||
| await rm(outsideDirectoryName, { force: true, recursive: true }); | ||
| // A non-existent HTML file request with accept header should fallback to the index HTML | ||
| response = await fetch(`http://localhost:${port}/does-not-exist.html`, { | ||
| headers: { accept: 'text/html' }, | ||
| }); | ||
| assert.strictEqual(response.status, 200, 'non-existent file response should fallback and be ok'); | ||
| assert.match( | ||
| await response.text(), | ||
| /<app-root/, | ||
| 'non-existent file response should fallback and contain html', | ||
| ); | ||
|
|
||
| // Vite will incorrectly fallback in all non-existent cases so skip last test case | ||
| // TODO: Remove conditional when Vite handles this case | ||
| if (getGlobalVariable('argv')['esbuild']) { | ||
| return; | ||
| } | ||
|
|
||
| // A non-existent file without an html accept header should not be found. | ||
| response = await fetch(`http://localhost:${port}/does-not-exist.png`); | ||
| assert.strictEqual(response.status, 404, 'non-existent file response should be not found'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.