@react-foundry/fastify-harden 0.1.8 → 0.2.0

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/dist/common.js CHANGED
@@ -1,7 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isDefined = exports.id = void 0;
4
- const id = (v) => v;
5
- exports.id = id;
6
- const isDefined = (v) => (v !== undefined && v !== '');
7
- exports.isDefined = isDefined;
1
+ export const id = (v) => v;
2
+ export const isDefined = (v) => (v !== undefined && v !== '');
@@ -1,41 +1,38 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.contentSecurityPolicy = exports.unsafeInline = exports.unsafeEval = exports.self = exports.none = void 0;
4
- const common_1 = require("./common");
5
- exports.none = "'none'";
6
- exports.self = "'self'";
7
- exports.unsafeEval = "'unsafe-eval'";
8
- exports.unsafeInline = "'unsafe-inline'";
9
- const contentSecurityPolicy = ({ dev = process.env.NODE_ENV === 'development', formAction: _formAction, frameAncestors: _frameAncestors, nonce: _nonce }) => {
1
+ import { id, isDefined } from './common';
2
+ export const none = "'none'";
3
+ export const self = "'self'";
4
+ export const unsafeEval = "'unsafe-eval'";
5
+ export const unsafeInline = "'unsafe-inline'";
6
+ export const contentSecurityPolicy = ({ dev = process.env.NODE_ENV === 'development', formAction: _formAction, frameAncestors: _frameAncestors, nonce: _nonce }) => {
10
7
  const formAction = (Array.isArray(_formAction)
11
8
  ? _formAction
12
- : [_formAction]).filter(common_1.id);
9
+ : [_formAction]).filter(id);
13
10
  const frameAncestors = (Array.isArray(_frameAncestors)
14
11
  ? _frameAncestors
15
- : [_frameAncestors]).filter(common_1.id);
12
+ : [_frameAncestors]).filter(id);
16
13
  const frameAncestor = (frameAncestors.length > 1
17
14
  ? 'multiple'
18
- : frameAncestors[0]) || exports.none;
15
+ : frameAncestors[0]) || none;
19
16
  const frameOptions = (frameAncestor === 'multiple'
20
17
  ? undefined
21
- : (frameAncestor === exports.self
18
+ : (frameAncestor === self
22
19
  ? 'SAMEORIGIN'
23
20
  : 'DENY'));
24
21
  const nonce = _nonce && `'nonce-${_nonce}'`;
25
22
  const cspObject = {
26
- 'default-src': exports.none,
23
+ 'default-src': none,
27
24
  'connect-src': (dev
28
- ? [exports.self, 'ws://localhost:*']
29
- : exports.self),
30
- 'font-src': exports.self,
31
- 'frame-src': exports.self,
32
- 'img-src': exports.self,
33
- 'manifest-src': exports.self,
34
- 'media-src': exports.self,
35
- 'script-src': [exports.self, nonce],
36
- 'style-src': [exports.self, exports.unsafeInline],
37
- 'form-action': formAction.length && formAction || exports.self,
38
- 'frame-ancestors': frameAncestors.length && frameAncestors || exports.none
25
+ ? [self, 'ws://localhost:*']
26
+ : self),
27
+ 'font-src': self,
28
+ 'frame-src': self,
29
+ 'img-src': self,
30
+ 'manifest-src': self,
31
+ 'media-src': self,
32
+ 'script-src': [self, nonce],
33
+ 'style-src': [self, unsafeInline],
34
+ 'form-action': formAction.length && formAction || self,
35
+ 'frame-ancestors': frameAncestors.length && frameAncestors || none
39
36
  };
40
37
  const cspString = (Object.keys(cspObject)
41
38
  .map(directive => {
@@ -44,18 +41,17 @@ const contentSecurityPolicy = ({ dev = process.env.NODE_ENV === 'development', f
44
41
  ? _valueArr
45
42
  : [_valueArr]);
46
43
  const values = (valueArr
47
- .filter(common_1.isDefined)
44
+ .filter(isDefined)
48
45
  .map(v => `${v}`)
49
46
  .join(' '));
50
47
  return (values === ''
51
48
  ? undefined
52
49
  : `${directive} ${values}`);
53
50
  })
54
- .filter(common_1.isDefined)
51
+ .filter(isDefined)
55
52
  .join('; '));
56
53
  return {
57
54
  policy: cspString,
58
55
  frameOptions
59
56
  };
60
57
  };
61
- exports.contentSecurityPolicy = contentSecurityPolicy;
package/dist/index.js CHANGED
@@ -1,12 +1,6 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fastifyHarden = void 0;
7
- const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
8
- const permissions_policy_1 = require("./permissions-policy");
9
- const content_security_policy_1 = require("./content-security-policy");
1
+ import fp from 'fastify-plugin';
2
+ import { permissionsPolicy } from './permissions-policy';
3
+ import { contentSecurityPolicy } from './content-security-policy';
10
4
  const fastifyHardenPlugin = async (fastify, { contentSecurityPolicy: cspOptions = {}, dev = process.env.NODE_ENV === 'development', permissionsPolicy: ppOptions = {} }) => {
11
5
  if (!dev) {
12
6
  fastify.setErrorHandler((error, req, reply) => {
@@ -26,8 +20,8 @@ const fastifyHardenPlugin = async (fastify, { contentSecurityPolicy: cspOptions
26
20
  fastify.addHook('onSend', async (_req, reply, payload) => {
27
21
  const nonce = reply.cspNonce;
28
22
  const headers = reply.getHeaders();
29
- const { policy: pp } = (0, permissions_policy_1.permissionsPolicy)(ppOptions);
30
- const { policy: csp, frameOptions } = (0, content_security_policy_1.contentSecurityPolicy)({
23
+ const { policy: pp } = permissionsPolicy(ppOptions);
24
+ const { policy: csp, frameOptions } = contentSecurityPolicy({
31
25
  ...cspOptions,
32
26
  dev,
33
27
  nonce
@@ -49,8 +43,8 @@ const fastifyHardenPlugin = async (fastify, { contentSecurityPolicy: cspOptions
49
43
  return payload;
50
44
  });
51
45
  };
52
- exports.fastifyHarden = (0, fastify_plugin_1.default)(fastifyHardenPlugin, {
46
+ export const fastifyHarden = fp(fastifyHardenPlugin, {
53
47
  fastify: '5.x',
54
48
  name: 'harden',
55
49
  });
56
- exports.default = exports.fastifyHarden;
50
+ export default fastifyHarden;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.permissionsPolicy = void 0;
4
- const common_1 = require("./common");
1
+ import { isDefined } from './common';
5
2
  const keywords = [
6
3
  'self',
7
4
  'src'
@@ -60,7 +57,7 @@ const ppObj = {
60
57
  'vertical-scroll': 'self',
61
58
  'xr-spatial-tracking': 'self'
62
59
  };
63
- const permissionsPolicy = (_options) => ({
60
+ export const permissionsPolicy = (_options) => ({
64
61
  policy: Object.keys(ppObj)
65
62
  .map(directive => {
66
63
  const _valueArr = ppObj[directive];
@@ -68,7 +65,7 @@ const permissionsPolicy = (_options) => ({
68
65
  ? _valueArr
69
66
  : [_valueArr]);
70
67
  const values = (valueArr
71
- .filter(common_1.isDefined)
68
+ .filter(isDefined)
72
69
  .map(v => (keywords.includes(v)
73
70
  ? v
74
71
  : `"${v}"`))
@@ -77,7 +74,6 @@ const permissionsPolicy = (_options) => ({
77
74
  ? undefined
78
75
  : `${directive}=(${values})`);
79
76
  })
80
- .filter(common_1.isDefined)
77
+ .filter(isDefined)
81
78
  .join(', ')
82
79
  });
83
- exports.permissionsPolicy = permissionsPolicy;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@react-foundry/fastify-harden",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "Fastify plugin for extra cyber-security hardening.",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "exports": {
7
8
  ".": {
8
9
  "types": "./dist/index.d.ts",
9
- "import": "./dist/index.mjs",
10
- "require": "./dist/index.js",
11
- "default": "./dist/index.mjs"
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
12
  }
13
13
  },
14
14
  "files": [
@@ -26,16 +26,14 @@
26
26
  "fastify": "5.8.4",
27
27
  "jest": "30.3.0",
28
28
  "jest-environment-jsdom": "30.3.0",
29
- "ts-jest": "29.4.6",
29
+ "ts-jest": "29.4.9",
30
30
  "typescript": "5.9.3"
31
31
  },
32
32
  "scripts": {
33
33
  "test": "NODE_OPTIONS=--experimental-vm-modules jest",
34
- "build": "npm run build:esm && npm run build:cjs",
35
- "build:esm": "tsc -m es2022 && find dist -name '*.js' -exec sh -c 'mv \"$0\" \"${0%.js}.mjs\"' {} \\;",
36
- "build:cjs": "tsc",
34
+ "build": "tsc",
37
35
  "clean": "rm -rf dist tsconfig.tsbuildinfo"
38
36
  },
39
- "module": "dist/index.mjs",
37
+ "module": "dist/index.js",
40
38
  "typings": "dist/index.d.ts"
41
39
  }
package/dist/common.mjs DELETED
@@ -1,2 +0,0 @@
1
- export const id = (v) => v;
2
- export const isDefined = (v) => (v !== undefined && v !== '');
@@ -1,57 +0,0 @@
1
- import { id, isDefined } from './common';
2
- export const none = "'none'";
3
- export const self = "'self'";
4
- export const unsafeEval = "'unsafe-eval'";
5
- export const unsafeInline = "'unsafe-inline'";
6
- export const contentSecurityPolicy = ({ dev = process.env.NODE_ENV === 'development', formAction: _formAction, frameAncestors: _frameAncestors, nonce: _nonce }) => {
7
- const formAction = (Array.isArray(_formAction)
8
- ? _formAction
9
- : [_formAction]).filter(id);
10
- const frameAncestors = (Array.isArray(_frameAncestors)
11
- ? _frameAncestors
12
- : [_frameAncestors]).filter(id);
13
- const frameAncestor = (frameAncestors.length > 1
14
- ? 'multiple'
15
- : frameAncestors[0]) || none;
16
- const frameOptions = (frameAncestor === 'multiple'
17
- ? undefined
18
- : (frameAncestor === self
19
- ? 'SAMEORIGIN'
20
- : 'DENY'));
21
- const nonce = _nonce && `'nonce-${_nonce}'`;
22
- const cspObject = {
23
- 'default-src': none,
24
- 'connect-src': (dev
25
- ? [self, 'ws://localhost:*']
26
- : self),
27
- 'font-src': self,
28
- 'frame-src': self,
29
- 'img-src': self,
30
- 'manifest-src': self,
31
- 'media-src': self,
32
- 'script-src': [self, nonce],
33
- 'style-src': [self, unsafeInline],
34
- 'form-action': formAction.length && formAction || self,
35
- 'frame-ancestors': frameAncestors.length && frameAncestors || none
36
- };
37
- const cspString = (Object.keys(cspObject)
38
- .map(directive => {
39
- const _valueArr = cspObject[directive];
40
- const valueArr = (Array.isArray(_valueArr)
41
- ? _valueArr
42
- : [_valueArr]);
43
- const values = (valueArr
44
- .filter(isDefined)
45
- .map(v => `${v}`)
46
- .join(' '));
47
- return (values === ''
48
- ? undefined
49
- : `${directive} ${values}`);
50
- })
51
- .filter(isDefined)
52
- .join('; '));
53
- return {
54
- policy: cspString,
55
- frameOptions
56
- };
57
- };
package/dist/index.mjs DELETED
@@ -1,50 +0,0 @@
1
- import fp from 'fastify-plugin';
2
- import { permissionsPolicy } from './permissions-policy';
3
- import { contentSecurityPolicy } from './content-security-policy';
4
- const fastifyHardenPlugin = async (fastify, { contentSecurityPolicy: cspOptions = {}, dev = process.env.NODE_ENV === 'development', permissionsPolicy: ppOptions = {} }) => {
5
- if (!dev) {
6
- fastify.setErrorHandler((error, req, reply) => {
7
- const statusCode = error && error.statusCode;
8
- if (!statusCode || statusCode === 500) {
9
- error.message = 'An unexpected error occurred.';
10
- }
11
- reply.send(error);
12
- });
13
- }
14
- fastify.addHook('preHandler', async (_req, reply) => {
15
- const nonce = (Math.random()
16
- .toString(36)
17
- .slice(2));
18
- reply.cspNonce = nonce;
19
- });
20
- fastify.addHook('onSend', async (_req, reply, payload) => {
21
- const nonce = reply.cspNonce;
22
- const headers = reply.getHeaders();
23
- const { policy: pp } = permissionsPolicy(ppOptions);
24
- const { policy: csp, frameOptions } = contentSecurityPolicy({
25
- ...cspOptions,
26
- dev,
27
- nonce
28
- });
29
- if (!headers['cache-control']) {
30
- reply.header('Cache-Control', 'no-cache, no-store, must-revalidate, private');
31
- reply.header('Pragma', 'no-cache');
32
- reply.header('Expires', '0');
33
- reply.header('Cross-Origin-Embedder-Policy', 'require-corp');
34
- reply.header('Cross-Origin-Resource-Policy', 'same-origin');
35
- reply.header('Cross-Origin-Opener-Policy', 'same-origin');
36
- }
37
- reply.header('X-Content-Type-Options', 'nosniff');
38
- if (frameOptions) {
39
- reply.header('X-Frame-Options', frameOptions);
40
- }
41
- reply.header('Content-Security-Policy', csp);
42
- reply.header('Permissions-Policy', pp);
43
- return payload;
44
- });
45
- };
46
- export const fastifyHarden = fp(fastifyHardenPlugin, {
47
- fastify: '5.x',
48
- name: 'harden',
49
- });
50
- export default fastifyHarden;
@@ -1,79 +0,0 @@
1
- import { isDefined } from './common';
2
- const keywords = [
3
- 'self',
4
- 'src'
5
- ];
6
- const ppObj = {
7
- 'accelerometer': 'self',
8
- 'ambient-light-sensor': 'self',
9
- 'attribution-reporting': 'self',
10
- 'autoplay': 'self',
11
- 'battery': 'self',
12
- 'bluetooth': 'self',
13
- 'browsing-topics': 'self',
14
- 'camera': 'self',
15
- 'clipboard-read': 'self',
16
- 'clipboard-write': 'self',
17
- 'compute-pressure': 'self',
18
- 'conversion-measurement': 'self',
19
- 'cross-origin-isolated': 'self',
20
- 'display-capture': 'self',
21
- 'document-domain': 'self',
22
- 'encrypted-media': 'self',
23
- 'execution-while-not-rendered': 'self',
24
- 'execution-while-out-of-viewport': 'self',
25
- 'focus-without-user-activation': 'self',
26
- 'fullscreen': 'self',
27
- 'gamepad': 'self',
28
- 'geolocation': 'self',
29
- 'gyroscope': 'self',
30
- 'keyboard-map': 'self',
31
- 'hid': 'self',
32
- 'identity-credentials-get': 'self',
33
- 'idle-detection': 'self',
34
- 'interest-cohort': 'self',
35
- 'local-fonts': 'self',
36
- 'magnetometer': 'self',
37
- 'microphone': 'self',
38
- 'midi': 'self',
39
- 'navigation-override': 'self',
40
- 'otp-credentials': 'self',
41
- 'payment': 'self',
42
- 'picture-in-picture': 'self',
43
- 'publickey-credentials-create': 'self',
44
- 'publickey-credentials-get': 'self',
45
- 'screen-wake-lock': 'self',
46
- 'serial': 'self',
47
- 'speaker-selection': 'self',
48
- 'storage-access': 'self',
49
- 'sync-script': 'self',
50
- 'sync-xhr': 'self',
51
- 'trust-token-redemption': 'self',
52
- 'unload': 'self',
53
- 'usb': 'self',
54
- 'web-share': 'self',
55
- 'window-management': 'self',
56
- 'window-placement': 'self',
57
- 'vertical-scroll': 'self',
58
- 'xr-spatial-tracking': 'self'
59
- };
60
- export const permissionsPolicy = (_options) => ({
61
- policy: Object.keys(ppObj)
62
- .map(directive => {
63
- const _valueArr = ppObj[directive];
64
- const valueArr = (Array.isArray(_valueArr)
65
- ? _valueArr
66
- : [_valueArr]);
67
- const values = (valueArr
68
- .filter(isDefined)
69
- .map(v => (keywords.includes(v)
70
- ? v
71
- : `"${v}"`))
72
- .join(' '));
73
- return (values === ''
74
- ? undefined
75
- : `${directive}=(${values})`);
76
- })
77
- .filter(isDefined)
78
- .join(', ')
79
- });