Skip to content

Commit c25ee2b

Browse files
committed
granular interface
1 parent 4ff9f55 commit c25ee2b

21 files changed

+344
-231
lines changed

README.md

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
# Helmet Security Headers Library
22

3-
Helps to secure apps by setting HTTP response headers.
4-
Inspired by [`helmet`](https://github.com/helmetjs/helmet) and [`http-helmet`](https://github.com/mcansh/http-helmet)
3+
Helps secure applications by setting HTTP response headers.
4+
Inspired by [`helmet`](https://github.com/helmetjs/helmet) and [`http-helmet`](https://github.com/mcansh/http-helmet).
55

66
## Overview
77

8-
This package provides a flexible and modular way for managing security headers in a structured way.
8+
This package provides a flexible and modular way for managing security headers in a structured manner.
99

10-
- **General security headers**
11-
- **HTML-specific headers** (e.g., `Content-Security-Policy`, `X-Download-Options`)
12-
- **CORS-related configurations**
13-
14-
## Features
15-
16-
- Returns security headers with sensible defaults (inspired by Express Helmet)
17-
- **HTML-specific options** are only applied when `html: true` is set
18-
- **Cross-Origin-Resource-Policy** defaults to `'same-origin'`, but switches to `'cross-origin'` if `cors: true`
10+
- Provides security headers with sensible defaults (inspired by Express Helmet).
11+
- Content-specific options available as needed.
12+
- Resource Sharing Security Headers.
1913

2014
## Installation
2115

@@ -25,22 +19,70 @@ npm install @nichtsam/helmet
2519

2620
## Usage
2721

28-
```ts
29-
import helmet from "@nichtsam/helmet";
22+
This is the most basic usage, which applies security headers for general purpose, best practices for protecting any type of resource.
3023

24+
```ts
25+
import { helmet } from "@nichtsam/helmet";
3126
const headers = new Headers();
32-
33-
// general
3427
helmet(headers);
35-
// with html
36-
helmet(headers, { html: true });
37-
// non html with cors
38-
helmet(headers, { cors: true });
39-
// customize rules
28+
```
29+
30+
There are options to enable more detailed security headers, such as for html webpage contents.
31+
32+
```ts
4033
helmet(headers, {
41-
options: {
42-
crossOriginEmbedderPolicy: false,
43-
contentSecurityPolicy: {},
44-
},
34+
content: { contentSecurityPolicy: {} },
35+
});
36+
```
37+
38+
If you want to share the resource across origins, you can enable the resourceSharing option.
39+
40+
```ts
41+
helmet(headers, { resourceSharing: true });
42+
```
43+
44+
> [!IMPORTANT]
45+
> This only sets the headers for enhanced security.
46+
> You are responsible for setting the correct CORS headers.
47+
> https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_request_headers
48+
49+
### For `node-http`
50+
51+
The package provides a simple wrapper to make it smoother to use on `http.ServerResponse`.
52+
For example in an express app:
53+
54+
```ts
55+
import { helmet } from "@nichtsam/helmet/node-http";
56+
57+
const app = express();
58+
app.use((req, res, next) => {
59+
helmet(res);
60+
next();
4561
});
4662
```
63+
64+
### Granular Interface
65+
66+
The main `helmet` function integrates all the security rules, you can find them all individually under `@nichtsam/helmet/rules`.
67+
They're categorized under `general`, `content` and `resourceSharing`, just like the options in the integrated `helmet` function.
68+
This allows for a layered application approach to better suit individual routes.
69+
70+
For example:
71+
72+
```ts
73+
import { generalSecurity } from "@nichtsam/helmet/rules/general/index";
74+
import { contentSecurity } from "@nichtsam/helmet/rules/content/index";
75+
import { resourceSharingSecurity } from "@nichtsam/helmet/rules/resourceSharing/index";
76+
77+
const headers = new Headers();
78+
// on root level
79+
generalSecurity(headers);
80+
// after the content-type is set
81+
contentSecurity(headers);
82+
// if you want to share across origins
83+
resourceSharingSecurity(headers, { strategy: "cross-origin" });
84+
```
85+
86+
> [!NOTE]
87+
> The `generalSecurity` function includes `resourceSharingSecurity(headers, { strategy: "same-origin" })` by default.
88+
> So you only need to call `resourceSharingSecurity` if you want to share resources across origins or customize the strategy.

package.json

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44
"name": "@nichtsam/helmet",
55
"version": "0.1.0",
66
"license": "MIT",
7-
"keywords": [
8-
"web",
9-
"security",
10-
"helmet"
11-
],
7+
"keywords": ["web", "security", "helmet"],
128
"author": {
139
"name": "Samuel Jensen",
1410
"url": "https://nichtsam.com"
@@ -27,14 +23,11 @@
2723
"quality": "biome check .",
2824
"quality:fix": "biome check . --write --unsafe"
2925
},
30-
"files": [
31-
"build",
32-
"package.json",
33-
"README.md"
34-
],
26+
"files": ["build", "package.json", "README.md"],
3527
"exports": {
3628
".": "./build/index.js",
37-
"./*": "./build/rules/*.js",
29+
"./node-http": "./build/node-http.js",
30+
"./rules/*": "./build/rules/*.js",
3831
"./package.json": "./package.json"
3932
},
4033
"devDependencies": {

src/index.ts

Lines changed: 39 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,76 @@
11
import {
2-
type ContentSecurityPolicyOptions,
3-
type CrossOriginEmbedderPolicyOptions,
4-
type CrossOriginOpenerPolicyOptions,
5-
type CrossOriginResourcePolicyOptions,
6-
type ReferrerPolicyOptions,
7-
type StrictTransportSecurityOptions,
8-
type XDnsPrefetchControlOptions,
9-
type XFrameOptionsOptions,
10-
type XPermittedCrossDomainPoliciesOptions,
11-
contentSecurityPolicy,
12-
crossOriginEmbedderPolicy,
13-
crossOriginOpenerPolicy,
14-
crossOriginResourcePolicy,
15-
originAgentCluster,
16-
referrerPolicy,
17-
strictTransportSecurity,
18-
xContentTypeOptions,
19-
xDnsPrefetchControl,
20-
xDownloadOptions,
21-
xFrameOptions,
22-
xPermittedCrossDomainPolicies,
23-
xXssProtection,
24-
} from "./rules/index.js";
2+
type ContentSecureOptions,
3+
contentSecurity,
4+
} from "./rules/content/index.js";
5+
import {
6+
type GeneralSecureOptions,
7+
generalSecurity,
8+
} from "./rules/general/index.js";
9+
import {
10+
type ResourceSharingSecureOptions,
11+
resourceSharing,
12+
} from "./rules/resourceSharing/index.js";
13+
14+
const headers = new Headers();
15+
helmet(headers, { content: { contentSecurityPolicy: {} } });
16+
17+
helmet(headers, { resourceSharing: true });
2518

2619
/**
27-
* Sets sensible security headers onto a Headers instance.
28-
*
29-
* This utility function configures security headers based on the provided settings.
20+
* Applies security headers to a `Headers` instance with sensible defaults.
21+
* General security headers are always set, while content and resource-sharing headers can be opted in.
3022
*/
31-
export function helmet(
32-
headers: Headers,
33-
{ options = {}, html = false, cors = false }: HelmetOptions = {},
34-
) {
35-
switch (options.crossOriginResourcePolicy) {
23+
export function helmet(headers: Headers, options: HelmetOptions = {}) {
24+
switch (options.general) {
3625
case undefined:
3726
case true:
38-
crossOriginResourcePolicy(headers, cors ? "cross-origin" : "same-origin");
27+
generalSecurity(headers);
3928
break;
4029
case false:
4130
break;
4231
default:
43-
crossOriginResourcePolicy(headers, options.crossOriginResourcePolicy);
44-
}
45-
46-
if (options.originAgentCluster ?? true) {
47-
originAgentCluster(headers);
32+
generalSecurity(headers, options.general);
4833
}
4934

50-
switch (options.referrerPolicy) {
35+
switch (options.content) {
5136
case undefined:
52-
case true:
53-
referrerPolicy(headers);
54-
break;
5537
case false:
5638
break;
57-
default:
58-
referrerPolicy(headers, options.referrerPolicy);
59-
}
60-
61-
switch (options.strictTransportSecurity) {
62-
case undefined:
6339
case true:
64-
strictTransportSecurity(headers);
65-
break;
66-
case false:
40+
contentSecurity(headers);
6741
break;
6842
default:
69-
strictTransportSecurity(headers, options.strictTransportSecurity);
43+
contentSecurity(headers, options.content);
7044
}
7145

72-
if (options.xContentTypeOptions ?? true) {
73-
xContentTypeOptions(headers);
74-
}
75-
76-
switch (options.xDnsPrefetchControl) {
46+
switch (options.resourceSharing) {
7747
case undefined:
78-
case true:
79-
xDnsPrefetchControl(headers);
80-
break;
8148
case false:
8249
break;
83-
default:
84-
xDnsPrefetchControl(headers, options.xDnsPrefetchControl);
85-
}
86-
87-
switch (options.xPermittedCrossDomainPolicies) {
88-
case undefined:
8950
case true:
90-
xPermittedCrossDomainPolicies(headers);
91-
break;
92-
case false:
51+
resourceSharing(headers);
9352
break;
9453
default:
95-
xPermittedCrossDomainPolicies(
96-
headers,
97-
options.xPermittedCrossDomainPolicies,
98-
);
99-
}
100-
101-
if (options.xXssProtection ?? true) {
102-
xXssProtection(headers);
103-
}
104-
105-
if (html) {
106-
switch (options.contentSecurityPolicy) {
107-
case undefined:
108-
case true:
109-
contentSecurityPolicy(headers);
110-
break;
111-
case false:
112-
break;
113-
default:
114-
contentSecurityPolicy(headers, options.contentSecurityPolicy);
115-
}
116-
117-
switch (options.crossOriginOpenerPolicy) {
118-
case undefined:
119-
case true:
120-
crossOriginOpenerPolicy(headers);
121-
break;
122-
case false:
123-
break;
124-
default:
125-
crossOriginOpenerPolicy(headers, options.crossOriginOpenerPolicy);
126-
}
127-
128-
switch (options.crossOriginEmbedderPolicy) {
129-
case undefined:
130-
case true:
131-
crossOriginEmbedderPolicy(headers);
132-
break;
133-
case false:
134-
break;
135-
default:
136-
crossOriginEmbedderPolicy(headers, options.crossOriginEmbedderPolicy);
137-
}
138-
139-
if (options.xDownloadOptions ?? true) {
140-
xDownloadOptions(headers);
141-
}
142-
143-
switch (options.xFrameOptions) {
144-
case undefined:
145-
case true:
146-
xFrameOptions(headers);
147-
break;
148-
case false:
149-
break;
150-
default:
151-
xFrameOptions(headers, options.xFrameOptions);
152-
}
54+
resourceSharing(headers, options.resourceSharing);
15355
}
15456
}
15557

15658
export type HelmetOptions = {
15759
/**
158-
* Options to enable, disable defaults, or customized security headers for your own need
60+
* Configures general security headers.
61+
* Enabled by default.
15962
*/
160-
options?: SecureOptions;
63+
general?: GeneralSecureOptions | boolean;
16164
/**
162-
* Whether the content type is html, this enables document specific security headers.
163-
* @default {true}
65+
* Configures security headers relevant to content, typically for `text/html` responses.
66+
* Disabled by default.
67+
*
68+
* @see https://developer.mozilla.org/en-US/docs/Glossary/Browsing_context
16469
*/
165-
html?: boolean;
70+
content?: ContentSecureOptions | boolean;
16671
/**
167-
* Whether the content is shared cross-origin, this modifys defaults to support cross origin resource sharing.
168-
* @default {false}
72+
* Configures security headers related to resource sharing (CORS).
73+
* Disabled by default.
16974
*/
170-
cors?: boolean;
171-
};
172-
173-
export type SecureOptions = GeneralSecureOptions & HtmlSpecificSecureOptions;
174-
175-
type GeneralSecureOptions = {
176-
crossOriginResourcePolicy?: CrossOriginResourcePolicyOptions | boolean;
177-
originAgentCluster?: boolean;
178-
referrerPolicy?: ReferrerPolicyOptions | boolean;
179-
strictTransportSecurity?: StrictTransportSecurityOptions | boolean;
180-
xContentTypeOptions?: boolean;
181-
xDnsPrefetchControl?: XDnsPrefetchControlOptions | boolean;
182-
xPermittedCrossDomainPolicies?:
183-
| XPermittedCrossDomainPoliciesOptions
184-
| boolean;
185-
xXssProtection?: boolean;
186-
};
187-
188-
type HtmlSpecificSecureOptions = {
189-
contentSecurityPolicy?: ContentSecurityPolicyOptions | boolean;
190-
crossOriginOpenerPolicy?: CrossOriginOpenerPolicyOptions | boolean;
191-
crossOriginEmbedderPolicy?: CrossOriginEmbedderPolicyOptions | boolean;
192-
xDownloadOptions?: boolean;
193-
xFrameOptions?: XFrameOptionsOptions | boolean;
75+
resourceSharing?: ResourceSharingSecureOptions | boolean;
19476
};

src/node-http.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { ServerResponse } from "node:http";
2+
import { type HelmetOptions, helmet as _helmet } from "./index.js";
3+
4+
/**
5+
* Sets sensible security headers onto `http.ServerResponse`.
6+
*
7+
* This utility function configures security headers based on the provided settings.
8+
*/
9+
export function helmet(response: ServerResponse, options?: HelmetOptions) {
10+
const headers = new Headers();
11+
_helmet(headers, options);
12+
response.setHeaders(headers);
13+
}

src/rules/content-security-policy.ts renamed to src/rules/content/content-security-policy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SmartString } from "../helpers/types.js";
1+
import type { SmartString } from "../../helpers/types.js";
22

33
/**
44
* Sets `Content-Security-Policy` headers onto a `Headers` instance.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)