mates-fullstack 1.0.0-beta.1
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 +311 -0
- package/dist/arctic-auth.d.ts +101 -0
- package/dist/arctic-auth.d.ts.map +1 -0
- package/dist/arctic-auth.js +538 -0
- package/dist/arctic-auth.js.map +1 -0
- package/dist/asset-manifest.d.ts +14 -0
- package/dist/asset-manifest.d.ts.map +1 -0
- package/dist/asset-manifest.js +102 -0
- package/dist/asset-manifest.js.map +1 -0
- package/dist/browser.d.ts +18 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +25 -0
- package/dist/browser.js.map +1 -0
- package/dist/build-esbuild.d.ts +29 -0
- package/dist/build-esbuild.d.ts.map +1 -0
- package/dist/build-esbuild.js +699 -0
- package/dist/build-esbuild.js.map +1 -0
- package/dist/build-prod.d.ts +126 -0
- package/dist/build-prod.d.ts.map +1 -0
- package/dist/build-prod.js +1014 -0
- package/dist/build-prod.js.map +1 -0
- package/dist/cli-new.d.ts +14 -0
- package/dist/cli-new.d.ts.map +1 -0
- package/dist/cli-new.js +637 -0
- package/dist/cli-new.js.map +1 -0
- package/dist/client.d.ts +43 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +130 -0
- package/dist/client.js.map +1 -0
- package/dist/cors.d.ts +16 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +60 -0
- package/dist/cors.js.map +1 -0
- package/dist/ctx.d.ts +78 -0
- package/dist/ctx.d.ts.map +1 -0
- package/dist/ctx.js +280 -0
- package/dist/ctx.js.map +1 -0
- package/dist/dev-watcher.d.ts +23 -0
- package/dist/dev-watcher.d.ts.map +1 -0
- package/dist/dev-watcher.js +136 -0
- package/dist/dev-watcher.js.map +1 -0
- package/dist/docs-generator.d.ts +69 -0
- package/dist/docs-generator.d.ts.map +1 -0
- package/dist/docs-generator.js +557 -0
- package/dist/docs-generator.js.map +1 -0
- package/dist/docs-page.d.ts +20 -0
- package/dist/docs-page.d.ts.map +1 -0
- package/dist/docs-page.js +1152 -0
- package/dist/docs-page.js.map +1 -0
- package/dist/download.d.ts +78 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +202 -0
- package/dist/download.js.map +1 -0
- package/dist/env-loader.d.ts +76 -0
- package/dist/env-loader.d.ts.map +1 -0
- package/dist/env-loader.js +213 -0
- package/dist/env-loader.js.map +1 -0
- package/dist/errors.d.ts +146 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +386 -0
- package/dist/errors.js.map +1 -0
- package/dist/head.d.ts +31 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/head.js +245 -0
- package/dist/head.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-prefixes.d.ts +16 -0
- package/dist/internal-prefixes.d.ts.map +1 -0
- package/dist/internal-prefixes.js +16 -0
- package/dist/internal-prefixes.js.map +1 -0
- package/dist/internal.d.ts +25 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +25 -0
- package/dist/internal.js.map +1 -0
- package/dist/jwt.d.ts +166 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +261 -0
- package/dist/jwt.js.map +1 -0
- package/dist/log.d.ts +44 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +66 -0
- package/dist/log.js.map +1 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +138 -0
- package/dist/logger.js.map +1 -0
- package/dist/main-runner.d.ts +59 -0
- package/dist/main-runner.d.ts.map +1 -0
- package/dist/main-runner.js +157 -0
- package/dist/main-runner.js.map +1 -0
- package/dist/mates-auth.d.ts +82 -0
- package/dist/mates-auth.d.ts.map +1 -0
- package/dist/mates-auth.js +323 -0
- package/dist/mates-auth.js.map +1 -0
- package/dist/middleware.d.ts +30 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +67 -0
- package/dist/middleware.js.map +1 -0
- package/dist/project-resolver.d.ts +102 -0
- package/dist/project-resolver.d.ts.map +1 -0
- package/dist/project-resolver.js +271 -0
- package/dist/project-resolver.js.map +1 -0
- package/dist/rate-limit.d.ts +37 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +109 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/redirect.d.ts +84 -0
- package/dist/redirect.d.ts.map +1 -0
- package/dist/redirect.js +105 -0
- package/dist/redirect.js.map +1 -0
- package/dist/renderer.d.ts +91 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +630 -0
- package/dist/renderer.js.map +1 -0
- package/dist/request-logger.d.ts +12 -0
- package/dist/request-logger.d.ts.map +1 -0
- package/dist/request-logger.js +55 -0
- package/dist/request-logger.js.map +1 -0
- package/dist/rest.d.ts +25 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +93 -0
- package/dist/rest.js.map +1 -0
- package/dist/router.d.ts +71 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +222 -0
- package/dist/router.js.map +1 -0
- package/dist/rpc-registry.d.ts +84 -0
- package/dist/rpc-registry.d.ts.map +1 -0
- package/dist/rpc-registry.js +271 -0
- package/dist/rpc-registry.js.map +1 -0
- package/dist/rpc-runner.d.ts +82 -0
- package/dist/rpc-runner.d.ts.map +1 -0
- package/dist/rpc-runner.js +564 -0
- package/dist/rpc-runner.js.map +1 -0
- package/dist/sanitize.d.ts +61 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +193 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/security-headers.d.ts +114 -0
- package/dist/security-headers.d.ts.map +1 -0
- package/dist/security-headers.js +121 -0
- package/dist/security-headers.js.map +1 -0
- package/dist/server-fn.d.ts +323 -0
- package/dist/server-fn.d.ts.map +1 -0
- package/dist/server-fn.js +373 -0
- package/dist/server-fn.js.map +1 -0
- package/dist/server-public.d.ts +13 -0
- package/dist/server-public.d.ts.map +1 -0
- package/dist/server-public.js +12 -0
- package/dist/server-public.js.map +1 -0
- package/dist/server-timeout.d.ts +38 -0
- package/dist/server-timeout.d.ts.map +1 -0
- package/dist/server-timeout.js +46 -0
- package/dist/server-timeout.js.map +1 -0
- package/dist/server.d.ts +100 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1218 -0
- package/dist/server.js.map +1 -0
- package/dist/socket-router.d.ts +153 -0
- package/dist/socket-router.d.ts.map +1 -0
- package/dist/socket-router.js +612 -0
- package/dist/socket-router.js.map +1 -0
- package/dist/sso.d.ts +90 -0
- package/dist/sso.d.ts.map +1 -0
- package/dist/sso.js +261 -0
- package/dist/sso.js.map +1 -0
- package/dist/ssr-context.d.ts +49 -0
- package/dist/ssr-context.d.ts.map +1 -0
- package/dist/ssr-context.js +85 -0
- package/dist/ssr-context.js.map +1 -0
- package/dist/ssr-globals.d.ts +32 -0
- package/dist/ssr-globals.d.ts.map +1 -0
- package/dist/ssr-globals.js +1010 -0
- package/dist/ssr-globals.js.map +1 -0
- package/dist/ssr-template.d.ts +73 -0
- package/dist/ssr-template.d.ts.map +1 -0
- package/dist/ssr-template.js +507 -0
- package/dist/ssr-template.js.map +1 -0
- package/dist/stack-mapper.d.ts +25 -0
- package/dist/stack-mapper.d.ts.map +1 -0
- package/dist/stack-mapper.js +139 -0
- package/dist/stack-mapper.js.map +1 -0
- package/dist/stream.d.ts +89 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +299 -0
- package/dist/stream.js.map +1 -0
- package/dist/upload.d.ts +69 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +110 -0
- package/dist/upload.js.map +1 -0
- package/dist/validate.d.ts +58 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +89 -0
- package/dist/validate.js.map +1 -0
- package/dist/verify-package.d.ts +3 -0
- package/dist/verify-package.d.ts.map +1 -0
- package/dist/verify-package.js +128 -0
- package/dist/verify-package.js.map +1 -0
- package/package.json +79 -0
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mates-ssr — sanitize.ts
|
|
3
|
+
*
|
|
4
|
+
* Automatic XSS sanitization of parsed JSON request bodies.
|
|
5
|
+
*
|
|
6
|
+
* Runs after JSON.parse on every API route body and serverFn arg list.
|
|
7
|
+
* Walks the object tree and cleans every string value.
|
|
8
|
+
*
|
|
9
|
+
* No external dependencies — pure string operations.
|
|
10
|
+
*
|
|
11
|
+
* Rules:
|
|
12
|
+
* - Only string values are touched
|
|
13
|
+
* - Numbers, booleans, null, undefined pass through unchanged
|
|
14
|
+
* - Objects and arrays are recursed into
|
|
15
|
+
* - Fields in the skip list are never touched
|
|
16
|
+
* - Circular references are safely handled
|
|
17
|
+
*/
|
|
18
|
+
// ─── Built-in fields that must never be sanitized ─────────────────────────────
|
|
19
|
+
// Passwords must preserve all characters.
|
|
20
|
+
// Tokens/secrets are cryptographic values.
|
|
21
|
+
// Content/html/markdown fields contain intentional markup.
|
|
22
|
+
const BUILTIN_SKIP = new Set([
|
|
23
|
+
"password",
|
|
24
|
+
"passwordConfirm",
|
|
25
|
+
"confirmPassword",
|
|
26
|
+
"currentPassword",
|
|
27
|
+
"newPassword",
|
|
28
|
+
"oldPassword",
|
|
29
|
+
"token",
|
|
30
|
+
"secret",
|
|
31
|
+
"apiKey",
|
|
32
|
+
"api_key",
|
|
33
|
+
"privateKey",
|
|
34
|
+
"private_key",
|
|
35
|
+
"accessToken",
|
|
36
|
+
"access_token",
|
|
37
|
+
"refreshToken",
|
|
38
|
+
"refresh_token",
|
|
39
|
+
"content",
|
|
40
|
+
"body",
|
|
41
|
+
"html",
|
|
42
|
+
"markdown",
|
|
43
|
+
"rawContent",
|
|
44
|
+
"raw_content",
|
|
45
|
+
"template",
|
|
46
|
+
"source",
|
|
47
|
+
]);
|
|
48
|
+
// ─── HTML detection ───────────────────────────────────────────────────────────
|
|
49
|
+
// Matches opening and closing HTML tags including self-closing
|
|
50
|
+
// e.g. <script>, </div>, <img src="x" onerror="xss">, <!-- comment -->
|
|
51
|
+
const HTML_TAG_REGEX = /<\/?[a-zA-Z][^>]*>|<!--|-->/g;
|
|
52
|
+
// Strict check — does the string contain any HTML tags at all?
|
|
53
|
+
function containsHtml(value) {
|
|
54
|
+
// Reset lastIndex since we reuse a stateful regex
|
|
55
|
+
HTML_TAG_REGEX.lastIndex = 0;
|
|
56
|
+
return HTML_TAG_REGEX.test(value);
|
|
57
|
+
}
|
|
58
|
+
// ─── Sanitizers ───────────────────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* Strip all HTML tags from a string.
|
|
61
|
+
* "<script>alert(1)</script>hello" → "hello"
|
|
62
|
+
* "<b>bold</b> and <i>italic</i>" → "bold and italic"
|
|
63
|
+
*/
|
|
64
|
+
function stripHtml(value) {
|
|
65
|
+
return value.replace(/<\/?[a-zA-Z][^>]*>|<!--|-->/g, "");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Escape HTML special characters.
|
|
69
|
+
* Only escapes the five characters that are dangerous in HTML context.
|
|
70
|
+
* Does NOT mangle normal text — "O'Brien" stays "O'Brien" when escaped,
|
|
71
|
+
* which round-trips correctly. Use this if you want to preserve the content
|
|
72
|
+
* but neutralize it for HTML output.
|
|
73
|
+
*/
|
|
74
|
+
function escapeHtml(value) {
|
|
75
|
+
return value
|
|
76
|
+
.replace(/&/g, "&")
|
|
77
|
+
.replace(/</g, "<")
|
|
78
|
+
.replace(/>/g, ">")
|
|
79
|
+
.replace(/"/g, """)
|
|
80
|
+
.replace(/'/g, "'");
|
|
81
|
+
}
|
|
82
|
+
// ─── Core sanitizer ───────────────────────────────────────────────────────────
|
|
83
|
+
export class SanitizeError extends Error {
|
|
84
|
+
status = 400;
|
|
85
|
+
code = "XSS_DETECTED";
|
|
86
|
+
field;
|
|
87
|
+
constructor(field) {
|
|
88
|
+
super(`HTML content is not allowed in field: "${field}"`);
|
|
89
|
+
this.name = "SanitizeError";
|
|
90
|
+
this.field = field;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Sanitize a single string value.
|
|
95
|
+
* Returns the cleaned string or throws SanitizeError in "reject" mode.
|
|
96
|
+
*/
|
|
97
|
+
export function sanitizeString(value, fieldName, mode) {
|
|
98
|
+
switch (mode) {
|
|
99
|
+
case "strip":
|
|
100
|
+
return stripHtml(value);
|
|
101
|
+
case "escape":
|
|
102
|
+
return escapeHtml(value);
|
|
103
|
+
case "reject":
|
|
104
|
+
if (containsHtml(value))
|
|
105
|
+
throw new SanitizeError(fieldName);
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Recursively sanitize all string values in an object or array.
|
|
111
|
+
*
|
|
112
|
+
* @param input The parsed JSON value (object, array, primitive)
|
|
113
|
+
* @param config Sanitize configuration
|
|
114
|
+
* @param fieldName Current field path (for error messages)
|
|
115
|
+
* @param seen WeakSet to detect circular references
|
|
116
|
+
* @returns The sanitized value (mutates strings in place for objects)
|
|
117
|
+
*/
|
|
118
|
+
export function sanitizeBody(input, config, fieldName = "body", seen = new WeakSet()) {
|
|
119
|
+
if (!config.enabled)
|
|
120
|
+
return input;
|
|
121
|
+
// Primitives other than string — pass through unchanged
|
|
122
|
+
if (input === null || input === undefined)
|
|
123
|
+
return input;
|
|
124
|
+
if (typeof input === "number")
|
|
125
|
+
return input;
|
|
126
|
+
if (typeof input === "boolean")
|
|
127
|
+
return input;
|
|
128
|
+
// String — sanitize it
|
|
129
|
+
if (typeof input === "string") {
|
|
130
|
+
return sanitizeString(input, fieldName, config.mode);
|
|
131
|
+
}
|
|
132
|
+
// Array — recurse into each element
|
|
133
|
+
if (Array.isArray(input)) {
|
|
134
|
+
if (seen.has(input))
|
|
135
|
+
return input;
|
|
136
|
+
seen.add(input);
|
|
137
|
+
return input.map((item, i) => sanitizeBody(item, config, `${fieldName}[${i}]`, seen));
|
|
138
|
+
}
|
|
139
|
+
// Object — recurse into each value, respecting skip list
|
|
140
|
+
if (typeof input === "object") {
|
|
141
|
+
if (seen.has(input))
|
|
142
|
+
return input;
|
|
143
|
+
seen.add(input);
|
|
144
|
+
const skipSet = new Set([...BUILTIN_SKIP, ...config.skip]);
|
|
145
|
+
const result = {};
|
|
146
|
+
for (const [key, value] of Object.entries(input)) {
|
|
147
|
+
// Skip fields in the skip list — return value as-is
|
|
148
|
+
if (skipSet.has(key)) {
|
|
149
|
+
result[key] = value;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
result[key] = sanitizeBody(value, config, fieldName === "body" ? key : `${fieldName}.${key}`, seen);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
// Anything else — pass through
|
|
158
|
+
return input;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Sanitize a WebSocket message.
|
|
162
|
+
* Parses JSON if the message is a string, sanitizes all string fields,
|
|
163
|
+
* and returns the cleaned object (or the original string if not JSON).
|
|
164
|
+
*
|
|
165
|
+
* @param msg Raw WebSocket message (string or Buffer)
|
|
166
|
+
* @param config Sanitize config
|
|
167
|
+
* @returns { parsed: any, raw: string } — parsed is the sanitized object,
|
|
168
|
+
* raw is the original string for pass-through if needed
|
|
169
|
+
*/
|
|
170
|
+
export function sanitizeWsMessage(msg, config) {
|
|
171
|
+
const raw = typeof msg === "string" ? msg : msg.toString("utf-8");
|
|
172
|
+
if (!config.enabled)
|
|
173
|
+
return { parsed: raw, raw, isJson: false };
|
|
174
|
+
// Try to parse as JSON
|
|
175
|
+
let parsed;
|
|
176
|
+
let isJson = false;
|
|
177
|
+
try {
|
|
178
|
+
parsed = JSON.parse(raw);
|
|
179
|
+
isJson = true;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Not JSON — treat as plain string and sanitize it directly
|
|
183
|
+
return {
|
|
184
|
+
parsed: sanitizeString(raw, "message", config.mode),
|
|
185
|
+
raw,
|
|
186
|
+
isJson: false,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// JSON parsed — sanitize all string values
|
|
190
|
+
const sanitized = sanitizeBody(parsed, config, "message");
|
|
191
|
+
return { parsed: sanitized, raw, isJson: true };
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mates-fullstack — security-headers.ts
|
|
3
|
+
*
|
|
4
|
+
* Sets all standard security response headers in one call.
|
|
5
|
+
* Every header is on by default and individually overridable or disableable.
|
|
6
|
+
*
|
|
7
|
+
* Covers the full helmet header set plus Permissions-Policy.
|
|
8
|
+
*/
|
|
9
|
+
export interface SecurityHeadersOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Strict-Transport-Security.
|
|
12
|
+
* Default in production: "max-age=31536000; includeSubDomains".
|
|
13
|
+
* Disabled in development by default.
|
|
14
|
+
* Set to false to always omit.
|
|
15
|
+
*/
|
|
16
|
+
hsts?: string | false;
|
|
17
|
+
/**
|
|
18
|
+
* X-Frame-Options — controls whether the page can be embedded in an iframe.
|
|
19
|
+
* Default: "SAMEORIGIN". Set to false to omit.
|
|
20
|
+
*/
|
|
21
|
+
xFrameOptions?: "DENY" | "SAMEORIGIN" | false;
|
|
22
|
+
/**
|
|
23
|
+
* Cross-Origin-Opener-Policy — isolates the browsing context group.
|
|
24
|
+
* Default: "same-origin". Set to false to omit.
|
|
25
|
+
*/
|
|
26
|
+
crossOriginOpenerPolicy?: string | false;
|
|
27
|
+
/**
|
|
28
|
+
* Cross-Origin-Embedder-Policy — requires resources to opt in to being loaded.
|
|
29
|
+
* Needed for SharedArrayBuffer / high-res timers.
|
|
30
|
+
* Default: "require-corp". Set to false or "unsafe-none" to disable.
|
|
31
|
+
*
|
|
32
|
+
* Note: "require-corp" will block third-party resources (CDN fonts, images, etc.)
|
|
33
|
+
* that do not send CORP or CORS headers. Set to false if your app loads
|
|
34
|
+
* resources from external origins without CORS headers.
|
|
35
|
+
*/
|
|
36
|
+
crossOriginEmbedderPolicy?: string | false;
|
|
37
|
+
/**
|
|
38
|
+
* Cross-Origin-Resource-Policy — prevents other origins from loading your resources.
|
|
39
|
+
* Default: "same-origin". Set to false to omit.
|
|
40
|
+
*/
|
|
41
|
+
crossOriginResourcePolicy?: string | false;
|
|
42
|
+
/**
|
|
43
|
+
* X-Content-Type-Options: nosniff — prevents MIME-type sniffing.
|
|
44
|
+
* Default: true.
|
|
45
|
+
*/
|
|
46
|
+
xContentTypeOptions?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* X-XSS-Protection: 0 — explicitly disables the legacy XSS filter.
|
|
49
|
+
* Disabling it is the modern recommendation (the filter can introduce vulnerabilities).
|
|
50
|
+
* Default: true (sends "0").
|
|
51
|
+
*/
|
|
52
|
+
xXssProtection?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Referrer-Policy.
|
|
55
|
+
* Default: "no-referrer". Set to false to omit.
|
|
56
|
+
*/
|
|
57
|
+
referrerPolicy?: string | false;
|
|
58
|
+
/**
|
|
59
|
+
* X-DNS-Prefetch-Control — disables browser DNS prefetching.
|
|
60
|
+
* Default: "off". Set to false to omit.
|
|
61
|
+
*/
|
|
62
|
+
xDnsPrefetchControl?: "on" | "off" | false;
|
|
63
|
+
/**
|
|
64
|
+
* X-Permitted-Cross-Domain-Policies — disallows Adobe Flash/Acrobat cross-domain requests.
|
|
65
|
+
* Default: "none". Set to false to omit.
|
|
66
|
+
*/
|
|
67
|
+
xPermittedCrossDomainPolicies?: string | false;
|
|
68
|
+
/**
|
|
69
|
+
* X-Download-Options: noopen — prevents IE from executing downloads in site context.
|
|
70
|
+
* Default: true. Set to false to omit.
|
|
71
|
+
*/
|
|
72
|
+
xDownloadOptions?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Origin-Agent-Cluster: ?1 — hints the browser to isolate this origin in its own agent cluster.
|
|
75
|
+
* Default: true. Set to false to omit.
|
|
76
|
+
*/
|
|
77
|
+
originAgentCluster?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Content-Security-Policy.
|
|
80
|
+
* Not set by default — CSP is highly application-specific.
|
|
81
|
+
* Set to false to explicitly omit.
|
|
82
|
+
*/
|
|
83
|
+
contentSecurityPolicy?: string | false;
|
|
84
|
+
/**
|
|
85
|
+
* Permissions-Policy (formerly Feature-Policy).
|
|
86
|
+
* Not set by default — policy is application-specific.
|
|
87
|
+
* Set to false to explicitly omit.
|
|
88
|
+
*/
|
|
89
|
+
permissionsPolicy?: string | false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Register all standard security headers on every response.
|
|
93
|
+
*
|
|
94
|
+
* Call once at module level in server/main.ts.
|
|
95
|
+
* Every header has a safe default — pass options to override or disable individual headers.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* // All defaults
|
|
99
|
+
* useSecurityHeaders();
|
|
100
|
+
*
|
|
101
|
+
* // Custom CSP + preload HSTS
|
|
102
|
+
* useSecurityHeaders({
|
|
103
|
+
* contentSecurityPolicy: "default-src 'self'",
|
|
104
|
+
* hsts: "max-age=63072000; includeSubDomains; preload",
|
|
105
|
+
* });
|
|
106
|
+
*
|
|
107
|
+
* // Disable COEP for apps that load cross-origin resources without CORS headers
|
|
108
|
+
* useSecurityHeaders({ crossOriginEmbedderPolicy: false });
|
|
109
|
+
*
|
|
110
|
+
* // Disable a header entirely
|
|
111
|
+
* useSecurityHeaders({ xFrameOptions: false });
|
|
112
|
+
*/
|
|
113
|
+
export declare function useSecurityHeaders(options?: SecurityHeadersOptions): void;
|
|
114
|
+
//# sourceMappingURL=security-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,sBAAsB;IAGrC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAItB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,KAAK,CAAC;IAE9C;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAEzC;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE3C;;;OAGG;IACH,yBAAyB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAI3C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAIzB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;IAI3C;;;OAGG;IACH,6BAA6B,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE/C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAI7B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAEvC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,sBAA2B,GAAG,IAAI,CA+G7E"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mates-fullstack — security-headers.ts
|
|
3
|
+
*
|
|
4
|
+
* Sets all standard security response headers in one call.
|
|
5
|
+
* Every header is on by default and individually overridable or disableable.
|
|
6
|
+
*
|
|
7
|
+
* Covers the full helmet header set plus Permissions-Policy.
|
|
8
|
+
*/
|
|
9
|
+
import { onRequest } from "./middleware.js";
|
|
10
|
+
/**
|
|
11
|
+
* Register all standard security headers on every response.
|
|
12
|
+
*
|
|
13
|
+
* Call once at module level in server/main.ts.
|
|
14
|
+
* Every header has a safe default — pass options to override or disable individual headers.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // All defaults
|
|
18
|
+
* useSecurityHeaders();
|
|
19
|
+
*
|
|
20
|
+
* // Custom CSP + preload HSTS
|
|
21
|
+
* useSecurityHeaders({
|
|
22
|
+
* contentSecurityPolicy: "default-src 'self'",
|
|
23
|
+
* hsts: "max-age=63072000; includeSubDomains; preload",
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Disable COEP for apps that load cross-origin resources without CORS headers
|
|
27
|
+
* useSecurityHeaders({ crossOriginEmbedderPolicy: false });
|
|
28
|
+
*
|
|
29
|
+
* // Disable a header entirely
|
|
30
|
+
* useSecurityHeaders({ xFrameOptions: false });
|
|
31
|
+
*/
|
|
32
|
+
export function useSecurityHeaders(options = {}) {
|
|
33
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
34
|
+
const resolved = {};
|
|
35
|
+
// X-Content-Type-Options
|
|
36
|
+
if (options.xContentTypeOptions !== false) {
|
|
37
|
+
resolved["x-content-type-options"] = "nosniff";
|
|
38
|
+
}
|
|
39
|
+
// X-Frame-Options
|
|
40
|
+
const xFrame = options.xFrameOptions !== undefined ? options.xFrameOptions : "SAMEORIGIN";
|
|
41
|
+
if (xFrame !== false) {
|
|
42
|
+
resolved["x-frame-options"] = xFrame;
|
|
43
|
+
}
|
|
44
|
+
// X-XSS-Protection: 0 (disable the legacy filter)
|
|
45
|
+
if (options.xXssProtection !== false) {
|
|
46
|
+
resolved["x-xss-protection"] = "0";
|
|
47
|
+
}
|
|
48
|
+
// X-DNS-Prefetch-Control
|
|
49
|
+
const dns = options.xDnsPrefetchControl !== undefined
|
|
50
|
+
? options.xDnsPrefetchControl
|
|
51
|
+
: "off";
|
|
52
|
+
if (dns !== false) {
|
|
53
|
+
resolved["x-dns-prefetch-control"] = dns;
|
|
54
|
+
}
|
|
55
|
+
// X-Download-Options
|
|
56
|
+
if (options.xDownloadOptions !== false) {
|
|
57
|
+
resolved["x-download-options"] = "noopen";
|
|
58
|
+
}
|
|
59
|
+
// X-Permitted-Cross-Domain-Policies
|
|
60
|
+
const xPdp = options.xPermittedCrossDomainPolicies !== undefined
|
|
61
|
+
? options.xPermittedCrossDomainPolicies
|
|
62
|
+
: "none";
|
|
63
|
+
if (xPdp !== false) {
|
|
64
|
+
resolved["x-permitted-cross-domain-policies"] = xPdp;
|
|
65
|
+
}
|
|
66
|
+
// Referrer-Policy
|
|
67
|
+
const referrer = options.referrerPolicy !== undefined
|
|
68
|
+
? options.referrerPolicy
|
|
69
|
+
: "no-referrer";
|
|
70
|
+
if (referrer !== false) {
|
|
71
|
+
resolved["referrer-policy"] = referrer;
|
|
72
|
+
}
|
|
73
|
+
// Strict-Transport-Security — production only by default
|
|
74
|
+
const hsts = options.hsts !== undefined
|
|
75
|
+
? options.hsts
|
|
76
|
+
: isProd
|
|
77
|
+
? "max-age=31536000; includeSubDomains"
|
|
78
|
+
: false;
|
|
79
|
+
if (hsts !== false) {
|
|
80
|
+
resolved["strict-transport-security"] = hsts;
|
|
81
|
+
}
|
|
82
|
+
// Origin-Agent-Cluster
|
|
83
|
+
if (options.originAgentCluster !== false) {
|
|
84
|
+
resolved["origin-agent-cluster"] = "?1";
|
|
85
|
+
}
|
|
86
|
+
// Cross-Origin-Opener-Policy
|
|
87
|
+
const coop = options.crossOriginOpenerPolicy !== undefined
|
|
88
|
+
? options.crossOriginOpenerPolicy
|
|
89
|
+
: "same-origin";
|
|
90
|
+
if (coop !== false) {
|
|
91
|
+
resolved["cross-origin-opener-policy"] = coop;
|
|
92
|
+
}
|
|
93
|
+
// Cross-Origin-Embedder-Policy
|
|
94
|
+
const coep = options.crossOriginEmbedderPolicy !== undefined
|
|
95
|
+
? options.crossOriginEmbedderPolicy
|
|
96
|
+
: "require-corp";
|
|
97
|
+
if (coep !== false) {
|
|
98
|
+
resolved["cross-origin-embedder-policy"] = coep;
|
|
99
|
+
}
|
|
100
|
+
// Cross-Origin-Resource-Policy
|
|
101
|
+
const corp = options.crossOriginResourcePolicy !== undefined
|
|
102
|
+
? options.crossOriginResourcePolicy
|
|
103
|
+
: "same-origin";
|
|
104
|
+
if (corp !== false) {
|
|
105
|
+
resolved["cross-origin-resource-policy"] = corp;
|
|
106
|
+
}
|
|
107
|
+
// Content-Security-Policy (opt-in — too app-specific for a default)
|
|
108
|
+
if (options.contentSecurityPolicy) {
|
|
109
|
+
resolved["content-security-policy"] = options.contentSecurityPolicy;
|
|
110
|
+
}
|
|
111
|
+
// Permissions-Policy (opt-in — too app-specific for a default)
|
|
112
|
+
if (options.permissionsPolicy) {
|
|
113
|
+
resolved["permissions-policy"] = options.permissionsPolicy;
|
|
114
|
+
}
|
|
115
|
+
onRequest((c) => {
|
|
116
|
+
for (const [key, value] of Object.entries(resolved)) {
|
|
117
|
+
c.resHeaders[key] = value;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=security-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../src/security-headers.ts"],"names":[],"mappings":"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"}
|