serverless-openapi-documenter 0.0.61 → 0.0.63
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.
- package/README.md +77 -0
- package/json/owasp.json +57 -0
- package/package.json +2 -2
- package/src/definitionGenerator.js +32 -6
- package/src/owasp.js +141 -0
- package/test/json/newOWASP.json +57 -0
- package/test/unit/owasp.spec.js +113 -0
package/README.md
CHANGED
|
@@ -61,6 +61,9 @@ Options:
|
|
|
61
61
|
#### Model Details
|
|
62
62
|
* [Models](#models)
|
|
63
63
|
* [Notes on Schemas](#notes-on-schemas)
|
|
64
|
+
#### Response Headers
|
|
65
|
+
* [CORS](#cors)
|
|
66
|
+
* [OWASP Secure Headers](#owasp)
|
|
64
67
|
|
|
65
68
|
### OpenAPI Mapping
|
|
66
69
|
|
|
@@ -729,6 +732,80 @@ You can automatically generate CORS response headers by setting `cors` at the fu
|
|
|
729
732
|
|
|
730
733
|
The generator will interpret your settings for CORS and automatically add the response headers. If for whatever reason you wish to override these, you can set them via the above `responseHeaders` setting and it'll apply your overrides.
|
|
731
734
|
|
|
735
|
+
##### OWASP
|
|
736
|
+
|
|
737
|
+
You can make use of the [OWASP Secure Headers](https://owasp.org/www-project-secure-headers/#x-permitted-cross-domain-policies) to generate response headers. These are a selection of response headers with default values that OWASP recommends returning with your response to help secure your application.
|
|
738
|
+
|
|
739
|
+
The OWASP Secure Headers Project contains a set of recommended headers to return with recommended values, when generating the documentation, the generator will attempt to get the latest version of this document and apply the latest recommendations. If you do not allow outside connections, it will default to a version of recommendations from **2023-05-26 12:22:30 UTC**.
|
|
740
|
+
|
|
741
|
+
Like CORS, if you have already set any of the OWASP Secure headers via `responseHeaders`, it will not overwrite them.
|
|
742
|
+
|
|
743
|
+
To make use of OWASP Secure Headers, you can use the following:
|
|
744
|
+
|
|
745
|
+
###### All OWASP Secure Headers
|
|
746
|
+
|
|
747
|
+
```yml
|
|
748
|
+
methodResponse:
|
|
749
|
+
- statusCode: 200
|
|
750
|
+
responseBody:
|
|
751
|
+
description: Success
|
|
752
|
+
responseModels:
|
|
753
|
+
application/json: "CreateResponse"
|
|
754
|
+
owasp: true
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
This will use the full set of OWASP Secure Headers and their recommended values. Some of these might not be appropriate for your application.
|
|
758
|
+
|
|
759
|
+
###### Subset of OWASP Secure Headers
|
|
760
|
+
|
|
761
|
+
```yml
|
|
762
|
+
methodResponse:
|
|
763
|
+
- statusCode: 200
|
|
764
|
+
responseBody:
|
|
765
|
+
description: Success
|
|
766
|
+
responseModels:
|
|
767
|
+
application/json: "CreateResponse"
|
|
768
|
+
owasp:
|
|
769
|
+
cacheControl: true
|
|
770
|
+
referrerPolicy: true
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
This will set only the `cacheControl` and `referrerPolicy` response header with the default recommendations.
|
|
774
|
+
|
|
775
|
+
The full list of OWASP Secure Headers you can set are:
|
|
776
|
+
|
|
777
|
+
* cacheControl - Cache-Control,
|
|
778
|
+
* clearSiteData - Clear-Site-Data,
|
|
779
|
+
* contentSecurityPolicy - Content-Security-Policy,
|
|
780
|
+
* crossOriginEmbedderPolicy - Cross-Origin-Embedder-Policy,
|
|
781
|
+
* crossOriginOpenerPolicy - Cross-Origin-Opener-Policy,
|
|
782
|
+
* crossOriginResourcePolicy - Cross-Origin-Resource-Policy,
|
|
783
|
+
* permissionsPolicy - Permissions-Policy,
|
|
784
|
+
* pragma - Pragma,
|
|
785
|
+
* referrerPolicy - Referrer-Policy,
|
|
786
|
+
* strictTransportSecurity - Strict-Transport-Security,
|
|
787
|
+
* xContentTypeOptions - X-Content-Type-Options,
|
|
788
|
+
* xFrameOptions - X-Frame-Options,
|
|
789
|
+
* xPermittedCrossDomainPolicies - X-Permitted-Cross-Domain-Policies
|
|
790
|
+
|
|
791
|
+
###### Subset of OWASP Secure Headers with user defined values
|
|
792
|
+
|
|
793
|
+
If you wish to override the OWASP Secure Headers, you can write your `methodResponse` like:
|
|
794
|
+
|
|
795
|
+
```yml
|
|
796
|
+
methodResponse:
|
|
797
|
+
- statusCode: 200
|
|
798
|
+
responseBody:
|
|
799
|
+
description: Success
|
|
800
|
+
responseModels:
|
|
801
|
+
application/json: "CreateResponse"
|
|
802
|
+
owasp:
|
|
803
|
+
cacheControl:
|
|
804
|
+
value: no-store
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
This will set the `Cache-Control` Response Header to have a value of "no-store" rather than any value the OWASP Secure Headers Project recommends.
|
|
808
|
+
|
|
732
809
|
## Example configuration
|
|
733
810
|
|
|
734
811
|
Please view the example [serverless.yml](test/serverless-tests/serverless%202/serverless.yml).
|
package/json/owasp.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"last_update_utc": "2023-05-26 12:22:30",
|
|
3
|
+
"headers": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Cache-Control",
|
|
6
|
+
"value": "no-store, max-age=0"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Clear-Site-Data",
|
|
10
|
+
"value": "\"cache\",\"cookies\",\"storage\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Content-Security-Policy",
|
|
14
|
+
"value": "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "Cross-Origin-Embedder-Policy",
|
|
18
|
+
"value": "require-corp"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Cross-Origin-Opener-Policy",
|
|
22
|
+
"value": "same-origin"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "Cross-Origin-Resource-Policy",
|
|
26
|
+
"value": "same-origin"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Permissions-Policy",
|
|
30
|
+
"value": "accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Pragma",
|
|
34
|
+
"value": "no-cache"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "Referrer-Policy",
|
|
38
|
+
"value": "no-referrer"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "Strict-Transport-Security",
|
|
42
|
+
"value": "max-age=31536000 ; includeSubDomains"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "X-Content-Type-Options",
|
|
46
|
+
"value": "nosniff"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "X-Frame-Options",
|
|
50
|
+
"value": "deny"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "X-Permitted-Cross-Domain-Policies",
|
|
54
|
+
"value": "none"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.63",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"json-schema-for-openapi": "^0.3.2",
|
|
39
39
|
"lodash.isequal": "^4.5.0",
|
|
40
40
|
"oas-validator": "^5.0.8",
|
|
41
|
-
"openapi-to-postmanv2": "^4.
|
|
41
|
+
"openapi-to-postmanv2": "^4.13.0",
|
|
42
42
|
"swagger2openapi": "^7.0.8",
|
|
43
43
|
"uuid": "^9.0.0"
|
|
44
44
|
},
|
|
@@ -6,6 +6,7 @@ const { v4: uuid } = require('uuid')
|
|
|
6
6
|
const validator = require('oas-validator');
|
|
7
7
|
|
|
8
8
|
const SchemaHandler = require('./schemaHandler')
|
|
9
|
+
const oWASP = require('./owasp')
|
|
9
10
|
|
|
10
11
|
class DefinitionGenerator {
|
|
11
12
|
constructor(serverless, options = {}) {
|
|
@@ -41,7 +42,7 @@ class DefinitionGenerator {
|
|
|
41
42
|
|
|
42
43
|
this.DEFAULT_CORS_HEADERS = {
|
|
43
44
|
'Access-Control-Allow-Origin': {
|
|
44
|
-
description: 'The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin.',
|
|
45
|
+
description: 'The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given [origin](https://developer.mozilla.org/en-US/docs/Glossary/Origin). - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)',
|
|
45
46
|
schema: {
|
|
46
47
|
type: 'string',
|
|
47
48
|
default: '*',
|
|
@@ -49,7 +50,7 @@ class DefinitionGenerator {
|
|
|
49
50
|
}
|
|
50
51
|
},
|
|
51
52
|
'Access-Control-Allow-Credentials': {
|
|
52
|
-
description: `The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode (Request.credentials) is include`,
|
|
53
|
+
description: `The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode ([Request.credentials](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)) is include. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials)`,
|
|
53
54
|
schema: {
|
|
54
55
|
type: 'boolean',
|
|
55
56
|
default: true
|
|
@@ -68,6 +69,8 @@ class DefinitionGenerator {
|
|
|
68
69
|
async parse() {
|
|
69
70
|
this.createInfo()
|
|
70
71
|
|
|
72
|
+
await oWASP.getLatest()
|
|
73
|
+
|
|
71
74
|
await this.schemaHandler.addModelsToOpenAPI()
|
|
72
75
|
.catch(err => {
|
|
73
76
|
throw err
|
|
@@ -379,21 +382,44 @@ class DefinitionGenerator {
|
|
|
379
382
|
})
|
|
380
383
|
}
|
|
381
384
|
|
|
385
|
+
let owaspHeaders = {}
|
|
386
|
+
if (response.owasp) {
|
|
387
|
+
if (typeof response.owasp === 'boolean') {
|
|
388
|
+
owaspHeaders = await this.createResponseHeaders(oWASP.DEFAULT_OWASP_HEADERS)
|
|
389
|
+
.catch(err => {
|
|
390
|
+
throw err
|
|
391
|
+
})
|
|
392
|
+
} else {
|
|
393
|
+
owaspHeaders = await this.createResponseHeaders(oWASP.getHeaders(response.owasp))
|
|
394
|
+
.catch(err => {
|
|
395
|
+
throw err
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
}
|
|
382
399
|
|
|
383
400
|
const corsHeaders = await this.corsHeaders()
|
|
384
401
|
.catch(err => {
|
|
385
402
|
throw err;
|
|
386
403
|
})
|
|
387
404
|
|
|
388
|
-
|
|
389
|
-
for (const key in
|
|
405
|
+
const addHeaders = (headers) => {
|
|
406
|
+
for (const key in headers) {
|
|
390
407
|
if (!(key in obj.headers) && (obj.headers[key] = {})) {
|
|
391
|
-
obj.headers[key] =
|
|
408
|
+
obj.headers[key] = headers[key]
|
|
392
409
|
}
|
|
393
410
|
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (obj.headers) {
|
|
414
|
+
addHeaders(corsHeaders)
|
|
415
|
+
addHeaders(owaspHeaders)
|
|
394
416
|
} else {
|
|
395
|
-
if (Object.keys(corsHeaders).length)
|
|
417
|
+
if (Object.keys(corsHeaders).length) {
|
|
396
418
|
obj.headers = corsHeaders
|
|
419
|
+
addHeaders(owaspHeaders)
|
|
420
|
+
} else {
|
|
421
|
+
obj.headers = owaspHeaders
|
|
422
|
+
}
|
|
397
423
|
}
|
|
398
424
|
|
|
399
425
|
Object.assign(responses, { [response.statusCode]: obj })
|
package/src/owasp.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const https = require('https')
|
|
4
|
+
|
|
5
|
+
const defaultOWASP = require('../json/owasp.json')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} Header
|
|
9
|
+
* @property {string} name - The name of the header
|
|
10
|
+
* @property {string} value - The default value of the header
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} OWASPHeaders
|
|
15
|
+
* @property {string} last_update_utc - When the headers were last updated in UTC
|
|
16
|
+
* @property {Array.<Header>} headers - An array of headers
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
class OWASP {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.DEFAULT_OWASP_HEADERS = {
|
|
22
|
+
"Cache-Control": {
|
|
23
|
+
description: 'The Cache-Control HTTP header field holds directives (instructions) — in both requests and responses — that control [caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) in browsers and shared caches (e.g. Proxies, CDNs). - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)',
|
|
24
|
+
},
|
|
25
|
+
"Clear-Site-Data": {
|
|
26
|
+
description: 'The Clear-Site-Data header clears browsing data (cookies, storage, cache) associated with the requesting website. It allows web developers to have more control over the data stored by a client browser for their origins. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data)',
|
|
27
|
+
},
|
|
28
|
+
"Content-Security-Policy": {
|
|
29
|
+
description: 'The HTTP Content-Security-Policy response header allows website administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks ([Cross-site scripting](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting)). - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)',
|
|
30
|
+
},
|
|
31
|
+
"Cross-Origin-Embedder-Policy": {
|
|
32
|
+
description: 'The HTTP Cross-Origin-Embedder-Policy (COEP) response header configures embedding cross-origin resources into the document. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy)',
|
|
33
|
+
},
|
|
34
|
+
"Cross-Origin-Opener-Policy": {
|
|
35
|
+
description: 'The HTTP Cross-Origin-Opener-Policy (COOP) response header allows you to ensure a top-level document does not share a browsing context group with cross-origin documents. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy)',
|
|
36
|
+
},
|
|
37
|
+
"Cross-Origin-Resource-Policy": {
|
|
38
|
+
description: 'Cross-Origin Resource Policy is a policy set by the Cross-Origin-Resource-Policy HTTP header that lets websites and applications opt in to protection against certain requests from other origins (such as those issued with elements like <script> and <img>), to mitigate speculative side-channel attacks, like Spectre, as well as Cross-Site Script Inclusion attacks. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy)',
|
|
39
|
+
},
|
|
40
|
+
"Permissions-Policy": {
|
|
41
|
+
description: 'The HTTP Permissions-Policy header provides a mechanism to allow and deny the use of browser features in a document or within any [<iframe>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) elements in the document. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy)',
|
|
42
|
+
},
|
|
43
|
+
"Pragma": {
|
|
44
|
+
description: 'The Pragma HTTP/1.0 general header is an implementation-specific header that may have various effects along the request-response chain. This header serves for backwards compatibility with the HTTP/1.0 caches that do not have a [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) HTTP/1.1 header. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pragma)',
|
|
45
|
+
deprecated: true,
|
|
46
|
+
},
|
|
47
|
+
"Referrer-Policy": {
|
|
48
|
+
description: 'The Referrer-Policy [HTTP header](https://developer.mozilla.org/en-US/docs/Glossary/HTTP_header) controls how much [referrer information](https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns) (sent with the [Referer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) header) should be included with requests. Aside from the HTTP header, you can [set this policy in HTML](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#integration_with_html). - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy)',
|
|
49
|
+
},
|
|
50
|
+
"Strict-Transport-Security": {
|
|
51
|
+
description: 'The HTTP Strict-Transport-Security response header (often abbreviated as [HSTS](https://developer.mozilla.org/en-US/docs/Glossary/HSTS)) informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be converted to HTTPS. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)',
|
|
52
|
+
},
|
|
53
|
+
"X-Content-Type-Options": {
|
|
54
|
+
description: 'The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) advertised in the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) headers should be followed and not be changed. The header allows you to avoid [MIME type sniffing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#mime_sniffing) by saying that the MIME types are deliberately configured. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options)',
|
|
55
|
+
},
|
|
56
|
+
"X-Frame-Options": {
|
|
57
|
+
description: 'The X-Frame-Options [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) response header can be used to indicate whether or not a browser should be allowed to render a page in a [<frame>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frame), [<iframe>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe), [<embed>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed) or [<object>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object). Sites can use this to avoid [click-jacking](https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#click-jacking) attacks, by ensuring that their content is not embedded into other sites. - [MDN Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options)',
|
|
58
|
+
},
|
|
59
|
+
"X-Permitted-Cross-Domain-Policies": {
|
|
60
|
+
description: "A cross-domain policy file is an XML document that grants a web client, such as Adobe Flash Player or Adobe Acrobat (though not necessarily limited to these), permission to handle data across domains. When clients request content hosted on a particular source domain and that content makes requests directed towards a domain other than its own, the remote domain needs to host a cross-domain policy file that grants access to the source domain, allowing the client to continue the transaction. Normally a meta-policy is declared in the master policy file, but for those who can't write to the root directory, they can also declare a meta-policy using the X-Permitted-Cross-Domain-Policies HTTP response header. - [OWASP Link](https://owasp.org/www-project-secure-headers/#x-permitted-cross-domain-policies)",
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.headerMap = {
|
|
65
|
+
cacheControl: 'Cache-Control',
|
|
66
|
+
clearSiteData: 'Clear-Site-Data',
|
|
67
|
+
contentSecurityPolicy: 'Content-Security-Policy',
|
|
68
|
+
crossOriginEmbedderPolicy: 'Cross-Origin-Embedder-Policy',
|
|
69
|
+
crossOriginOpenerPolicy: 'Cross-Origin-Opener-Policy',
|
|
70
|
+
crossOriginResourcePolicy: 'Cross-Origin-Resource-Policy',
|
|
71
|
+
permissionsPolicy: 'Permissions-Policy',
|
|
72
|
+
pragma: 'Pragma',
|
|
73
|
+
referrerPolicy: 'Referrer-Policy',
|
|
74
|
+
strictTransportSecurity: 'Strict-Transport-Security',
|
|
75
|
+
xContentTypeOptions: 'X-Content-Type-Options',
|
|
76
|
+
xFrameOptions: 'X-Frame-Options',
|
|
77
|
+
xPermittedCrossDomainPolicies: 'X-Permitted-Cross-Domain-Policies'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getLatest() {
|
|
82
|
+
const headerJSON = await new Promise((resolve, reject) => {
|
|
83
|
+
const req = https.get('https://owasp.org/www-project-secure-headers/ci/headers_add.json', (res) => {
|
|
84
|
+
let data = []
|
|
85
|
+
|
|
86
|
+
if (res.statusCode !== 200) {
|
|
87
|
+
resolve(defaultOWASP)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
res.on('error', (err) => {
|
|
91
|
+
resolve(defaultOWASP)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
res.on('data', (chunk) => {
|
|
95
|
+
data.push(chunk)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
res.on('end', () => {
|
|
99
|
+
resolve(JSON.parse(Buffer.concat(data).toString()))
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
.on('error', (err) => {
|
|
103
|
+
resolve(defaultOWASP)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
req.end()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
this.populateDefaults(headerJSON)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @funtion populateDefaults
|
|
114
|
+
* @param {OWASPHeaders} headerJSON
|
|
115
|
+
*/
|
|
116
|
+
populateDefaults(headerJSON) {
|
|
117
|
+
for (const header of headerJSON.headers) {
|
|
118
|
+
if (this.DEFAULT_OWASP_HEADERS?.[header.name]) {
|
|
119
|
+
Object.assign(this.DEFAULT_OWASP_HEADERS[header.name], {schema: {type: 'string', default: header.value}})
|
|
120
|
+
} else {
|
|
121
|
+
Object.assign(this.DEFAULT_OWASP_HEADERS, {[header.name]: {schema: {type: 'string', default: header.value}}})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getHeaders(headerList) {
|
|
127
|
+
const obj = {}
|
|
128
|
+
for (const headerName of Object.keys(headerList)) {
|
|
129
|
+
const defaultHeader = this.DEFAULT_OWASP_HEADERS[this.headerMap[headerName]]
|
|
130
|
+
Object.assign(obj, {[this.headerMap[headerName]]: defaultHeader})
|
|
131
|
+
|
|
132
|
+
if (typeof headerList[headerName] !== 'boolean') {
|
|
133
|
+
obj[this.headerMap[headerName]].schema.default = headerList[headerName].value
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return obj
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = new OWASP()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"last_update_utc": "2023-05-26 12:22:30",
|
|
3
|
+
"headers": [
|
|
4
|
+
{
|
|
5
|
+
"name": "Cache-Control",
|
|
6
|
+
"value": "no-store, max-age=0"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Clear-Site-Data",
|
|
10
|
+
"value": "\"cache\",\"cookies\",\"storage\""
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Content-Security-Policy",
|
|
14
|
+
"value": "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "Cross-Origin-Embedder-Policy",
|
|
18
|
+
"value": "credentialless"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Cross-Origin-Opener-Policy",
|
|
22
|
+
"value": "same-origin"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "Cross-Origin-Resource-Policy",
|
|
26
|
+
"value": "same-origin"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Permissions-Policy",
|
|
30
|
+
"value": "accelerometer=(),ambient-light-sensor=(),autoplay=(),battery=(),camera=(),display-capture=(),document-domain=(),encrypted-media=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),layout-animations=(self),legacy-image-formats=(self),magnetometer=(),microphone=(),midi=(),oversized-images=(self),payment=(),picture-in-picture=(),publickey-credentials-get=(),speaker-selection=(),sync-xhr=(self),unoptimized-images=(self),unsized-media=(self),usb=(),screen-wake-lock=(),web-share=(),xr-spatial-tracking=()"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Pragma",
|
|
34
|
+
"value": "no-cache"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "Referrer-Policy",
|
|
38
|
+
"value": "no-referrer"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "Strict-Transport-Security",
|
|
42
|
+
"value": "max-age=31536000 ; includeSubDomains"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "X-Content-Type-Options",
|
|
46
|
+
"value": "nosniff"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "X-Frame-Options",
|
|
50
|
+
"value": "deny"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "X-Permitted-Cross-Domain-Policies",
|
|
54
|
+
"value": "none"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const expect = require('chai').expect
|
|
4
|
+
const nock = require('nock')
|
|
5
|
+
|
|
6
|
+
const owasp = require('../../src/owasp')
|
|
7
|
+
|
|
8
|
+
const owaspJSON = require('../../json/owasp.json')
|
|
9
|
+
const newOWASPJSON = require('../json/newOWASP.json')
|
|
10
|
+
|
|
11
|
+
describe(`owasp`, function () {
|
|
12
|
+
describe(`getLatest`, function () {
|
|
13
|
+
it(`populates the defaults from the included OWASP release when the online version can not be reached`, async function() {
|
|
14
|
+
nock('https://owasp.org')
|
|
15
|
+
.get('/www-project-secure-headers/ci/headers_add.json')
|
|
16
|
+
.reply(404, {})
|
|
17
|
+
|
|
18
|
+
await owasp.getLatest()
|
|
19
|
+
.catch(err => {
|
|
20
|
+
console.error(err)
|
|
21
|
+
expect(err).to.be.undefined
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['Permissions-Policy']).to.have.property('schema')
|
|
25
|
+
const permissionsPolicyDefault = owaspJSON.headers.filter(obj => obj.name === 'Permissions-Policy')
|
|
26
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['Permissions-Policy'].schema.default).to.be.equal(permissionsPolicyDefault[0].value)
|
|
27
|
+
expect(Object.keys(owasp.DEFAULT_OWASP_HEADERS).length).to.be.equal(13)
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it(`populates the defaults with information from a new OWASP release`, async function() {
|
|
31
|
+
nock('https://owasp.org')
|
|
32
|
+
.get('/www-project-secure-headers/ci/headers_add.json')
|
|
33
|
+
.reply(200, newOWASPJSON)
|
|
34
|
+
|
|
35
|
+
await owasp.getLatest()
|
|
36
|
+
.catch(err => {
|
|
37
|
+
console.error(err)
|
|
38
|
+
expect(err).to.be.undefined
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['Cross-Origin-Embedder-Policy']).to.have.property('schema')
|
|
42
|
+
const newCrossOriginEmbedderPolicy = newOWASPJSON.headers.filter(obj => obj.name === 'Cross-Origin-Embedder-Policy')
|
|
43
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['Cross-Origin-Embedder-Policy'].schema.default).to.be.equal(newCrossOriginEmbedderPolicy[0].value)
|
|
44
|
+
expect(Object.keys(owasp.DEFAULT_OWASP_HEADERS).length).to.be.equal(13)
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it(`does not remove any defaults not contained in a new release`, async function() {
|
|
48
|
+
const newOWASPJSONMissing = JSON.parse(JSON.stringify(newOWASPJSON))
|
|
49
|
+
|
|
50
|
+
const headers = newOWASPJSONMissing.headers.filter(obj => obj.name !== 'Pragma')
|
|
51
|
+
newOWASPJSONMissing.headers = headers
|
|
52
|
+
|
|
53
|
+
nock('https://owasp.org')
|
|
54
|
+
.get('/www-project-secure-headers/ci/headers_add.json')
|
|
55
|
+
.reply(200, newOWASPJSONMissing)
|
|
56
|
+
|
|
57
|
+
await owasp.getLatest()
|
|
58
|
+
.catch(err => {
|
|
59
|
+
console.error(err)
|
|
60
|
+
expect(err).to.be.undefined
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(owasp.DEFAULT_OWASP_HEADERS).to.have.property('Pragma')
|
|
64
|
+
expect(Object.keys(owasp.DEFAULT_OWASP_HEADERS).length).to.be.equal(13)
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it(`adds any properties contained in a new release`, async function() {
|
|
68
|
+
const newOWASPJSONAdded = JSON.parse(JSON.stringify(newOWASPJSON))
|
|
69
|
+
newOWASPJSONAdded.headers.push({name: 'x-added', value: 'true'})
|
|
70
|
+
|
|
71
|
+
nock('https://owasp.org')
|
|
72
|
+
.get('/www-project-secure-headers/ci/headers_add.json')
|
|
73
|
+
.reply(200, newOWASPJSONAdded)
|
|
74
|
+
|
|
75
|
+
await owasp.getLatest()
|
|
76
|
+
.catch(err => {
|
|
77
|
+
console.error(err)
|
|
78
|
+
expect(err).to.be.undefined
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(owasp.DEFAULT_OWASP_HEADERS).to.have.property('x-added')
|
|
82
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['x-added']).to.have.property('schema')
|
|
83
|
+
expect(owasp.DEFAULT_OWASP_HEADERS['x-added'].schema.default).to.be.equal('true')
|
|
84
|
+
expect(Object.keys(owasp.DEFAULT_OWASP_HEADERS).length).to.be.equal(14)
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe(`getHeaders`, function () {
|
|
89
|
+
it(`brings back default headers from a list`, function() {
|
|
90
|
+
const headerOptions = {cacheControl: true, xFrameOptions: true}
|
|
91
|
+
const headers = owasp.getHeaders(headerOptions)
|
|
92
|
+
|
|
93
|
+
expect(Object.keys(headers).length).to.be.equal(2)
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it(`brings back default headers from a list with new schema defaults when values are provided`, function() {
|
|
97
|
+
const headerOptions = {
|
|
98
|
+
referrerPolicy: {
|
|
99
|
+
value: 'true'
|
|
100
|
+
},
|
|
101
|
+
crossOriginOpenerPolicy: {
|
|
102
|
+
value: 'strict'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const headers = owasp.getHeaders(headerOptions)
|
|
107
|
+
|
|
108
|
+
expect(Object.keys(headers).length).to.be.equal(2)
|
|
109
|
+
|
|
110
|
+
expect(headers['Cross-Origin-Opener-Policy'].schema.default === 'strict')
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|