Skip to content

Commit a54a991

Browse files
committed
feat(@angular/cli): add host check flags to ng serve
Two new flags are added to `ng serve`: - `--public-host` (aliased by `--live-reload-client): Specify the URL that the browser client will use. - `--disable-host-check`: Don't verify connected clients are part of allowed hosts. Setting `--disable-host-check` will output a warning: ``` WARNING Running a server with --disable-host-check is a security risk. See https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a for more information. ``` See #6070 for more context about this change. Fix #6070
1 parent 5a41c42 commit a54a991

File tree

8 files changed

+88
-14
lines changed

8 files changed

+88
-14
lines changed

docs/documentation/serve.md

+13-3
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,22 @@ All the build Options are available in serve, below are the additional options.
3939
</details>
4040

4141
<details>
42-
<summary>live-reload-client</summary>
42+
<summary>public-host</summary>
4343
<p>
44-
<code>--live-reload-client</code>
44+
<code>--public-host</code> (aliases: <code>--live-reload-client</code>)
4545
</p>
4646
<p>
47-
Specify the URL that the live reload browser client will use.
47+
Specify the URL that the browser client will use.
48+
</p>
49+
</details>
50+
51+
<details>
52+
<summary>disable-host-check</summary>
53+
<p>
54+
<code>--disable-host-check</code> <em>default value: false</em>
55+
</p>
56+
<p>
57+
Don't verify connected clients are part of allowed hosts.
4858
</p>
4959
</details>
5060

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
"walk-sync": "^0.3.1",
9696
"webpack": "~2.4.0",
9797
"webpack-dev-middleware": "^1.10.2",
98-
"webpack-dev-server": "~2.4.2",
98+
"webpack-dev-server": "~2.4.5",
9999
"webpack-merge": "^2.4.0",
100100
"zone.js": "^0.8.4"
101101
},

packages/@angular/cli/commands/serve.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export interface ServeTaskOptions extends BuildOptions {
2020
host?: string;
2121
proxyConfig?: string;
2222
liveReload?: boolean;
23-
liveReloadClient?: string;
23+
publicHost?: string;
24+
disableHostCheck?: boolean;
2425
ssl?: boolean;
2526
sslKey?: string;
2627
sslCert?: string;
@@ -84,9 +85,16 @@ export const baseServeCommandOptions: any = overrideOptions([
8485
description: 'Whether to reload the page on change, using live-reload.'
8586
},
8687
{
87-
name: 'live-reload-client',
88+
name: 'public-host',
8889
type: String,
89-
description: 'Specify the URL that the live reload browser client will use.'
90+
aliases: ['live-reload-client'],
91+
description: 'Specify the URL that the browser client will use.'
92+
},
93+
{
94+
name: 'disable-host-check',
95+
type: Boolean,
96+
default: false,
97+
description: 'Don\'t verify connected clients are part of allowed hosts.',
9098
},
9199
{
92100
name: 'hmr',

packages/@angular/cli/custom-typings.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ interface IWebpackDevServerConfigurationOptions {
2121
key?: string;
2222
cert?: string;
2323
overlay?: boolean | { errors: boolean, warnings: boolean };
24+
public?: string;
25+
disableHostCheck?: boolean;
2426
}

packages/@angular/cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"walk-sync": "^0.3.1",
8181
"webpack": "~2.4.0",
8282
"webpack-dev-middleware": "^1.10.2",
83-
"webpack-dev-server": "~2.4.2",
83+
"webpack-dev-server": "~2.4.5",
8484
"webpack-merge": "^2.4.0",
8585
"zone.js": "^0.8.4"
8686
},

packages/@angular/cli/tasks/serve.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,18 @@ export default Task.extend({
4949
hostname: serveTaskOptions.host === '0.0.0.0' ? 'localhost' : serveTaskOptions.host,
5050
port: serveTaskOptions.port.toString()
5151
});
52+
53+
if (serveTaskOptions.disableHostCheck) {
54+
ui.writeLine(oneLine`
55+
${chalk.yellow('WARNING')} Running a server with --disable-host-check is a security risk.
56+
See https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
57+
for more information.
58+
`);
59+
}
60+
5261
let clientAddress = serverAddress;
53-
if (serveTaskOptions.liveReloadClient) {
54-
const clientUrl = url.parse(serveTaskOptions.liveReloadClient);
62+
if (serveTaskOptions.publicHost) {
63+
const clientUrl = url.parse(serveTaskOptions.publicHost);
5564
// very basic sanity check
5665
if (!clientUrl.host) {
5766
return Promise.reject(new SilentError(`'live-reload-client' must be a full URL.`));
@@ -96,7 +105,7 @@ export default Task.extend({
96105
webpackConfig.plugins.unshift({
97106
apply: (compiler: any) => {
98107
compiler.plugin('after-environment', () => {
99-
compiler.watchFileSystem = { watch: () => {} };
108+
compiler.watchFileSystem = { watch: () => { } };
100109
});
101110
}
102111
});
@@ -153,7 +162,9 @@ export default Task.extend({
153162
errors: serveTaskOptions.target === 'development',
154163
warnings: false
155164
},
156-
contentBase: false
165+
contentBase: false,
166+
public: serveTaskOptions.publicHost,
167+
disableHostCheck: serveTaskOptions.disableHostCheck
157168
};
158169

159170
if (sslKey != null && sslCert != null) {
@@ -193,7 +204,7 @@ export default Task.extend({
193204
return reject(err);
194205
}
195206
if (serveTaskOptions.open) {
196-
opn(serverAddress);
207+
opn(serverAddress);
197208
}
198209
});
199210
})

tests/e2e/tests/misc/live-reload.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export default function () {
124124
// Serve with live reload client set to api should call api.
125125
.then(_ => silentExecAndWaitForOutputToMatch(
126126
'ng',
127-
['e2e', '--watch', `--live-reload-client=${apiUrl}`],
127+
['e2e', '--watch', `--public-host=${apiUrl}`],
128128
protractorGoodRegEx
129129
))
130130
.then(_ => wait(2000))

tests/e2e/tests/misc/public-host.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as os from 'os';
2+
import * as _ from 'lodash';
3+
4+
import { request } from '../../utils/http';
5+
import { killAllProcesses } from '../../utils/process';
6+
import { ngServe } from '../../utils/project';
7+
8+
export default function () {
9+
const firstLocalIp = _(os.networkInterfaces())
10+
.values()
11+
.flatten()
12+
.filter({ family: 'IPv4', internal: false })
13+
.map('address')
14+
.first();
15+
const publicHost = `${firstLocalIp}:4200`;
16+
const localAddress = `http://${publicHost}`;
17+
18+
return Promise.resolve()
19+
.then(() => ngServe('--host=0.0.0.0'))
20+
.then(() => request(localAddress))
21+
.then(body => {
22+
if (!body.match(/Invalid Host header/)) {
23+
throw new Error('Response does not match expected value.');
24+
}
25+
})
26+
.then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; })
27+
.then(() => ngServe('--host=0.0.0.0', `--public-host=${publicHost}`))
28+
.then(() => request(localAddress))
29+
.then(body => {
30+
if (!body.match(/<app-root><\/app-root>/)) {
31+
throw new Error('Response does not match expected value.');
32+
}
33+
})
34+
.then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; })
35+
.then(() => ngServe('--host=0.0.0.0', `--disable-host-check`))
36+
.then(() => request(localAddress))
37+
.then(body => {
38+
if (!body.match(/<app-root><\/app-root>/)) {
39+
throw new Error('Response does not match expected value.');
40+
}
41+
})
42+
.then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; });
43+
}

0 commit comments

Comments
 (0)