Skip to content

Commit 06beb0f

Browse files
committed
Merge remote-tracking branch 'origin/v21'
2 parents 8540cd3 + 923491b commit 06beb0f

31 files changed

+576
-600
lines changed

.github/workflows/ci-module.yml

+3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ name: ci
33
on:
44
push:
55
branches:
6+
- v21
67
- master
78
pull_request:
89
workflow_dispatch:
910

1011
jobs:
1112
test:
1213
uses: hapijs/.github/.github/workflows/ci-module.yml@master
14+
with:
15+
min-node-version: 14

API.md

+31-43
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ All options are optionals.
2626

2727
#### <a name="server.options.address" /> `server.options.address`
2828

29-
Default value: `'0.0.0.0'` (all available network interfaces).
29+
Default value: `'::'` if IPv6 is available, otherwise `'0.0.0.0'` (i.e. all available network interfaces).
3030

3131
Sets the hostname or IP address the server will listen on. If not configured, defaults to
3232
[`host`](#server.options.host) if present, otherwise to all available network interfaces. Set to
33-
`'127.0.0.1'` or `'localhost'` to restrict the server to only those coming from the same host.
33+
`'127.0.0.1'`, `'::1'`, or `'localhost'` to restrict the server to only those coming from the same host.
3434

3535
#### <a name="server.options.app" /> `server.options.app`
3636

@@ -1539,11 +1539,11 @@ async function example() {
15391539
const server = Hapi.server({ port: 80 });
15401540
server.event('test');
15411541
server.events.on('test', (update) => console.log(update));
1542-
await server.events.emit('test', 'hello');
1542+
await server.events.gauge('test', 'hello');
15431543
}
15441544
```
15451545

1546-
### <a name="server.events.emit()" /> `await server.events.emit(criteria, data)`
1546+
### <a name="server.events.emit()" /> `server.events.emit(criteria, data)`
15471547

15481548
Emits a custom application event to all the subscribed listeners where:
15491549

@@ -1569,7 +1569,7 @@ async function example() {
15691569
const server = Hapi.server({ port: 80 });
15701570
server.event('test');
15711571
server.events.on('test', (update) => console.log(update));
1572-
await server.events.emit('test', 'hello'); // await is optional
1572+
server.events.emit('test', 'hello');
15731573
}
15741574
```
15751575

@@ -1634,7 +1634,7 @@ async function example() {
16341634
const server = Hapi.server({ port: 80 });
16351635
server.event('test');
16361636
server.events.on('test', (update) => console.log(update));
1637-
await server.events.emit('test', 'hello');
1637+
server.events.emit('test', 'hello');
16381638
}
16391639
```
16401640

@@ -1652,8 +1652,8 @@ async function example() {
16521652
const server = Hapi.server({ port: 80 });
16531653
server.event('test');
16541654
server.events.once('test', (update) => console.log(update));
1655-
await server.events.emit('test', 'hello');
1656-
await server.events.emit('test', 'hello'); // Ignored
1655+
server.events.emit('test', 'hello');
1656+
server.events.emit('test', 'hello'); // Ignored
16571657
}
16581658
```
16591659

@@ -1671,11 +1671,17 @@ async function example() {
16711671
const server = Hapi.server({ port: 80 });
16721672
server.event('test');
16731673
const pending = server.events.once('test');
1674-
await server.events.emit('test', 'hello');
1674+
server.events.emit('test', 'hello');
16751675
const update = await pending;
16761676
}
16771677
```
16781678

1679+
### <a name="server.events.gauge()" /> `await server.events.gauge(criteria, data)`
1680+
1681+
Behaves identically to [`server.events.emit()`](#server.events.emit()), but also returns an array of the results of all the event listeners that run. The return value is that of `Promise.allSettled()`, where each item in the resulting array is `{ status: 'fulfilled', value }` in the case of a successful handler, or `{ status: 'rejected', reason }` in the case of a handler that throws.
1682+
1683+
Please note that system errors such as a `TypeError` are not handled specially, and it's recommended to scrutinize any rejections using something like [bounce](https://hapi.dev/module/bounce/).
1684+
16791685
### <a name="server.expose()" /> `server.expose(key, value, [options])`
16801686

16811687
Used within a plugin to expose a property via [`server.plugins[name]`](#server.plugins) where:
@@ -2895,6 +2901,9 @@ object with the following options:
28952901
- `credentials` - if `true`, allows user credentials to be sent
28962902
('Access-Control-Allow-Credentials'). Defaults to `false`.
28972903

2904+
- `preflightStatusCode` - the status code used for CORS preflight responses, either `200` or `204`.
2905+
Defaults to `200`.
2906+
28982907
### <a name="route.options.description" /> `route.options.description`
28992908

29002909
Default value: none.
@@ -2974,21 +2983,6 @@ string payload or escaping it after stringification. Supports the following:
29742983
- `escape` - calls [`Hoek.jsonEscape()`](https://hapi.dev/family/hoek/api/#escapejsonstring)
29752984
after conversion to JSON string. Defaults to `false`.
29762985

2977-
### <a name="route.options.jsonp" /> `route.options.jsonp`
2978-
2979-
Default value: none.
2980-
2981-
Enables JSONP support by setting the value to the query parameter name containing the function name
2982-
used to wrap the response payload.
2983-
2984-
For example, if the value is `'callback'`, a request comes in with `'callback=me'`, and the JSON
2985-
response is `'{ "a":"b" }'`, the payload will be `'me({ "a":"b" });'`. Cannot be used with stream
2986-
responses.
2987-
2988-
The 'Content-Type' response header is set to `'text/javascript'` and the 'X-Content-Type-Options'
2989-
response header is set to `'nosniff'`, and will override those headers even if explicitly set by
2990-
[`response.type()`](#response.type()).
2991-
29922986
### <a name="route.options.log" /> `route.options.log`
29932987

29942988
Default value: `{ collect: false }`.
@@ -3361,13 +3355,17 @@ following options:
33613355
otherwise this field is ignored. If `rule` is `'allow-from'` but `source` is unset, the
33623356
rule will be automatically changed to `'sameorigin'`.
33633357

3364-
- `xss` - boolean that controls the 'X-XSS-PROTECTION' header for Internet Explorer. Defaults to
3365-
`true` which sets the header to equal `'1; mode=block'`.
3366-
- Note: this setting can create a security vulnerability in versions of Internet Exploere below
3367-
8, as well as unpatched versions of IE8. See [here](https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/)
3368-
and [here](https://technet.microsoft.com/library/security/ms10-002) for more information. If
3369-
you actively support old versions of IE, it may be wise to explicitly set this flag to
3370-
`false`.
3358+
- `xss` - controls the 'X-XSS-Protection' header, where:
3359+
3360+
- `'disable'` - the header will be set to `'0'`. This is the default value.
3361+
- `'enable'` - the header will be set to `'1; mode=block'`.
3362+
- `false` - the header will be omitted.
3363+
3364+
Note: when enabled, this setting can create a security vulnerabilities in versions of Internet Explorer
3365+
below 8, unpatched versions of IE8, and browsers that employ an XSS filter/auditor. See
3366+
[here](https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/),
3367+
[here](https://technet.microsoft.com/library/security/ms10-002), and
3368+
[here](https://blog.innerht.ml/the-misunderstood-x-xss-protection/) for more information.
33713369

33723370
- `noOpen` - boolean controlling the 'X-Download-Options' header for Internet Explorer, preventing
33733371
downloads from executing in your context. Defaults to `true` setting the header to `'noopen'`.
@@ -3448,7 +3446,8 @@ Default value: `'error'` (return a Bad Request (400) error response).
34483446

34493447
A [`failAction` value](#lifecycle-failAction) which determines how to handle failed validations.
34503448
When set to a function, the `err` argument includes the type of validation error under
3451-
`err.output.payload.validation.source`.
3449+
`err.output.payload.validation.source`. The default error that would otherwise have been logged
3450+
or returned can be accessed under `err.data.defaultError`.
34523451

34533452
#### <a name="route.options.validate.headers" /> `route.options.validate.headers`
34543453

@@ -3616,19 +3615,12 @@ the same. The following is the complete list of steps a request can go through:
36163615
- [`request.route`](#request.route) is unassigned.
36173616
- [`request.url`](#request.url) can be `null` if the incoming request path is invalid.
36183617
- [`request.path`](#request.path) can be an invalid path.
3619-
- JSONP configuration is ignored for any response returned from the extension point since no
3620-
route is matched yet and the JSONP configuration is unavailable.
36213618

36223619
- _**Route lookup**_
36233620
- lookup based on `request.path` and `request.method`.
36243621
- skips to _**onPreResponse**_ if no route is found or if the path violates the HTTP
36253622
specification.
36263623

3627-
- _**JSONP processing**_
3628-
- based on the route [`jsonp`](#route.options.jsonp) option.
3629-
- parses JSONP parameter from [`request.query`](#request.query).
3630-
- skips to _**Response validation**_ on error.
3631-
36323624
- _**Cookies processing**_
36333625
- based on the route [`state`](#route.options.state) option.
36343626
- error handling based on [`failAction`](#route.options.state.failAction).
@@ -3663,10 +3655,6 @@ the same. The following is the complete list of steps a request can go through:
36633655
- based on the route [`validate.params`](#route.options.validate.params) option.
36643656
- error handling based on [`failAction`](#route.options.validate.failAction).
36653657

3666-
- _**JSONP cleanup**_
3667-
- based on the route [`jsonp`](#route.options.jsonp) option.
3668-
- remove the JSONP parameter from [`request.query`](#request.query).
3669-
36703658
- _**Query validation**_
36713659
- based on the route [`validate.query`](#route.options.validate.query) option.
36723660
- error handling based on [`failAction`](#route.options.validate.failAction).

LICENSE.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
Copyright (c) 2011-2020, Sideway Inc, and project contributors
2-
Copyright (c) 2011-2014, Walmart
3-
Copyright (c) 2011, Yahoo Inc.
1+
Copyright (c) 2011-2022, Project contributors
2+
Copyright (c) 2011-2020, Sideway Inc
3+
Copyright (c) 2011-2014, Walmart
4+
Copyright (c) 2011, Yahoo Inc.
45
All rights reserved.
56

67
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7-
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8-
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9-
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
8+
9+
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10+
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
11+
- The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
1012

1113
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

lib/auth.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ exports = module.exports = internals.Auth = class {
6767
Hoek.assert(typeof strategy.authenticate === 'function', 'Invalid scheme:', name, 'invalid authenticate() method');
6868
Hoek.assert(!strategy.payload || typeof strategy.payload === 'function', 'Invalid scheme:', name, 'invalid payload() method');
6969
Hoek.assert(!strategy.response || typeof strategy.response === 'function', 'Invalid scheme:', name, 'invalid response() method');
70-
strategy.options = strategy.options || {};
70+
strategy.options = strategy.options ?? {};
7171
Hoek.assert(strategy.payload || !strategy.options.payload, 'Cannot require payload validation without a payload method');
7272

7373
this.#strategies[name] = {
@@ -171,10 +171,10 @@ exports = module.exports = internals.Auth = class {
171171
options = Hoek.applyToDefaults(this.settings.default, options);
172172
}
173173

174-
path = path || 'default strategy';
175-
Hoek.assert(options.strategies && options.strategies.length, 'Missing authentication strategy:', path);
174+
path = path ?? 'default strategy';
175+
Hoek.assert(options.strategies?.length, 'Missing authentication strategy:', path);
176176

177-
options.mode = options.mode || 'required';
177+
options.mode = options.mode ?? 'required';
178178

179179
if (options.entity !== undefined || // Backwards compatibility with <= 11.x.x
180180
options.scope !== undefined) {
@@ -304,9 +304,7 @@ exports = module.exports = internals.Auth = class {
304304
_access(request, route) {
305305

306306
const config = this.lookup(route || request.route);
307-
if (!config ||
308-
!config.access) {
309-
307+
if (!config?.access) {
310308
return true;
311309
}
312310

@@ -389,7 +387,7 @@ exports = module.exports = internals.Auth = class {
389387
}
390388

391389
const config = auth.lookup(request.route);
392-
const setting = config.payload || (strategy.methods.options.payload ? 'required' : false);
390+
const setting = config.payload ?? (strategy.methods.options.payload ? 'required' : false);
393391
if (!setting) {
394392
return;
395393
}
@@ -451,13 +449,13 @@ internals.setupScope = function (access) {
451449
const prefix = value[0];
452450
const type = prefix === '+' ? 'required' : (prefix === '!' ? 'forbidden' : 'selection');
453451
const clean = type === 'selection' ? value : value.slice(1);
454-
scope[type] = scope[type] || [];
452+
scope[type] = scope[type] ?? [];
455453
scope[type].push(clean);
456454

457-
if ((!scope._hasParameters || !scope._hasParameters[type]) &&
455+
if ((!scope._hasParameters?.[type]) &&
458456
/{([^}]+)}/.test(clean)) {
459457

460-
scope._hasParameters = scope._hasParameters || {};
458+
scope._hasParameters = scope._hasParameters ?? {};
461459
scope._hasParameters[type] = true;
462460
}
463461
}
@@ -468,7 +466,7 @@ internals.setupScope = function (access) {
468466

469467
internals.validate = function (err, result, name, config, request, errors) { // err can be Boom, Error, or a valid response object
470468

471-
result = result || {};
469+
result = result ?? {};
472470
request.auth.isAuthenticated = !err;
473471

474472
if (err) {

lib/config.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const Os = require('os');
44

5+
const Somever = require('@hapi/somever');
56
const Validate = require('@hapi/validate');
67

78

@@ -38,6 +39,7 @@ exports.enable = function (options) {
3839
return settings;
3940
};
4041

42+
exports.versionMatch = (version, range) => Somever.match(version, range, { includePrerelease: true });
4143

4244
internals.access = Validate.object({
4345
entity: Validate.valid('user', 'app', 'any'),
@@ -109,7 +111,8 @@ internals.routeBase = Validate.object({
109111
additionalHeaders: Validate.array().items(Validate.string()).default([]),
110112
exposedHeaders: Validate.array().items(Validate.string()).default(['WWW-Authenticate', 'Server-Authorization']),
111113
additionalExposedHeaders: Validate.array().items(Validate.string()).default([]),
112-
credentials: Validate.boolean().when('origin', { is: 'ignore', then: false }).default(false)
114+
credentials: Validate.boolean().when('origin', { is: 'ignore', then: false }).default(false),
115+
preflightStatusCode: Validate.valid(200, 204).default(200)
113116
})
114117
.allow(false, true)
115118
.default(false),
@@ -134,7 +137,6 @@ internals.routeBase = Validate.object({
134137
escape: Validate.boolean().default(false)
135138
})
136139
.default(),
137-
jsonp: Validate.string(),
138140
log: Validate.object({
139141
collect: Validate.boolean().default(false)
140142
})
@@ -194,7 +196,7 @@ internals.routeBase = Validate.object({
194196
})
195197
])
196198
.default('deny'),
197-
xss: Validate.boolean().default(true),
199+
xss: Validate.valid('enabled', 'disabled', false).default('disabled'),
198200
noOpen: Validate.boolean().default(true),
199201
noSniff: Validate.boolean().default(true),
200202
referrer: Validate.alternatives([

0 commit comments

Comments
 (0)