Skip to content

Commit

Permalink
fix: migrate firewall to allowedHosts option (#3345)
Browse files Browse the repository at this point in the history
  • Loading branch information
anshumanv committed May 28, 2021
1 parent a84ecd2 commit 81e4e55
Show file tree
Hide file tree
Showing 17 changed files with 405 additions and 238 deletions.
12 changes: 3 additions & 9 deletions bin/cli-flags.js
Expand Up @@ -395,21 +395,15 @@ module.exports = {
negative: true,
},
{
name: 'firewall',
type: [Boolean, String],
name: 'allowed-hosts',
type: String,
configs: [
{
type: 'boolean',
},
{
type: 'string',
},
],
description:
'Enable firewall or set hosts that are allowed to access the dev server.',
negatedDescription: 'Disable firewall.',
description: 'Set hosts that are allowed to access the dev server.',
multiple: true,
negative: true,
},
{
name: 'watch-files',
Expand Down
2 changes: 1 addition & 1 deletion examples/cli/web-socket-url/webpack.config.js
Expand Up @@ -12,6 +12,6 @@ module.exports = setup({
client: {
webSocketURL: 'ws://localhost:8080',
},
firewall: false,
allowedHosts: 'all',
},
});
6 changes: 3 additions & 3 deletions lib/Server.js
Expand Up @@ -899,8 +899,8 @@ class Server {

checkHeaders(headers, headerToCheck) {
// allow user to opt out of this security check, at their own risk
// by explicitly disabling firewall
if (!this.options.firewall) {
// by explicitly enabling allowedHosts
if (this.options.allowedHosts === 'all') {
return true;
}

Expand Down Expand Up @@ -942,7 +942,7 @@ class Server {
return true;
}

const allowedHosts = this.options.firewall;
const allowedHosts = this.options.allowedHosts;

// always allow localhost host, for convenience
// allow if hostname is in allowedHosts
Expand Down
13 changes: 9 additions & 4 deletions lib/options.json
Expand Up @@ -337,20 +337,25 @@
"type": "object",
"description": "Provide options to webpack-dev-middleware which handles webpack assets. https://webpack.js.org/configuration/dev-server/#devserverdevmiddleware"
},
"firewall": {
"allowedHosts": {
"anyOf": [
{
"type": "boolean"
"enum": ["auto", "all"]
},
{
"type": "string",
"minLength": 1
},
{
"type": "array",
"items": {
"type": "string"
"type": "string",
"minLength": 1
},
"minItems": 1
}
],
"description": "Defines routes which are enabled by default, on by default and allows localhost. https://webpack.js.org/configuration/dev-server/#devserverfirewall"
"description": "Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts"
},
"headers": {
"anyOf": [
Expand Down
15 changes: 12 additions & 3 deletions lib/utils/normalizeOptions.js
Expand Up @@ -128,9 +128,18 @@ function normalizeOptions(compiler, options, logger) {

options.devMiddleware = options.devMiddleware || {};

if (typeof options.firewall === 'undefined') {
// firewall is enabled by default
options.firewall = true;
if (typeof options.allowedHosts === 'undefined') {
// allowedHosts allows some default hosts picked from
// `options.host` or `webSocketURL.host` and `localhost`
options.allowedHosts = 'auto';
}
if (
typeof options.allowedHosts === 'string' &&
options.allowedHosts !== 'auto' &&
options.allowedHosts !== 'all'
) {
// we store allowedHosts as array when supplied as string
options.allowedHosts = [options.allowedHosts];
}

if (typeof options.setupExitSignals === 'undefined') {
Expand Down
55 changes: 39 additions & 16 deletions test/__snapshots__/validate-options.test.js.snap.webpack4
@@ -1,5 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`options validate should throw an error on the "allowedHosts" option with '123' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "allowedHosts" option with 'false' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "allowedHosts" option with 'true' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "bonjour" option with '' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.bonjour should be one of these:
Expand Down Expand Up @@ -141,22 +180,6 @@ exports[`options validate should throw an error on the "devMiddleware" option wi
-> Provide options to webpack-dev-middleware which handles webpack assets. https://webpack.js.org/configuration/dev-server/#devserverdevmiddleware"
`;

exports[`options validate should throw an error on the "firewall" option with '' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.firewall should be one of these:
boolean | [string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost. https://webpack.js.org/configuration/dev-server/#devserverfirewall
Details:
* configuration.firewall should be a boolean.
* configuration.firewall should be an array:
[string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "firewall" option with '[]' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.firewall should be an non-empty array."
`;

exports[`options validate should throw an error on the "headers" option with '1' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.headers should be one of these:
Expand Down
55 changes: 39 additions & 16 deletions test/__snapshots__/validate-options.test.js.snap.webpack5
@@ -1,5 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`options validate should throw an error on the "allowedHosts" option with '123' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "allowedHosts" option with 'false' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "allowedHosts" option with 'true' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\" | non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost/value from the 'host' option/value from the 'client.webSocketURL' option. https://webpack.js.org/configuration/dev-server/#devserverallowedhosts
Details:
* configuration.allowedHosts should be one of these:
\\"auto\\" | \\"all\\"
* configuration.allowedHosts should be a non-empty string.
* configuration.allowedHosts should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "bonjour" option with '' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.bonjour should be one of these:
Expand Down Expand Up @@ -141,22 +180,6 @@ exports[`options validate should throw an error on the "devMiddleware" option wi
-> Provide options to webpack-dev-middleware which handles webpack assets. https://webpack.js.org/configuration/dev-server/#devserverdevmiddleware"
`;

exports[`options validate should throw an error on the "firewall" option with '' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.firewall should be one of these:
boolean | [string, ...] (should not have fewer than 1 item)
-> Defines routes which are enabled by default, on by default and allows localhost. https://webpack.js.org/configuration/dev-server/#devserverfirewall
Details:
* configuration.firewall should be a boolean.
* configuration.firewall should be an array:
[string, ...] (should not have fewer than 1 item)"
`;

exports[`options validate should throw an error on the "firewall" option with '[]' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.firewall should be an non-empty array."
`;

exports[`options validate should throw an error on the "headers" option with '1' value 1`] = `
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
- configuration.headers should be one of these:
Expand Down
5 changes: 2 additions & 3 deletions test/cli/__snapshots__/cli.test.js.snap.webpack4
Expand Up @@ -271,9 +271,8 @@ Options:
Page Applications.
--compress Enable gzip compression.
--no-compress Disable gzip compression.
--firewall [value...] Enable firewall or set hosts that are
allowed to access the dev server.
--no-firewall Disable firewall.
--allowed-hosts <value...> Set hosts that are allowed to access the dev
server.
--watch-files <value...> Watch static files for file changes.

Global options:
Expand Down
5 changes: 2 additions & 3 deletions test/cli/__snapshots__/cli.test.js.snap.webpack5
Expand Up @@ -272,9 +272,8 @@ Options:
Page Applications.
--compress Enable gzip compression.
--no-compress Disable gzip compression.
--firewall [value...] Enable firewall or set hosts that are
allowed to access the dev server.
--no-firewall Disable firewall.
--allowed-hosts <value...> Set hosts that are allowed to access the dev
server.
--watch-files <value...> Watch static files for file changes.

Global options:
Expand Down
43 changes: 43 additions & 0 deletions test/cli/cli.test.js
Expand Up @@ -654,6 +654,49 @@ describe('CLI', () => {
.catch(done);
});

describe('allowed-hosts', () => {
it('--allowed-hosts auto', (done) => {
testBin(['--allowed-hosts', 'auto'])
.then((output) => {
expect(output.exitCode).toEqual(0);
done();
})
.catch(done);
});

it('--allowed-hosts all', (done) => {
testBin(['--allowed-hosts', 'all'])
.then((output) => {
expect(output.exitCode).toEqual(0);
done();
})
.catch(done);
});

it('--allowed-hosts string', (done) => {
testBin(['--allowed-hosts', 'testhost.com'])
.then((output) => {
expect(output.exitCode).toEqual(0);
done();
})
.catch(done);
});

it('--allowed-hosts multiple', (done) => {
testBin([
'--allowed-hosts',
'testhost.com',
'--allowed-hosts',
'testhost1.com',
])
.then((output) => {
expect(output.exitCode).toEqual(0);
done();
})
.catch(done);
});
});

it('--no-static-serve-index', (done) => {
testBin('--no-static-serve-index')
.then((output) => {
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/web-socket-server-and-url.test.js
Expand Up @@ -65,7 +65,7 @@ for (const webSocketServerType of webSocketServerTypes) {
webSocketServer: webSocketServerType,
port: devServerPort,
host: devServerHost,
firewall: false,
allowedHosts: 'all',
hot: true,
};

Expand Down Expand Up @@ -132,7 +132,7 @@ for (const webSocketServerType of webSocketServerTypes) {
webSocketServer: webSocketServerType,
port: devServerPort,
host: devServerHost,
firewall: false,
allowedHosts: 'all',
hot: true,
};

Expand Down Expand Up @@ -204,7 +204,7 @@ for (const webSocketServerType of webSocketServerTypes) {
port: devServerPort,
host: devServerHost,
webSocketServer: webSocketServerType,
firewall: false,
allowedHosts: 'all',
hot: true,
static: true,
};
Expand Down

0 comments on commit 81e4e55

Please sign in to comment.