Skip to content

Commit fdddf2c

Browse files
committed
fix(@angular/build): update vite to version 5.4.14
Version update from 5.4.6 to address advisory GHSA-vg6x-rcgg-rjx6 Vite version 5.4.12+, which is now used by the Angular CLI with the `application`/`browser-esbuild` builders, contains a potentially breaking change for some development setups. Examples of such setups include those that use reverse proxies or custom host names during development. The change within a patch release was made by Vite to address a security vulnerability. For projects that directly access the development server via `localhost`, no changes should be needed. However, some development setups may now need to adjust the `allowedHosts` development server option. This option can include an array of host names that are allowed to communicate with the development server. The option sets the corresponding Vite option within the Angular CLI. For more information on the option and its specific behavior, please see the Vite documentation located here: https://vite.dev/config/server-options.html#server-allowedhosts The following is an example of the configuration option allowing `example.com`: ``` "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "allowedHosts": ["example.com"] }, ```
1 parent 0434eff commit fdddf2c

File tree

11 files changed

+207
-8
lines changed

11 files changed

+207
-8
lines changed

goldens/public-api/angular/build/index.api.md

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export enum BuildOutputFileType {
110110

111111
// @public
112112
export interface DevServerBuilderOptions {
113+
allowedHosts?: AllowedHosts;
113114
buildTarget: string;
114115
headers?: {
115116
[key: string]: string;

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@
201201
"undici": "6.19.7",
202202
"verdaccio": "5.32.1",
203203
"verdaccio-auth-memory": "^10.0.0",
204-
"vite": "5.4.6",
204+
"vite": "5.4.14",
205205
"watchpack": "2.4.1",
206206
"webpack": "5.94.0",
207207
"webpack-dev-middleware": "7.4.2",

packages/angular/build/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"rollup": "4.22.4",
4242
"sass": "1.77.6",
4343
"semver": "7.6.3",
44-
"vite": "5.4.6",
44+
"vite": "5.4.14",
4545
"watchpack": "2.4.1"
4646
},
4747
"peerDependencies": {

packages/angular/build/src/builders/dev-server/options.ts

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export async function normalizeOptions(
103103
sslCert,
104104
sslKey,
105105
prebundle,
106+
allowedHosts,
106107
} = options;
107108

108109
// Return all the normalized options
@@ -128,5 +129,6 @@ export async function normalizeOptions(
128129
// Prebundling defaults to true but requires caching to function
129130
prebundle: cacheOptions.enabled && !optimization.scripts && prebundle,
130131
inspect,
132+
allowedHosts: allowedHosts ? allowedHosts : [],
131133
};
132134
}

packages/angular/build/src/builders/dev-server/schema.json

+17
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@
3636
"type": "string",
3737
"description": "SSL certificate to use for serving HTTPS."
3838
},
39+
"allowedHosts": {
40+
"description": "The hosts that can access the development server. This option sets the Vite option of the same name. For further details: https://vite.dev/config/server-options.html#server-allowedhosts",
41+
"default": [],
42+
"oneOf": [
43+
{
44+
"type": "array",
45+
"description": "List of hosts that are allowed to access the development server.",
46+
"items": {
47+
"type": "string"
48+
}
49+
},
50+
{
51+
"type": "boolean",
52+
"description": "Indicates that all hosts are allowed. This is not recommended and a security risk."
53+
}
54+
]
55+
},
3956
"headers": {
4057
"type": "object",
4158
"description": "Custom HTTP headers to be added to all responses.",

packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts

+47-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
*/
88

99
import { lastValueFrom, mergeMap, take, timeout } from 'rxjs';
10-
import { URL } from 'url';
10+
import { get, IncomingMessage, RequestOptions } from 'node:http';
11+
import { text } from 'node:stream/consumers';
1112
import {
1213
BuilderHarness,
1314
BuilderHarnessExecutionOptions,
@@ -41,3 +42,48 @@ export async function executeOnceAndFetch<T>(
4142
),
4243
);
4344
}
45+
46+
/**
47+
* Executes the builder and then immediately performs a GET request
48+
* via the Node.js `http` builtin module. This is useful for cases
49+
* where the `fetch` API is limited such as testing different `Host`
50+
* header values with the development server.
51+
* The `fetch` based alternative is preferred otherwise.
52+
*
53+
* @param harness A builder harness instance.
54+
* @param url The URL string to get.
55+
* @param options An options object.
56+
*/
57+
export async function executeOnceAndGet<T>(
58+
harness: BuilderHarness<T>,
59+
url: string,
60+
options?: Partial<BuilderHarnessExecutionOptions> & { request?: RequestOptions },
61+
): Promise<BuilderHarnessExecutionResult & { response?: IncomingMessage; content?: string }> {
62+
return lastValueFrom(
63+
harness.execute().pipe(
64+
timeout(30_000),
65+
mergeMap(async (executionResult) => {
66+
let response = undefined;
67+
let content = undefined;
68+
if (executionResult.result?.success) {
69+
let baseUrl = `${executionResult.result.baseUrl}`;
70+
baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`;
71+
const resolvedUrl = new URL(url, baseUrl);
72+
73+
response = await new Promise<IncomingMessage>((resolve) =>
74+
get(resolvedUrl, options?.request ?? {}, resolve),
75+
);
76+
77+
if (response.statusCode === 200) {
78+
content = await text(response);
79+
}
80+
81+
response.resume();
82+
}
83+
84+
return { ...executionResult, response, content };
85+
}),
86+
take(1),
87+
),
88+
);
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { executeDevServer } from '../../index';
10+
import { executeOnceAndGet } from '../execute-fetch';
11+
import { describeServeBuilder } from '../jasmine-helpers';
12+
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
13+
14+
const FETCH_HEADERS = Object.freeze({ Host: 'example.com' });
15+
16+
describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
17+
describe('option: "allowedHosts"', () => {
18+
beforeEach(async () => {
19+
setupTarget(harness);
20+
21+
// Application code is not needed for these tests
22+
await harness.writeFile('src/main.ts', '');
23+
});
24+
25+
it('does not allow an invalid host when option is not present', async () => {
26+
harness.useTarget('serve', {
27+
...BASE_OPTIONS,
28+
});
29+
30+
const { result, response } = await executeOnceAndGet(harness, '/', {
31+
request: { headers: FETCH_HEADERS },
32+
});
33+
34+
expect(result?.success).toBeTrue();
35+
expect(response?.statusCode).toBe(403);
36+
});
37+
38+
it('does not allow an invalid host when option is an empty array', async () => {
39+
harness.useTarget('serve', {
40+
...BASE_OPTIONS,
41+
allowedHosts: [],
42+
});
43+
44+
const { result, response } = await executeOnceAndGet(harness, '/', {
45+
request: { headers: FETCH_HEADERS },
46+
});
47+
48+
expect(result?.success).toBeTrue();
49+
expect(response?.statusCode).toBe(403);
50+
});
51+
52+
it('allows a host when specified in the option', async () => {
53+
harness.useTarget('serve', {
54+
...BASE_OPTIONS,
55+
allowedHosts: ['example.com'],
56+
});
57+
58+
const { result, content } = await executeOnceAndGet(harness, '/', {
59+
request: { headers: FETCH_HEADERS },
60+
});
61+
62+
expect(result?.success).toBeTrue();
63+
expect(content).toContain('<title>');
64+
});
65+
66+
it('allows a host when option is true', async () => {
67+
harness.useTarget('serve', {
68+
...BASE_OPTIONS,
69+
allowedHosts: true,
70+
});
71+
72+
const { result, content } = await executeOnceAndGet(harness, '/', {
73+
request: { headers: FETCH_HEADERS },
74+
});
75+
76+
expect(result?.success).toBeTrue();
77+
expect(content).toContain('<title>');
78+
});
79+
});
80+
});

packages/angular/build/src/builders/dev-server/vite-server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ export async function setupServer(
551551
strictPort: true,
552552
host: serverOptions.host,
553553
open: serverOptions.open,
554+
allowedHosts: serverOptions.allowedHosts,
554555
headers: serverOptions.headers,
555556
proxy,
556557
cors: {

packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,21 @@ export function execute(
8585
);
8686
}
8787

88+
// New build system uses Vite's allowedHost option convention of true for disabling host checks
89+
if (normalizedOptions.disableHostCheck) {
90+
(normalizedOptions as unknown as { allowedHosts: true }).allowedHosts = true;
91+
} else {
92+
normalizedOptions.allowedHosts ??= [];
93+
}
94+
8895
return defer(() =>
8996
Promise.all([import('@angular/build/private'), import('../browser-esbuild')]),
9097
).pipe(
9198
switchMap(([{ serveWithVite, buildApplicationInternal }, { convertBrowserOptions }]) =>
9299
serveWithVite(
93-
normalizedOptions,
100+
normalizedOptions as typeof normalizedOptions & {
101+
allowedHosts: true | string[];
102+
},
94103
builderName,
95104
(options, context, codePlugins) => {
96105
return builderName === '@angular-devkit/build-angular:browser-esbuild'

packages/angular_devkit/build_angular/src/builders/dev-server/schema.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
},
7474
"allowedHosts": {
7575
"type": "array",
76-
"description": "List of hosts that are allowed to access the dev server. This option has no effect when using the 'application' or other esbuild-based builders.",
76+
"description": "List of hosts that are allowed to access the dev server.",
7777
"default": [],
7878
"items": {
7979
"type": "string"
@@ -85,7 +85,7 @@
8585
},
8686
"disableHostCheck": {
8787
"type": "boolean",
88-
"description": "Don't verify connected clients are part of allowed hosts. This option has no effect when using the 'application' or other esbuild-based builders.",
88+
"description": "Don't verify connected clients are part of allowed hosts.",
8989
"default": false
9090
},
9191
"hmr": {

yarn.lock

+45-2
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ __metadata:
401401
rollup: "npm:4.22.4"
402402
sass: "npm:1.77.6"
403403
semver: "npm:7.6.3"
404-
vite: "npm:5.4.6"
404+
vite: "npm:5.4.14"
405405
watchpack: "npm:2.4.1"
406406
peerDependencies:
407407
"@angular/compiler-cli": ^18.0.0
@@ -805,7 +805,7 @@ __metadata:
805805
undici: "npm:6.19.7"
806806
verdaccio: "npm:5.32.1"
807807
verdaccio-auth-memory: "npm:^10.0.0"
808-
vite: "npm:5.4.6"
808+
vite: "npm:5.4.14"
809809
watchpack: "npm:2.4.1"
810810
webpack: "npm:5.94.0"
811811
webpack-dev-middleware: "npm:7.4.2"
@@ -18053,6 +18053,49 @@ __metadata:
1805318053
languageName: node
1805418054
linkType: hard
1805518055

18056+
"vite@npm:5.4.14":
18057+
version: 5.4.14
18058+
resolution: "vite@npm:5.4.14"
18059+
dependencies:
18060+
esbuild: "npm:^0.21.3"
18061+
fsevents: "npm:~2.3.3"
18062+
postcss: "npm:^8.4.43"
18063+
rollup: "npm:^4.20.0"
18064+
peerDependencies:
18065+
"@types/node": ^18.0.0 || >=20.0.0
18066+
less: "*"
18067+
lightningcss: ^1.21.0
18068+
sass: "*"
18069+
sass-embedded: "*"
18070+
stylus: "*"
18071+
sugarss: "*"
18072+
terser: ^5.4.0
18073+
dependenciesMeta:
18074+
fsevents:
18075+
optional: true
18076+
peerDependenciesMeta:
18077+
"@types/node":
18078+
optional: true
18079+
less:
18080+
optional: true
18081+
lightningcss:
18082+
optional: true
18083+
sass:
18084+
optional: true
18085+
sass-embedded:
18086+
optional: true
18087+
stylus:
18088+
optional: true
18089+
sugarss:
18090+
optional: true
18091+
terser:
18092+
optional: true
18093+
bin:
18094+
vite: bin/vite.js
18095+
checksum: 10c0/8842933bd70ca6a98489a0bb9c8464bec373de00f9a97c8c7a4e64b24d15c88bfaa8c1acb38a68c3e5eb49072ffbccb146842c2d4edcdd036a9802964cffe3d1
18096+
languageName: node
18097+
linkType: hard
18098+
1805618099
"vite@npm:5.4.6":
1805718100
version: 5.4.6
1805818101
resolution: "vite@npm:5.4.6"

0 commit comments

Comments
 (0)