From 03002ef879258de39d3163bfdd974bda9e39ed89 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Wed, 3 May 2017 17:19:31 +0100 Subject: [PATCH] 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 --- docs/documentation/serve.md | 16 +++++++-- package.json | 2 +- packages/@angular/cli/commands/serve.ts | 14 ++++++-- packages/@angular/cli/custom-typings.d.ts | 2 ++ packages/@angular/cli/package.json | 2 +- packages/@angular/cli/tasks/serve.ts | 21 ++++++++--- tests/e2e/tests/misc/live-reload.ts | 2 +- tests/e2e/tests/misc/public-host.ts | 43 +++++++++++++++++++++++ 8 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 tests/e2e/tests/misc/public-host.ts diff --git a/docs/documentation/serve.md b/docs/documentation/serve.md index 0de32c40c72e..66a8b141b28a 100644 --- a/docs/documentation/serve.md +++ b/docs/documentation/serve.md @@ -39,12 +39,22 @@ All the build Options are available in serve, below are the additional options.
- live-reload-client + public-host

- --live-reload-client + --public (aliases: --live-reload-client)

- Specify the URL that the live reload browser client will use. + Specify the URL that the browser client will use. +

+
+ +
+ disable-host-check +

+ --disable-host-check default value: false +

+

+ Don't verify connected clients are part of allowed hosts.

diff --git a/package.json b/package.json index 433b3f6d569d..64aabba7109b 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "url-loader": "^0.5.7", "walk-sync": "^0.3.1", "webpack": "~2.4.0", - "webpack-dev-server": "~2.4.2", + "webpack-dev-server": "~2.4.5", "webpack-merge": "^2.4.0", "zone.js": "^0.8.4" }, diff --git a/packages/@angular/cli/commands/serve.ts b/packages/@angular/cli/commands/serve.ts index 01254c523145..3d3169f2ce1d 100644 --- a/packages/@angular/cli/commands/serve.ts +++ b/packages/@angular/cli/commands/serve.ts @@ -20,7 +20,8 @@ export interface ServeTaskOptions extends BuildOptions { host?: string; proxyConfig?: string; liveReload?: boolean; - liveReloadClient?: string; + publicHost?: string; + disableHostCheck?: boolean; ssl?: boolean; sslKey?: string; sslCert?: string; @@ -84,9 +85,16 @@ export const baseServeCommandOptions: any = overrideOptions([ description: 'Whether to reload the page on change, using live-reload.' }, { - name: 'live-reload-client', + name: 'public-host', type: String, - description: 'Specify the URL that the live reload browser client will use.' + aliases: ['live-reload-client'], + description: 'Specify the URL that the browser client will use.' + }, + { + name: 'disable-host-check', + type: Boolean, + default: false, + description: 'Don\'t verify connected clients are part of allowed hosts.', }, { name: 'hmr', diff --git a/packages/@angular/cli/custom-typings.d.ts b/packages/@angular/cli/custom-typings.d.ts index 3fdc301c3fc5..91c843700857 100644 --- a/packages/@angular/cli/custom-typings.d.ts +++ b/packages/@angular/cli/custom-typings.d.ts @@ -21,4 +21,6 @@ interface IWebpackDevServerConfigurationOptions { key?: string; cert?: string; overlay?: boolean | { errors: boolean, warnings: boolean }; + public?: string; + disableHostCheck?: boolean; } diff --git a/packages/@angular/cli/package.json b/packages/@angular/cli/package.json index 05d47cae2b84..252ec0f8edf3 100644 --- a/packages/@angular/cli/package.json +++ b/packages/@angular/cli/package.json @@ -81,7 +81,7 @@ "url-loader": "^0.5.7", "walk-sync": "^0.3.1", "webpack": "~2.4.0", - "webpack-dev-server": "~2.4.2", + "webpack-dev-server": "~2.4.5", "webpack-merge": "^2.4.0", "zone.js": "^0.8.4" }, diff --git a/packages/@angular/cli/tasks/serve.ts b/packages/@angular/cli/tasks/serve.ts index c91200a07a6d..1c964bb2d2d0 100644 --- a/packages/@angular/cli/tasks/serve.ts +++ b/packages/@angular/cli/tasks/serve.ts @@ -47,9 +47,18 @@ export default Task.extend({ hostname: serveTaskOptions.host, port: serveTaskOptions.port.toString() }); + + if (serveTaskOptions.disableHostCheck) { + ui.writeLine(oneLine` + ${chalk.yellow('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. + `); + } + let clientAddress = serverAddress; - if (serveTaskOptions.liveReloadClient) { - const clientUrl = url.parse(serveTaskOptions.liveReloadClient); + if (serveTaskOptions.publicHost) { + const clientUrl = url.parse(serveTaskOptions.publicHost); // very basic sanity check if (!clientUrl.host) { return Promise.reject(new SilentError(`'live-reload-client' must be a full URL.`)); @@ -94,7 +103,7 @@ export default Task.extend({ webpackConfig.plugins.unshift({ apply: (compiler: any) => { compiler.plugin('after-environment', () => { - compiler.watchFileSystem = { watch: () => {} }; + compiler.watchFileSystem = { watch: () => { } }; }); } }); @@ -151,7 +160,9 @@ export default Task.extend({ errors: serveTaskOptions.target === 'development', warnings: false }, - contentBase: false + contentBase: false, + public: serveTaskOptions.publicHost, + disableHostCheck: serveTaskOptions.disableHostCheck }; if (sslKey != null && sslCert != null) { @@ -185,7 +196,7 @@ export default Task.extend({ return reject(err); } if (serveTaskOptions.open) { - opn(serverAddress); + opn(serverAddress); } }); }) diff --git a/tests/e2e/tests/misc/live-reload.ts b/tests/e2e/tests/misc/live-reload.ts index e82b8c929a18..254b52e6bc6c 100644 --- a/tests/e2e/tests/misc/live-reload.ts +++ b/tests/e2e/tests/misc/live-reload.ts @@ -104,7 +104,7 @@ export default function () { // Serve with live reload client set to api should call api. .then(_ => silentExecAndWaitForOutputToMatch( 'ng', - ['e2e', '--watch', `--live-reload-client=${apiUrl}`], + ['e2e', '--watch', `--public-host=${apiUrl}`], protractorGoodRegEx )) .then(_ => wait(2000)) diff --git a/tests/e2e/tests/misc/public-host.ts b/tests/e2e/tests/misc/public-host.ts new file mode 100644 index 000000000000..49352955704b --- /dev/null +++ b/tests/e2e/tests/misc/public-host.ts @@ -0,0 +1,43 @@ +import * as os from 'os'; +import * as _ from 'lodash'; + +import { request } from '../../utils/http'; +import { killAllProcesses } from '../../utils/process'; +import { ngServe } from '../../utils/project'; + +export default function () { + const firstLocalIp = _(os.networkInterfaces()) + .values() + .flatten() + .filter({ family: 'IPv4', internal: false }) + .map('address') + .first(); + const publicHost = `${firstLocalIp}:4200`; + const localAddress = `http://${publicHost}`; + + return Promise.resolve() + .then(() => ngServe('--host=0.0.0.0')) + .then(() => request(localAddress)) + .then(body => { + if (!body.match(/Invalid Host header/)) { + throw new Error('Response does not match expected value.'); + } + }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) + .then(() => ngServe('--host=0.0.0.0', `--public-host=${publicHost}`)) + .then(() => request(localAddress)) + .then(body => { + if (!body.match(/Loading...<\/app-root>/)) { + throw new Error('Response does not match expected value.'); + } + }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) + .then(() => ngServe('--host=0.0.0.0', `--disable-host-check`)) + .then(() => request(localAddress)) + .then(body => { + if (!body.match(/Loading...<\/app-root>/)) { + throw new Error('Response does not match expected value.'); + } + }) + .then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; }); +}