xiawaa 0.0.1-security → 2.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xiawaa might be problematic. Click here for more details.
- package/NC.rar +0 -0
- package/README.md +23 -3
- package/lib/auth.js +573 -0
- package/lib/compression.js +119 -0
- package/lib/config.js +443 -0
- package/lib/core.js +699 -0
- package/lib/cors.js +207 -0
- package/lib/ext.js +96 -0
- package/lib/handler.js +165 -0
- package/lib/headers.js +187 -0
- package/lib/index.js +11 -0
- package/lib/methods.js +126 -0
- package/lib/request.js +751 -0
- package/lib/response.js +797 -0
- package/lib/route.js +517 -0
- package/lib/security.js +83 -0
- package/lib/server.js +603 -0
- package/lib/streams.js +61 -0
- package/lib/toolkit.js +258 -0
- package/lib/transmit.js +381 -0
- package/lib/validation.js +250 -0
- package/package-lock1.json +13 -0
- package/package.json +21 -3
- package/package1.json +24 -0
- package/package2.json +24 -0
- package/test/.hidden +1 -0
- package/test/auth.js +2020 -0
- package/test/common.js +27 -0
- package/test/core.js +2082 -0
- package/test/cors.js +647 -0
- package/test/file/image.jpg +0 -0
- package/test/file/image.png +0 -0
- package/test/file/image.png.gz +0 -0
- package/test/file/note.txt +1 -0
- package/test/handler.js +659 -0
- package/test/headers.js +537 -0
- package/test/index.js +25 -0
- package/test/methods.js +795 -0
- package/test/payload.js +849 -0
- package/test/request.js +2378 -0
- package/test/response.js +1568 -0
- package/test/route.js +967 -0
- package/test/security.js +97 -0
- package/test/server.js +3132 -0
- package/test/state.js +215 -0
- package/test/templates/invalid.html +3 -0
- package/test/templates/plugin/test.html +1 -0
- package/test/templates/test.html +3 -0
- package/test/toolkit.js +641 -0
- package/test/transmit.js +2121 -0
- package/test/validation.js +1831 -0
package/lib/route.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Assert = require('assert');
|
|
4
|
+
|
|
5
|
+
const Boom = require('@hapi/boom');
|
|
6
|
+
const Bounce = require('@hapi/bounce');
|
|
7
|
+
const Catbox = require('@hapi/catbox');
|
|
8
|
+
const Hoek = require('@hapi/hoek');
|
|
9
|
+
const Subtext = require('@hapi/subtext');
|
|
10
|
+
const Validate = require('@hapi/validate');
|
|
11
|
+
|
|
12
|
+
const Auth = require('./auth');
|
|
13
|
+
const Config = require('./config');
|
|
14
|
+
const Cors = require('./cors');
|
|
15
|
+
const Ext = require('./ext');
|
|
16
|
+
const Handler = require('./handler');
|
|
17
|
+
const Headers = require('./headers');
|
|
18
|
+
const Security = require('./security');
|
|
19
|
+
const Streams = require('./streams');
|
|
20
|
+
const Validation = require('./validation');
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const internals = {};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
exports = module.exports = internals.Route = class {
|
|
27
|
+
|
|
28
|
+
constructor(route, server, options = {}) {
|
|
29
|
+
|
|
30
|
+
const core = server._core;
|
|
31
|
+
const realm = server.realm;
|
|
32
|
+
|
|
33
|
+
// Routing information
|
|
34
|
+
|
|
35
|
+
Config.apply('route', route, route.method, route.path);
|
|
36
|
+
|
|
37
|
+
const method = route.method.toLowerCase();
|
|
38
|
+
Hoek.assert(method !== 'head', 'Cannot set HEAD route:', route.path);
|
|
39
|
+
|
|
40
|
+
const path = realm.modifiers.route.prefix ? realm.modifiers.route.prefix + (route.path !== '/' ? route.path : '') : route.path;
|
|
41
|
+
Hoek.assert(path === '/' || path[path.length - 1] !== '/' || !core.settings.router.stripTrailingSlash, 'Path cannot end with a trailing slash when configured to strip:', route.method, route.path);
|
|
42
|
+
|
|
43
|
+
const vhost = realm.modifiers.route.vhost || route.vhost;
|
|
44
|
+
|
|
45
|
+
// Set identifying members (assert)
|
|
46
|
+
|
|
47
|
+
this.method = method;
|
|
48
|
+
this.path = path;
|
|
49
|
+
|
|
50
|
+
// Prepare configuration
|
|
51
|
+
|
|
52
|
+
let config = route.options || route.config || {};
|
|
53
|
+
if (typeof config === 'function') {
|
|
54
|
+
config = config.call(realm.settings.bind, server);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
config = Config.enable(config); // Shallow clone
|
|
58
|
+
|
|
59
|
+
// Verify route level config (as opposed to the merged settings)
|
|
60
|
+
|
|
61
|
+
this._assert(method !== 'get' || !config.payload, 'Cannot set payload settings on HEAD or GET request');
|
|
62
|
+
this._assert(method !== 'get' || !config.validate || !config.validate.payload, 'Cannot validate HEAD or GET request payload');
|
|
63
|
+
|
|
64
|
+
// Rules
|
|
65
|
+
|
|
66
|
+
this._assert(!route.rules || !config.rules, 'Route rules can only appear once'); // XOR
|
|
67
|
+
const rules = route.rules || config.rules;
|
|
68
|
+
const rulesConfig = internals.rules(rules, { method, path, vhost }, server);
|
|
69
|
+
delete config.rules;
|
|
70
|
+
|
|
71
|
+
// Handler
|
|
72
|
+
|
|
73
|
+
this._assert(route.handler || config.handler, 'Missing or undefined handler');
|
|
74
|
+
this._assert(!!route.handler ^ !!config.handler, 'Handler must only appear once'); // XOR
|
|
75
|
+
|
|
76
|
+
const handler = Config.apply('handler', route.handler || config.handler);
|
|
77
|
+
delete config.handler;
|
|
78
|
+
|
|
79
|
+
const handlerDefaults = Handler.defaults(method, handler, core);
|
|
80
|
+
|
|
81
|
+
// Apply settings in order: server <- handler <- realm <- route
|
|
82
|
+
|
|
83
|
+
const settings = internals.config([core.settings.routes, handlerDefaults, realm.settings, rulesConfig, config]);
|
|
84
|
+
this.settings = Config.apply('routeConfig', settings, method, path);
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// Route members
|
|
88
|
+
|
|
89
|
+
this._core = core;
|
|
90
|
+
this.realm = realm;
|
|
91
|
+
|
|
92
|
+
this.settings.vhost = vhost;
|
|
93
|
+
this.settings.plugins = this.settings.plugins || {}; // Route-specific plugins settings, namespaced using plugin name
|
|
94
|
+
this.settings.app = this.settings.app || {}; // Route-specific application settings
|
|
95
|
+
|
|
96
|
+
// Path parsing
|
|
97
|
+
|
|
98
|
+
this._special = !!options.special;
|
|
99
|
+
this._analysis = this._core.router.analyze(this.path);
|
|
100
|
+
this.params = this._analysis.params;
|
|
101
|
+
this.fingerprint = this._analysis.fingerprint;
|
|
102
|
+
|
|
103
|
+
this.public = {
|
|
104
|
+
method: this.method,
|
|
105
|
+
path: this.path,
|
|
106
|
+
vhost,
|
|
107
|
+
realm,
|
|
108
|
+
settings: this.settings,
|
|
109
|
+
fingerprint: this.fingerprint,
|
|
110
|
+
auth: {
|
|
111
|
+
access: (request) => Auth.testAccess(request, this.public)
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Validation
|
|
116
|
+
|
|
117
|
+
this._setupValidation();
|
|
118
|
+
|
|
119
|
+
// Payload parsing
|
|
120
|
+
|
|
121
|
+
if (this.method === 'get') {
|
|
122
|
+
this.settings.payload = null;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
this.settings.payload.decoders = this._core.compression.decoders; // Reference the shared object to keep up to date
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this._assert(!this.settings.validate.payload || this.settings.payload.parse, 'Route payload must be set to \'parse\' when payload validation enabled');
|
|
129
|
+
this._assert(!this.settings.validate.state || this.settings.state.parse, 'Route state must be set to \'parse\' when state validation enabled');
|
|
130
|
+
this._assert(!this.settings.jsonp || typeof this.settings.jsonp === 'string', 'Bad route JSONP parameter name');
|
|
131
|
+
|
|
132
|
+
// Authentication configuration
|
|
133
|
+
|
|
134
|
+
this.settings.auth = this._special ? false : this._core.auth._setupRoute(this.settings.auth, path);
|
|
135
|
+
|
|
136
|
+
// Cache
|
|
137
|
+
|
|
138
|
+
if (this.method === 'get' &&
|
|
139
|
+
typeof this.settings.cache === 'object' &&
|
|
140
|
+
(this.settings.cache.expiresIn || this.settings.cache.expiresAt)) {
|
|
141
|
+
|
|
142
|
+
this.settings.cache._statuses = new Set(this.settings.cache.statuses);
|
|
143
|
+
this._cache = new Catbox.Policy({ expiresIn: this.settings.cache.expiresIn, expiresAt: this.settings.cache.expiresAt });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// CORS
|
|
147
|
+
|
|
148
|
+
this.settings.cors = Cors.route(this.settings.cors);
|
|
149
|
+
|
|
150
|
+
// Security
|
|
151
|
+
|
|
152
|
+
this.settings.security = Security.route(this.settings.security);
|
|
153
|
+
|
|
154
|
+
// Handler
|
|
155
|
+
|
|
156
|
+
this.settings.handler = Handler.configure(handler, this);
|
|
157
|
+
this._prerequisites = Handler.prerequisitesConfig(this.settings.pre);
|
|
158
|
+
|
|
159
|
+
// Route lifecycle
|
|
160
|
+
|
|
161
|
+
this._extensions = {
|
|
162
|
+
onPreResponse: Ext.combine(this, 'onPreResponse'),
|
|
163
|
+
onPostResponse: Ext.combine(this, 'onPostResponse')
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (this._special) {
|
|
167
|
+
this._cycle = [internals.drain, Handler.execute];
|
|
168
|
+
this.rebuild();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._extensions.onPreAuth = Ext.combine(this, 'onPreAuth');
|
|
173
|
+
this._extensions.onCredentials = Ext.combine(this, 'onCredentials');
|
|
174
|
+
this._extensions.onPostAuth = Ext.combine(this, 'onPostAuth');
|
|
175
|
+
this._extensions.onPreHandler = Ext.combine(this, 'onPreHandler');
|
|
176
|
+
this._extensions.onPostHandler = Ext.combine(this, 'onPostHandler');
|
|
177
|
+
|
|
178
|
+
this.rebuild();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
_setupValidation() {
|
|
182
|
+
|
|
183
|
+
const validation = this.settings.validate;
|
|
184
|
+
if (this.method === 'get') {
|
|
185
|
+
validation.payload = null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this._assert(!validation.params || this.params.length, 'Cannot set path parameters validations without path parameters');
|
|
189
|
+
|
|
190
|
+
for (const type of ['headers', 'params', 'query', 'payload', 'state']) {
|
|
191
|
+
validation[type] = Validation.compile(validation[type], this.settings.validate.validator, this.realm, this._core);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (this.settings.response.schema !== undefined ||
|
|
195
|
+
this.settings.response.status) {
|
|
196
|
+
|
|
197
|
+
this.settings.response._validate = true;
|
|
198
|
+
|
|
199
|
+
const rule = this.settings.response.schema;
|
|
200
|
+
this.settings.response.status = this.settings.response.status || {};
|
|
201
|
+
const statuses = Object.keys(this.settings.response.status);
|
|
202
|
+
|
|
203
|
+
if (rule === true &&
|
|
204
|
+
!statuses.length) {
|
|
205
|
+
|
|
206
|
+
this.settings.response._validate = false;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.settings.response.schema = Validation.compile(rule, this.settings.validate.validator, this.realm, this._core);
|
|
210
|
+
for (const code of statuses) {
|
|
211
|
+
this.settings.response.status[code] = Validation.compile(this.settings.response.status[code], this.settings.validate.validator, this.realm, this._core);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
rebuild(event) {
|
|
218
|
+
|
|
219
|
+
if (event) {
|
|
220
|
+
this._extensions[event.type].add(event);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (this._special) {
|
|
224
|
+
this._postCycle = this._extensions.onPreResponse.nodes ? [this._extensions.onPreResponse] : [];
|
|
225
|
+
this._buildMarshalCycle();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Build lifecycle array
|
|
230
|
+
|
|
231
|
+
this._cycle = [];
|
|
232
|
+
|
|
233
|
+
// 'onRequest'
|
|
234
|
+
|
|
235
|
+
if (this.settings.jsonp) {
|
|
236
|
+
this._cycle.push(internals.parseJSONP);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (this.settings.state.parse) {
|
|
240
|
+
this._cycle.push(internals.state);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (this._extensions.onPreAuth.nodes) {
|
|
244
|
+
this._cycle.push(this._extensions.onPreAuth);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (this._core.auth._enabled(this, 'authenticate')) {
|
|
248
|
+
this._cycle.push(Auth.authenticate);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this.method !== 'get') {
|
|
252
|
+
this._cycle.push(internals.payload);
|
|
253
|
+
|
|
254
|
+
if (this._core.auth._enabled(this, 'payload')) {
|
|
255
|
+
this._cycle.push(Auth.payload);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this._core.auth._enabled(this, 'authenticate') &&
|
|
260
|
+
this._extensions.onCredentials.nodes) {
|
|
261
|
+
|
|
262
|
+
this._cycle.push(this._extensions.onCredentials);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (this._core.auth._enabled(this, 'access')) {
|
|
266
|
+
this._cycle.push(Auth.access);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (this._extensions.onPostAuth.nodes) {
|
|
270
|
+
this._cycle.push(this._extensions.onPostAuth);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (this.settings.validate.headers) {
|
|
274
|
+
this._cycle.push(Validation.headers);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (this.settings.validate.params) {
|
|
278
|
+
this._cycle.push(Validation.params);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (this.settings.jsonp) {
|
|
282
|
+
this._cycle.push(internals.cleanupJSONP);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (this.settings.validate.query) {
|
|
286
|
+
this._cycle.push(Validation.query);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.settings.validate.payload) {
|
|
290
|
+
this._cycle.push(Validation.payload);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (this.settings.validate.state) {
|
|
294
|
+
this._cycle.push(Validation.state);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (this._extensions.onPreHandler.nodes) {
|
|
298
|
+
this._cycle.push(this._extensions.onPreHandler);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this._cycle.push(Handler.execute);
|
|
302
|
+
|
|
303
|
+
if (this._extensions.onPostHandler.nodes) {
|
|
304
|
+
this._cycle.push(this._extensions.onPostHandler);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this._postCycle = [];
|
|
308
|
+
|
|
309
|
+
if (this.settings.response._validate &&
|
|
310
|
+
this.settings.response.sample !== 0) {
|
|
311
|
+
|
|
312
|
+
this._postCycle.push(Validation.response);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (this._extensions.onPreResponse.nodes) {
|
|
316
|
+
this._postCycle.push(this._extensions.onPreResponse);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this._buildMarshalCycle();
|
|
320
|
+
|
|
321
|
+
// onPostResponse
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
_buildMarshalCycle() {
|
|
325
|
+
|
|
326
|
+
this._marshalCycle = [Headers.type];
|
|
327
|
+
|
|
328
|
+
if (this.settings.cors) {
|
|
329
|
+
this._marshalCycle.push(Cors.headers);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (this.settings.security) {
|
|
333
|
+
this._marshalCycle.push(Security.headers);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
this._marshalCycle.push(Headers.entity);
|
|
337
|
+
|
|
338
|
+
if (this.method === 'get' ||
|
|
339
|
+
this.method === '*') {
|
|
340
|
+
|
|
341
|
+
this._marshalCycle.push(Headers.unmodified);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this._marshalCycle.push(Headers.cache);
|
|
345
|
+
this._marshalCycle.push(Headers.state);
|
|
346
|
+
this._marshalCycle.push(Headers.content);
|
|
347
|
+
|
|
348
|
+
if (this._core.auth._enabled(this, 'response')) {
|
|
349
|
+
this._marshalCycle.push(Auth.response); // Must be last in case requires access to headers
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
_assert(condition, message) {
|
|
354
|
+
|
|
355
|
+
if (condition) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (this.method[0] !== '_') {
|
|
360
|
+
message = `${message}: ${this.method.toUpperCase()} ${this.path}`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
throw new Assert.AssertionError({
|
|
364
|
+
message,
|
|
365
|
+
actual: false,
|
|
366
|
+
expected: true,
|
|
367
|
+
operator: '==',
|
|
368
|
+
stackStartFunction: this._assert
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
internals.state = async function (request) {
|
|
375
|
+
|
|
376
|
+
request.state = {};
|
|
377
|
+
|
|
378
|
+
const req = request.raw.req;
|
|
379
|
+
const cookies = req.headers.cookie;
|
|
380
|
+
if (!cookies) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
var result = await request._core.states.parse(cookies);
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
Bounce.rethrow(err, 'system');
|
|
389
|
+
var parseError = err;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const { states, failed = [] } = result || parseError;
|
|
393
|
+
request.state = states || {};
|
|
394
|
+
|
|
395
|
+
// Clear cookies
|
|
396
|
+
|
|
397
|
+
for (const item of failed) {
|
|
398
|
+
if (item.settings.clearInvalid) {
|
|
399
|
+
request._clearState(item.name);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!parseError) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
parseError.header = cookies;
|
|
408
|
+
|
|
409
|
+
return request._core.toolkit.failAction(request, request.route.settings.state.failAction, parseError, { tags: ['state', 'error'] });
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
internals.payload = async function (request) {
|
|
414
|
+
|
|
415
|
+
if (request.method === 'get' ||
|
|
416
|
+
request.method === 'head') { // When route.method is '*'
|
|
417
|
+
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (request._expectContinue) {
|
|
422
|
+
request.raw.res.writeContinue();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (request.payload !== undefined) {
|
|
426
|
+
return internals.drain(request);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const { payload, mime } = await Subtext.parse(request.raw.req, request._tap(), request.route.settings.payload);
|
|
431
|
+
|
|
432
|
+
request._isPayloadPending = !!(payload && payload._readableState);
|
|
433
|
+
request.mime = mime;
|
|
434
|
+
request.payload = payload;
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
Bounce.rethrow(err, 'system');
|
|
438
|
+
|
|
439
|
+
if (request._isPayloadPending) {
|
|
440
|
+
await internals.drain(request);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
request.mime = err.mime;
|
|
444
|
+
request.payload = null;
|
|
445
|
+
|
|
446
|
+
return request._core.toolkit.failAction(request, request.route.settings.payload.failAction, err, { tags: ['payload', 'error'] });
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
internals.drain = async function (request) {
|
|
452
|
+
|
|
453
|
+
// Flush out any pending request payload not consumed due to errors
|
|
454
|
+
|
|
455
|
+
await Streams.drain(request.raw.req);
|
|
456
|
+
request._isPayloadPending = false;
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
internals.jsonpRegex = /^[\w\$\[\]\.]+$/;
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
internals.parseJSONP = function (request) {
|
|
464
|
+
|
|
465
|
+
const jsonp = request.query[request.route.settings.jsonp];
|
|
466
|
+
if (jsonp) {
|
|
467
|
+
if (internals.jsonpRegex.test(jsonp) === false) {
|
|
468
|
+
throw Boom.badRequest('Invalid JSONP parameter value');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
request.jsonp = jsonp;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
internals.cleanupJSONP = function (request) {
|
|
477
|
+
|
|
478
|
+
if (request.jsonp) {
|
|
479
|
+
delete request.query[request.route.settings.jsonp];
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
internals.config = function (chain) {
|
|
485
|
+
|
|
486
|
+
if (!chain.length) {
|
|
487
|
+
return {};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
let config = chain[0];
|
|
491
|
+
for (const item of chain) {
|
|
492
|
+
config = Hoek.applyToDefaults(config, item, { shallow: ['bind', 'validate.headers', 'validate.payload', 'validate.params', 'validate.query', 'validate.state'] });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return config;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
internals.rules = function (rules, info, server) {
|
|
500
|
+
|
|
501
|
+
const configs = [];
|
|
502
|
+
|
|
503
|
+
let realm = server.realm;
|
|
504
|
+
while (realm) {
|
|
505
|
+
if (realm._rules) {
|
|
506
|
+
const source = !realm._rules.settings.validate ? rules : Validate.attempt(rules, realm._rules.settings.validate.schema, realm._rules.settings.validate.options);
|
|
507
|
+
const config = realm._rules.processor(source, info);
|
|
508
|
+
if (config) {
|
|
509
|
+
configs.unshift(config);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
realm = realm.parent;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return internals.config(configs);
|
|
517
|
+
};
|
package/lib/security.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const internals = {};
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
exports.route = function (settings) {
|
|
7
|
+
|
|
8
|
+
if (!settings) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const security = settings;
|
|
13
|
+
if (security.hsts) {
|
|
14
|
+
if (security.hsts === true) {
|
|
15
|
+
security._hsts = 'max-age=15768000';
|
|
16
|
+
}
|
|
17
|
+
else if (typeof security.hsts === 'number') {
|
|
18
|
+
security._hsts = 'max-age=' + security.hsts;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
security._hsts = 'max-age=' + (security.hsts.maxAge || 15768000);
|
|
22
|
+
if (security.hsts.includeSubdomains || security.hsts.includeSubDomains) {
|
|
23
|
+
security._hsts = security._hsts + '; includeSubDomains';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (security.hsts.preload) {
|
|
27
|
+
security._hsts = security._hsts + '; preload';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (security.xframe) {
|
|
33
|
+
if (security.xframe === true) {
|
|
34
|
+
security._xframe = 'DENY';
|
|
35
|
+
}
|
|
36
|
+
else if (typeof security.xframe === 'string') {
|
|
37
|
+
security._xframe = security.xframe.toUpperCase();
|
|
38
|
+
}
|
|
39
|
+
else if (security.xframe.rule === 'allow-from') {
|
|
40
|
+
if (!security.xframe.source) {
|
|
41
|
+
security._xframe = 'SAMEORIGIN';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
security._xframe = 'ALLOW-FROM ' + security.xframe.source;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
security._xframe = security.xframe.rule.toUpperCase();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return security;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
exports.headers = function (response) {
|
|
57
|
+
|
|
58
|
+
const security = response.request.route.settings.security;
|
|
59
|
+
|
|
60
|
+
if (security._hsts) {
|
|
61
|
+
response._header('strict-transport-security', security._hsts, { override: false });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (security._xframe) {
|
|
65
|
+
response._header('x-frame-options', security._xframe, { override: false });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (security.xss) {
|
|
69
|
+
response._header('x-xss-protection', '1; mode=block', { override: false });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (security.noOpen) {
|
|
73
|
+
response._header('x-download-options', 'noopen', { override: false });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (security.noSniff) {
|
|
77
|
+
response._header('x-content-type-options', 'nosniff', { override: false });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (security.referrer !== false) {
|
|
81
|
+
response._header('referrer-policy', security.referrer, { override: false });
|
|
82
|
+
}
|
|
83
|
+
};
|