@uniforge/core 0.1.0-alpha.2
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/auth/index.d.cts +165 -0
- package/dist/auth/index.d.ts +165 -0
- package/dist/auth/index.js +443 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/index.mjs +406 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/billing/index.d.cts +34 -0
- package/dist/billing/index.d.ts +34 -0
- package/dist/billing/index.js +254 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/index.mjs +225 -0
- package/dist/billing/index.mjs.map +1 -0
- package/dist/config/index.d.cts +12 -0
- package/dist/config/index.d.ts +12 -0
- package/dist/config/index.js +186 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +156 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/database/index.d.cts +33 -0
- package/dist/database/index.d.ts +33 -0
- package/dist/database/index.js +127 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/index.mjs +95 -0
- package/dist/database/index.mjs.map +1 -0
- package/dist/graphql/index.d.cts +36 -0
- package/dist/graphql/index.d.ts +36 -0
- package/dist/graphql/index.js +209 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/index.mjs +179 -0
- package/dist/graphql/index.mjs.map +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/multi-store/index.d.cts +11 -0
- package/dist/multi-store/index.d.ts +11 -0
- package/dist/multi-store/index.js +473 -0
- package/dist/multi-store/index.js.map +1 -0
- package/dist/multi-store/index.mjs +447 -0
- package/dist/multi-store/index.mjs.map +1 -0
- package/dist/multi-tenant/index.d.cts +23 -0
- package/dist/multi-tenant/index.d.ts +23 -0
- package/dist/multi-tenant/index.js +69 -0
- package/dist/multi-tenant/index.js.map +1 -0
- package/dist/multi-tenant/index.mjs +41 -0
- package/dist/multi-tenant/index.mjs.map +1 -0
- package/dist/performance/index.d.cts +34 -0
- package/dist/performance/index.d.ts +34 -0
- package/dist/performance/index.js +319 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/index.mjs +290 -0
- package/dist/performance/index.mjs.map +1 -0
- package/dist/platform/index.d.cts +25 -0
- package/dist/platform/index.d.ts +25 -0
- package/dist/platform/index.js +91 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +62 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/rbac/index.d.cts +24 -0
- package/dist/rbac/index.d.ts +24 -0
- package/dist/rbac/index.js +267 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/rbac/index.mjs +236 -0
- package/dist/rbac/index.mjs.map +1 -0
- package/dist/schema-CM7mHj_H.d.cts +53 -0
- package/dist/schema-CM7mHj_H.d.ts +53 -0
- package/dist/security/index.d.cts +47 -0
- package/dist/security/index.d.ts +47 -0
- package/dist/security/index.js +505 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/index.mjs +474 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/session-storage/index.d.cts +70 -0
- package/dist/session-storage/index.d.ts +70 -0
- package/dist/session-storage/index.js +271 -0
- package/dist/session-storage/index.js.map +1 -0
- package/dist/session-storage/index.mjs +242 -0
- package/dist/session-storage/index.mjs.map +1 -0
- package/dist/webhooks/index.d.cts +89 -0
- package/dist/webhooks/index.d.ts +89 -0
- package/dist/webhooks/index.js +380 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/index.mjs +348 -0
- package/dist/webhooks/index.mjs.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { InputValidator, SecurityHeadersConfig, SecurityHeadersMiddleware, CSPDirectives, CSPMiddleware, RateLimitConfig, RequestRateLimiter, SecurityAuditor } from '@uniforge/platform-core/security';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Input validation implementation.
|
|
5
|
+
*
|
|
6
|
+
* Factory function that creates an InputValidator with methods for
|
|
7
|
+
* validating and sanitizing user input including shop domains, emails,
|
|
8
|
+
* URLs, API keys, and custom patterns.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Create an InputValidator with standard validation and sanitization methods. */
|
|
12
|
+
declare function createInputValidator(): InputValidator;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Security headers middleware implementation.
|
|
16
|
+
*
|
|
17
|
+
* Factory functions for applying security-related HTTP headers
|
|
18
|
+
* including HSTS, X-Content-Type-Options, X-Frame-Options,
|
|
19
|
+
* X-XSS-Protection, Referrer-Policy, and Content Security Policy.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Create a SecurityHeadersMiddleware that applies standard security headers to responses. */
|
|
23
|
+
declare function createSecurityHeadersMiddleware(config: SecurityHeadersConfig): SecurityHeadersMiddleware;
|
|
24
|
+
/** Create a CSPMiddleware that applies Content Security Policy headers to responses. */
|
|
25
|
+
declare function createCSPMiddleware(directives: CSPDirectives): CSPMiddleware;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* In-memory sliding window rate limiter.
|
|
29
|
+
*
|
|
30
|
+
* Tracks request timestamps per key and enforces a maximum
|
|
31
|
+
* number of requests within a configurable time window.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** Create a RequestRateLimiter using an in-memory sliding window algorithm. */
|
|
35
|
+
declare function createRateLimiter(config: RateLimitConfig): RequestRateLimiter;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Security auditor implementation.
|
|
39
|
+
*
|
|
40
|
+
* Factory function that creates a SecurityAuditor capable of running
|
|
41
|
+
* security configuration checks for encryption, headers, and more.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** Create a SecurityAuditor that checks encryption and header configurations. */
|
|
45
|
+
declare function createSecurityAuditor(): SecurityAuditor;
|
|
46
|
+
|
|
47
|
+
export { createCSPMiddleware, createInputValidator, createRateLimiter, createSecurityAuditor, createSecurityHeadersMiddleware };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { InputValidator, SecurityHeadersConfig, SecurityHeadersMiddleware, CSPDirectives, CSPMiddleware, RateLimitConfig, RequestRateLimiter, SecurityAuditor } from '@uniforge/platform-core/security';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Input validation implementation.
|
|
5
|
+
*
|
|
6
|
+
* Factory function that creates an InputValidator with methods for
|
|
7
|
+
* validating and sanitizing user input including shop domains, emails,
|
|
8
|
+
* URLs, API keys, and custom patterns.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Create an InputValidator with standard validation and sanitization methods. */
|
|
12
|
+
declare function createInputValidator(): InputValidator;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Security headers middleware implementation.
|
|
16
|
+
*
|
|
17
|
+
* Factory functions for applying security-related HTTP headers
|
|
18
|
+
* including HSTS, X-Content-Type-Options, X-Frame-Options,
|
|
19
|
+
* X-XSS-Protection, Referrer-Policy, and Content Security Policy.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** Create a SecurityHeadersMiddleware that applies standard security headers to responses. */
|
|
23
|
+
declare function createSecurityHeadersMiddleware(config: SecurityHeadersConfig): SecurityHeadersMiddleware;
|
|
24
|
+
/** Create a CSPMiddleware that applies Content Security Policy headers to responses. */
|
|
25
|
+
declare function createCSPMiddleware(directives: CSPDirectives): CSPMiddleware;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* In-memory sliding window rate limiter.
|
|
29
|
+
*
|
|
30
|
+
* Tracks request timestamps per key and enforces a maximum
|
|
31
|
+
* number of requests within a configurable time window.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** Create a RequestRateLimiter using an in-memory sliding window algorithm. */
|
|
35
|
+
declare function createRateLimiter(config: RateLimitConfig): RequestRateLimiter;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Security auditor implementation.
|
|
39
|
+
*
|
|
40
|
+
* Factory function that creates a SecurityAuditor capable of running
|
|
41
|
+
* security configuration checks for encryption, headers, and more.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** Create a SecurityAuditor that checks encryption and header configurations. */
|
|
45
|
+
declare function createSecurityAuditor(): SecurityAuditor;
|
|
46
|
+
|
|
47
|
+
export { createCSPMiddleware, createInputValidator, createRateLimiter, createSecurityAuditor, createSecurityHeadersMiddleware };
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/security/index.ts
|
|
21
|
+
var security_exports = {};
|
|
22
|
+
__export(security_exports, {
|
|
23
|
+
createCSPMiddleware: () => createCSPMiddleware,
|
|
24
|
+
createInputValidator: () => createInputValidator,
|
|
25
|
+
createRateLimiter: () => createRateLimiter,
|
|
26
|
+
createSecurityAuditor: () => createSecurityAuditor,
|
|
27
|
+
createSecurityHeadersMiddleware: () => createSecurityHeadersMiddleware
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(security_exports);
|
|
30
|
+
|
|
31
|
+
// src/security/input-validator.ts
|
|
32
|
+
var SHOP_DOMAIN_RE = /^[a-zA-Z0-9][a-zA-Z0-9-]*\.myshopify\.com$/;
|
|
33
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
34
|
+
var API_KEY_RE = /^[a-zA-Z0-9-]{32,64}$/;
|
|
35
|
+
var HTML_TAG_RE = /<[^>]*>/g;
|
|
36
|
+
function createInputValidator() {
|
|
37
|
+
function isValidShopDomain(domain) {
|
|
38
|
+
return SHOP_DOMAIN_RE.test(domain);
|
|
39
|
+
}
|
|
40
|
+
function isValidEmail(email) {
|
|
41
|
+
return EMAIL_RE.test(email);
|
|
42
|
+
}
|
|
43
|
+
function isValidUrl(url) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = new URL(url);
|
|
46
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isValidApiKey(key) {
|
|
52
|
+
return API_KEY_RE.test(key);
|
|
53
|
+
}
|
|
54
|
+
function sanitize(value, options) {
|
|
55
|
+
let result = value;
|
|
56
|
+
if (options?.stripHtml) {
|
|
57
|
+
result = result.replace(HTML_TAG_RE, "");
|
|
58
|
+
}
|
|
59
|
+
if (options?.maxLength != null && result.length > options.maxLength) {
|
|
60
|
+
result = result.slice(0, options.maxLength);
|
|
61
|
+
}
|
|
62
|
+
if (options?.allowedPattern !== void 0) {
|
|
63
|
+
const matches = result.match(options.allowedPattern);
|
|
64
|
+
result = matches ? matches.join("") : "";
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
function validateField(fieldValue, rule) {
|
|
69
|
+
const value = fieldValue;
|
|
70
|
+
if (rule.required && (value == null || value === "")) {
|
|
71
|
+
return {
|
|
72
|
+
field: rule.field,
|
|
73
|
+
message: rule.message ?? `${rule.field} is required`,
|
|
74
|
+
code: "REQUIRED"
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (value == null || value === "") {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
const strValue = String(value);
|
|
81
|
+
if (rule.maxLength != null && strValue.length > rule.maxLength) {
|
|
82
|
+
return {
|
|
83
|
+
field: rule.field,
|
|
84
|
+
message: rule.message ?? `${rule.field} exceeds maximum length of ${rule.maxLength}`,
|
|
85
|
+
code: "MAX_LENGTH"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
switch (rule.type) {
|
|
89
|
+
case "shopDomain":
|
|
90
|
+
if (!isValidShopDomain(strValue)) {
|
|
91
|
+
return {
|
|
92
|
+
field: rule.field,
|
|
93
|
+
message: rule.message ?? `${rule.field} is not a valid shop domain`,
|
|
94
|
+
code: "INVALID_SHOP_DOMAIN"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
case "email":
|
|
99
|
+
if (!isValidEmail(strValue)) {
|
|
100
|
+
return {
|
|
101
|
+
field: rule.field,
|
|
102
|
+
message: rule.message ?? `${rule.field} is not a valid email`,
|
|
103
|
+
code: "INVALID_EMAIL"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case "url":
|
|
108
|
+
if (!isValidUrl(strValue)) {
|
|
109
|
+
return {
|
|
110
|
+
field: rule.field,
|
|
111
|
+
message: rule.message ?? `${rule.field} is not a valid URL`,
|
|
112
|
+
code: "INVALID_URL"
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case "apiKey":
|
|
117
|
+
if (!isValidApiKey(strValue)) {
|
|
118
|
+
return {
|
|
119
|
+
field: rule.field,
|
|
120
|
+
message: rule.message ?? `${rule.field} is not a valid API key`,
|
|
121
|
+
code: "INVALID_API_KEY"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case "custom":
|
|
126
|
+
if (rule.pattern !== void 0 && !rule.pattern.test(strValue)) {
|
|
127
|
+
return {
|
|
128
|
+
field: rule.field,
|
|
129
|
+
message: rule.message ?? `${rule.field} does not match the required pattern`,
|
|
130
|
+
code: "PATTERN_MISMATCH"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case "string":
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function validate(input, rules) {
|
|
140
|
+
const errors = [];
|
|
141
|
+
const sanitized = {};
|
|
142
|
+
for (const rule of rules) {
|
|
143
|
+
const rawValue = input[rule.field];
|
|
144
|
+
const error = validateField(rawValue, rule);
|
|
145
|
+
if (error) {
|
|
146
|
+
errors.push(error);
|
|
147
|
+
} else if (rawValue != null && rawValue !== "") {
|
|
148
|
+
sanitized[rule.field] = String(rawValue);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
valid: errors.length === 0,
|
|
153
|
+
errors,
|
|
154
|
+
sanitized
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
validate,
|
|
159
|
+
sanitize,
|
|
160
|
+
isValidShopDomain,
|
|
161
|
+
isValidEmail,
|
|
162
|
+
isValidUrl,
|
|
163
|
+
isValidApiKey
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/security/security-headers.ts
|
|
168
|
+
function createSecurityHeadersMiddleware(config) {
|
|
169
|
+
function applyHeaders(res) {
|
|
170
|
+
if (config.hsts) {
|
|
171
|
+
const maxAge = config.hstsMaxAge ?? 31536e3;
|
|
172
|
+
let value = `max-age=${maxAge}`;
|
|
173
|
+
if (config.hstsIncludeSubDomains === true) {
|
|
174
|
+
value += "; includeSubDomains";
|
|
175
|
+
}
|
|
176
|
+
res.setHeader("Strict-Transport-Security", value);
|
|
177
|
+
}
|
|
178
|
+
if (config.noSniff) {
|
|
179
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
180
|
+
}
|
|
181
|
+
res.setHeader("X-Frame-Options", config.frameOptions);
|
|
182
|
+
if (config.xssProtection) {
|
|
183
|
+
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
184
|
+
}
|
|
185
|
+
res.setHeader("Referrer-Policy", config.referrerPolicy);
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
config,
|
|
189
|
+
applyHeaders
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function buildCSPString(directives) {
|
|
193
|
+
const parts = [];
|
|
194
|
+
const directiveMap = [
|
|
195
|
+
["default-src", directives.defaultSrc],
|
|
196
|
+
["script-src", directives.scriptSrc],
|
|
197
|
+
["style-src", directives.styleSrc],
|
|
198
|
+
["img-src", directives.imgSrc],
|
|
199
|
+
["connect-src", directives.connectSrc],
|
|
200
|
+
["font-src", directives.fontSrc],
|
|
201
|
+
["frame-src", directives.frameSrc],
|
|
202
|
+
["frame-ancestors", directives.frameAncestors]
|
|
203
|
+
];
|
|
204
|
+
for (const [name, values] of directiveMap) {
|
|
205
|
+
if (values.length > 0) {
|
|
206
|
+
parts.push(`${name} ${values.join(" ")}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return parts.join("; ");
|
|
210
|
+
}
|
|
211
|
+
function createCSPMiddleware(directives) {
|
|
212
|
+
function toHeaderString() {
|
|
213
|
+
return buildCSPString(directives);
|
|
214
|
+
}
|
|
215
|
+
function applyCSP(res) {
|
|
216
|
+
const headerValue = toHeaderString();
|
|
217
|
+
if (headerValue) {
|
|
218
|
+
res.setHeader("Content-Security-Policy", headerValue);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
directives,
|
|
223
|
+
applyCSP,
|
|
224
|
+
toHeaderString
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/security/rate-limiter.ts
|
|
229
|
+
var CLEANUP_THRESHOLD = 1e4;
|
|
230
|
+
function createRateLimiter(config) {
|
|
231
|
+
const windows = /* @__PURE__ */ new Map();
|
|
232
|
+
function cleanupStaleEntries() {
|
|
233
|
+
if (windows.size <= CLEANUP_THRESHOLD) return;
|
|
234
|
+
const now = Date.now();
|
|
235
|
+
const cutoff = now - config.windowMs;
|
|
236
|
+
for (const [key, timestamps] of windows) {
|
|
237
|
+
if (timestamps.length === 0 || timestamps[timestamps.length - 1] <= cutoff) {
|
|
238
|
+
windows.delete(key);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function resolveKey(key) {
|
|
243
|
+
if (config.keyPrefix !== void 0) {
|
|
244
|
+
return `${config.keyPrefix}:${key}`;
|
|
245
|
+
}
|
|
246
|
+
return key;
|
|
247
|
+
}
|
|
248
|
+
async function check(key) {
|
|
249
|
+
cleanupStaleEntries();
|
|
250
|
+
const resolvedKey = resolveKey(key);
|
|
251
|
+
const now = Date.now();
|
|
252
|
+
const windowStart = now - config.windowMs;
|
|
253
|
+
let timestamps = windows.get(resolvedKey);
|
|
254
|
+
if (!timestamps) {
|
|
255
|
+
timestamps = [];
|
|
256
|
+
windows.set(resolvedKey, timestamps);
|
|
257
|
+
}
|
|
258
|
+
const validTimestamps = timestamps.filter((t) => t > windowStart);
|
|
259
|
+
windows.set(resolvedKey, validTimestamps);
|
|
260
|
+
const remaining = Math.max(0, config.maxRequests - validTimestamps.length);
|
|
261
|
+
const resetAt = now + config.windowMs;
|
|
262
|
+
if (validTimestamps.length >= config.maxRequests) {
|
|
263
|
+
const earliest = validTimestamps[0];
|
|
264
|
+
const retryAfter = earliest + config.windowMs - now;
|
|
265
|
+
return {
|
|
266
|
+
allowed: false,
|
|
267
|
+
remaining: 0,
|
|
268
|
+
resetAt,
|
|
269
|
+
retryAfter: Math.max(0, retryAfter)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
validTimestamps.push(now);
|
|
273
|
+
return {
|
|
274
|
+
allowed: true,
|
|
275
|
+
remaining: remaining - 1,
|
|
276
|
+
resetAt
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
async function reset(key) {
|
|
280
|
+
const resolvedKey = resolveKey(key);
|
|
281
|
+
windows.delete(resolvedKey);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
config,
|
|
285
|
+
check,
|
|
286
|
+
reset
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/security/security-auditor.ts
|
|
291
|
+
var SEVERITY_PENALTY = {
|
|
292
|
+
critical: 25,
|
|
293
|
+
high: 15,
|
|
294
|
+
medium: 10,
|
|
295
|
+
low: 5,
|
|
296
|
+
info: 0
|
|
297
|
+
};
|
|
298
|
+
function createFinding(id, severity, category, title, description, recommendation) {
|
|
299
|
+
return { id, severity, category, title, description, recommendation };
|
|
300
|
+
}
|
|
301
|
+
function calculateScore(findings) {
|
|
302
|
+
let score = 100;
|
|
303
|
+
for (const finding of findings) {
|
|
304
|
+
score -= SEVERITY_PENALTY[finding.severity];
|
|
305
|
+
}
|
|
306
|
+
return Math.max(0, score);
|
|
307
|
+
}
|
|
308
|
+
function buildResult(findings) {
|
|
309
|
+
const score = calculateScore(findings);
|
|
310
|
+
const hasCriticalOrHigh = findings.some(
|
|
311
|
+
(f) => f.severity === "critical" || f.severity === "high"
|
|
312
|
+
);
|
|
313
|
+
return {
|
|
314
|
+
passed: !hasCriticalOrHigh && score >= 70,
|
|
315
|
+
score,
|
|
316
|
+
findings,
|
|
317
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function createSecurityAuditor() {
|
|
321
|
+
function checkEncryption(config) {
|
|
322
|
+
const findings = [];
|
|
323
|
+
if (!config["encryptionKey"] && !config["ENCRYPTION_KEY"]) {
|
|
324
|
+
findings.push(
|
|
325
|
+
createFinding(
|
|
326
|
+
"ENC-001",
|
|
327
|
+
"critical",
|
|
328
|
+
"encryption",
|
|
329
|
+
"Missing encryption key",
|
|
330
|
+
"No encryption key is configured in the application.",
|
|
331
|
+
"Set the ENCRYPTION_KEY environment variable or encryptionKey config property."
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
} else {
|
|
335
|
+
const key = config["encryptionKey"] ?? config["ENCRYPTION_KEY"];
|
|
336
|
+
if (key.length < 32) {
|
|
337
|
+
findings.push(
|
|
338
|
+
createFinding(
|
|
339
|
+
"ENC-002",
|
|
340
|
+
"high",
|
|
341
|
+
"encryption",
|
|
342
|
+
"Encryption key too short",
|
|
343
|
+
`Encryption key is ${key.length} characters, which may be insufficient for AES-256.`,
|
|
344
|
+
"Use a key of at least 32 characters (256 bits) for AES-256-GCM encryption."
|
|
345
|
+
)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (config["algorithm"] && config["algorithm"] !== "aes-256-gcm") {
|
|
350
|
+
findings.push(
|
|
351
|
+
createFinding(
|
|
352
|
+
"ENC-003",
|
|
353
|
+
"medium",
|
|
354
|
+
"encryption",
|
|
355
|
+
"Non-standard encryption algorithm",
|
|
356
|
+
`Encryption algorithm "${String(config["algorithm"])}" is not the recommended AES-256-GCM.`,
|
|
357
|
+
"Use AES-256-GCM for authenticated encryption."
|
|
358
|
+
)
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
return buildResult(findings);
|
|
362
|
+
}
|
|
363
|
+
function checkHeaders(headers) {
|
|
364
|
+
const findings = [];
|
|
365
|
+
if (!headers["strict-transport-security"] && !headers["Strict-Transport-Security"]) {
|
|
366
|
+
findings.push(
|
|
367
|
+
createFinding(
|
|
368
|
+
"HDR-001",
|
|
369
|
+
"high",
|
|
370
|
+
"headers",
|
|
371
|
+
"Missing HSTS header",
|
|
372
|
+
"Strict-Transport-Security header is not configured.",
|
|
373
|
+
"Enable HSTS with a minimum max-age of 31536000 (1 year)."
|
|
374
|
+
)
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
if (!headers["x-content-type-options"] && !headers["X-Content-Type-Options"]) {
|
|
378
|
+
findings.push(
|
|
379
|
+
createFinding(
|
|
380
|
+
"HDR-002",
|
|
381
|
+
"medium",
|
|
382
|
+
"headers",
|
|
383
|
+
"Missing X-Content-Type-Options header",
|
|
384
|
+
"X-Content-Type-Options header is not set to nosniff.",
|
|
385
|
+
"Set X-Content-Type-Options: nosniff to prevent MIME type sniffing."
|
|
386
|
+
)
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
if (!headers["x-frame-options"] && !headers["X-Frame-Options"]) {
|
|
390
|
+
findings.push(
|
|
391
|
+
createFinding(
|
|
392
|
+
"HDR-003",
|
|
393
|
+
"medium",
|
|
394
|
+
"headers",
|
|
395
|
+
"Missing X-Frame-Options header",
|
|
396
|
+
"X-Frame-Options header is not configured.",
|
|
397
|
+
"Set X-Frame-Options to DENY or SAMEORIGIN to prevent clickjacking."
|
|
398
|
+
)
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (!headers["content-security-policy"] && !headers["Content-Security-Policy"]) {
|
|
402
|
+
findings.push(
|
|
403
|
+
createFinding(
|
|
404
|
+
"HDR-004",
|
|
405
|
+
"high",
|
|
406
|
+
"headers",
|
|
407
|
+
"Missing Content-Security-Policy header",
|
|
408
|
+
"Content-Security-Policy header is not configured.",
|
|
409
|
+
"Define a Content Security Policy to mitigate XSS and data injection attacks."
|
|
410
|
+
)
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
return buildResult(findings);
|
|
414
|
+
}
|
|
415
|
+
function audit(config) {
|
|
416
|
+
const allFindings = [];
|
|
417
|
+
if (config.checkEncryption) {
|
|
418
|
+
allFindings.push(
|
|
419
|
+
createFinding(
|
|
420
|
+
"AUD-001",
|
|
421
|
+
"info",
|
|
422
|
+
"audit",
|
|
423
|
+
"Encryption check enabled",
|
|
424
|
+
"Encryption configuration check is enabled. Use checkEncryption() for detailed results.",
|
|
425
|
+
"Run checkEncryption() with your application config for detailed findings."
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (config.checkHeaders) {
|
|
430
|
+
allFindings.push(
|
|
431
|
+
createFinding(
|
|
432
|
+
"AUD-002",
|
|
433
|
+
"info",
|
|
434
|
+
"audit",
|
|
435
|
+
"Headers check enabled",
|
|
436
|
+
"Security headers check is enabled. Use checkHeaders() for detailed results.",
|
|
437
|
+
"Run checkHeaders() with your response headers for detailed findings."
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
if (config.checkRateLimiting) {
|
|
442
|
+
allFindings.push(
|
|
443
|
+
createFinding(
|
|
444
|
+
"AUD-003",
|
|
445
|
+
"info",
|
|
446
|
+
"audit",
|
|
447
|
+
"Rate limiting check enabled",
|
|
448
|
+
"Rate limiting configuration check is enabled.",
|
|
449
|
+
"Ensure rate limiting is configured for all public-facing endpoints."
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (config.checkInputValidation) {
|
|
454
|
+
allFindings.push(
|
|
455
|
+
createFinding(
|
|
456
|
+
"AUD-004",
|
|
457
|
+
"info",
|
|
458
|
+
"audit",
|
|
459
|
+
"Input validation check enabled",
|
|
460
|
+
"Input validation check is enabled.",
|
|
461
|
+
"Ensure all user input is validated and sanitized before processing."
|
|
462
|
+
)
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
if (config.checkWebhookSecurity) {
|
|
466
|
+
allFindings.push(
|
|
467
|
+
createFinding(
|
|
468
|
+
"AUD-005",
|
|
469
|
+
"info",
|
|
470
|
+
"audit",
|
|
471
|
+
"Webhook security check enabled",
|
|
472
|
+
"Webhook security check is enabled.",
|
|
473
|
+
"Ensure webhook payloads are validated with HMAC signatures."
|
|
474
|
+
)
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (config.checkSessionSecurity) {
|
|
478
|
+
allFindings.push(
|
|
479
|
+
createFinding(
|
|
480
|
+
"AUD-006",
|
|
481
|
+
"info",
|
|
482
|
+
"audit",
|
|
483
|
+
"Session security check enabled",
|
|
484
|
+
"Session security check is enabled.",
|
|
485
|
+
"Ensure sessions are encrypted at rest and use secure cookie flags."
|
|
486
|
+
)
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
return buildResult(allFindings);
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
audit,
|
|
493
|
+
checkEncryption,
|
|
494
|
+
checkHeaders
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
498
|
+
0 && (module.exports = {
|
|
499
|
+
createCSPMiddleware,
|
|
500
|
+
createInputValidator,
|
|
501
|
+
createRateLimiter,
|
|
502
|
+
createSecurityAuditor,
|
|
503
|
+
createSecurityHeadersMiddleware
|
|
504
|
+
});
|
|
505
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/security/index.ts","../../src/security/input-validator.ts","../../src/security/security-headers.ts","../../src/security/rate-limiter.ts","../../src/security/security-auditor.ts"],"sourcesContent":["/**\n * @uniforge/core/security\n *\n * Core security implementations: input validation, security headers,\n * CSP middleware, rate limiting, and security auditing.\n */\n\nexport { createInputValidator } from './input-validator.js';\nexport { createSecurityHeadersMiddleware, createCSPMiddleware } from './security-headers.js';\nexport { createRateLimiter } from './rate-limiter.js';\nexport { createSecurityAuditor } from './security-auditor.js';\n","/**\n * Input validation implementation.\n *\n * Factory function that creates an InputValidator with methods for\n * validating and sanitizing user input including shop domains, emails,\n * URLs, API keys, and custom patterns.\n */\n\nimport type {\n InputValidator,\n} from '@uniforge/platform-core/security';\nimport type {\n InputValidationRule,\n InputValidationResult,\n InputValidationError,\n SanitizeOptions,\n} from '@uniforge/platform-core/security';\n\nconst SHOP_DOMAIN_RE = /^[a-zA-Z0-9][a-zA-Z0-9-]*\\.myshopify\\.com$/;\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nconst API_KEY_RE = /^[a-zA-Z0-9-]{32,64}$/;\n\nconst HTML_TAG_RE = /<[^>]*>/g;\n\n/** Create an InputValidator with standard validation and sanitization methods. */\nexport function createInputValidator(): InputValidator {\n function isValidShopDomain(domain: string): boolean {\n return SHOP_DOMAIN_RE.test(domain);\n }\n\n function isValidEmail(email: string): boolean {\n return EMAIL_RE.test(email);\n }\n\n function isValidUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'http:' || parsed.protocol === 'https:';\n } catch {\n return false;\n }\n }\n\n function isValidApiKey(key: string): boolean {\n return API_KEY_RE.test(key);\n }\n\n function sanitize(value: string, options?: SanitizeOptions): string {\n let result = value;\n\n if (options?.stripHtml) {\n result = result.replace(HTML_TAG_RE, '');\n }\n\n if (options?.maxLength != null && result.length > options.maxLength) {\n result = result.slice(0, options.maxLength);\n }\n\n if (options?.allowedPattern !== undefined) {\n const matches = result.match(options.allowedPattern);\n result = matches ? matches.join('') : '';\n }\n\n return result;\n }\n\n function validateField(\n fieldValue: unknown,\n rule: InputValidationRule,\n ): InputValidationError | undefined {\n const value = fieldValue as string | undefined | null;\n\n // Check required\n if (rule.required && (value == null || value === '')) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} is required`,\n code: 'REQUIRED',\n };\n }\n\n // If not required and empty, skip further validation\n if (value == null || value === '') {\n return undefined;\n }\n\n const strValue = String(value);\n\n // Check maxLength\n if (rule.maxLength != null && strValue.length > rule.maxLength) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} exceeds maximum length of ${rule.maxLength}`,\n code: 'MAX_LENGTH',\n };\n }\n\n // Type-specific validation\n switch (rule.type) {\n case 'shopDomain':\n if (!isValidShopDomain(strValue)) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} is not a valid shop domain`,\n code: 'INVALID_SHOP_DOMAIN',\n };\n }\n break;\n\n case 'email':\n if (!isValidEmail(strValue)) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} is not a valid email`,\n code: 'INVALID_EMAIL',\n };\n }\n break;\n\n case 'url':\n if (!isValidUrl(strValue)) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} is not a valid URL`,\n code: 'INVALID_URL',\n };\n }\n break;\n\n case 'apiKey':\n if (!isValidApiKey(strValue)) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} is not a valid API key`,\n code: 'INVALID_API_KEY',\n };\n }\n break;\n\n case 'custom':\n if (rule.pattern !== undefined && !rule.pattern.test(strValue)) {\n return {\n field: rule.field,\n message: rule.message ?? `${rule.field} does not match the required pattern`,\n code: 'PATTERN_MISMATCH',\n };\n }\n break;\n\n case 'string':\n // No additional validation for plain strings beyond required/maxLength\n break;\n }\n\n return undefined;\n }\n\n function validate(\n input: Record<string, unknown>,\n rules: InputValidationRule[],\n ): InputValidationResult {\n const errors: InputValidationError[] = [];\n const sanitized: Record<string, string> = {};\n\n for (const rule of rules) {\n const rawValue = input[rule.field];\n const error = validateField(rawValue, rule);\n\n if (error) {\n errors.push(error);\n } else if (rawValue != null && rawValue !== '') {\n sanitized[rule.field] = String(rawValue);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n sanitized,\n };\n }\n\n return {\n validate,\n sanitize,\n isValidShopDomain,\n isValidEmail,\n isValidUrl,\n isValidApiKey,\n };\n}\n","/**\n * Security headers middleware implementation.\n *\n * Factory functions for applying security-related HTTP headers\n * including HSTS, X-Content-Type-Options, X-Frame-Options,\n * X-XSS-Protection, Referrer-Policy, and Content Security Policy.\n */\n\nimport type {\n SecurityHeadersMiddleware,\n CSPMiddleware,\n SecurityResponse,\n} from '@uniforge/platform-core/security';\nimport type {\n SecurityHeadersConfig,\n CSPDirectives,\n} from '@uniforge/platform-core/security';\n\n/** Create a SecurityHeadersMiddleware that applies standard security headers to responses. */\nexport function createSecurityHeadersMiddleware(\n config: SecurityHeadersConfig,\n): SecurityHeadersMiddleware {\n function applyHeaders(res: SecurityResponse): void {\n if (config.hsts) {\n const maxAge = config.hstsMaxAge ?? 31536000;\n let value = `max-age=${maxAge}`;\n if (config.hstsIncludeSubDomains === true) {\n value += '; includeSubDomains';\n }\n res.setHeader('Strict-Transport-Security', value);\n }\n\n if (config.noSniff) {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n }\n\n res.setHeader('X-Frame-Options', config.frameOptions);\n\n if (config.xssProtection) {\n res.setHeader('X-XSS-Protection', '1; mode=block');\n }\n\n res.setHeader('Referrer-Policy', config.referrerPolicy);\n }\n\n return {\n config,\n applyHeaders,\n };\n}\n\n/** Build a CSP header string from a set of directives. */\nfunction buildCSPString(directives: CSPDirectives): string {\n const parts: string[] = [];\n\n const directiveMap: Array<[string, string[]]> = [\n ['default-src', directives.defaultSrc],\n ['script-src', directives.scriptSrc],\n ['style-src', directives.styleSrc],\n ['img-src', directives.imgSrc],\n ['connect-src', directives.connectSrc],\n ['font-src', directives.fontSrc],\n ['frame-src', directives.frameSrc],\n ['frame-ancestors', directives.frameAncestors],\n ];\n\n for (const [name, values] of directiveMap) {\n if (values.length > 0) {\n parts.push(`${name} ${values.join(' ')}`);\n }\n }\n\n return parts.join('; ');\n}\n\n/** Create a CSPMiddleware that applies Content Security Policy headers to responses. */\nexport function createCSPMiddleware(directives: CSPDirectives): CSPMiddleware {\n function toHeaderString(): string {\n return buildCSPString(directives);\n }\n\n function applyCSP(res: SecurityResponse): void {\n const headerValue = toHeaderString();\n if (headerValue) {\n res.setHeader('Content-Security-Policy', headerValue);\n }\n }\n\n return {\n directives,\n applyCSP,\n toHeaderString,\n };\n}\n","/**\n * In-memory sliding window rate limiter.\n *\n * Tracks request timestamps per key and enforces a maximum\n * number of requests within a configurable time window.\n */\n\nimport type { RequestRateLimiter } from '@uniforge/platform-core/security';\nimport type { RateLimitConfig, RateLimitResult } from '@uniforge/platform-core/security';\n\nconst CLEANUP_THRESHOLD = 10000;\n\n/** Create a RequestRateLimiter using an in-memory sliding window algorithm. */\nexport function createRateLimiter(config: RateLimitConfig): RequestRateLimiter {\n const windows = new Map<string, number[]>();\n\n function cleanupStaleEntries(): void {\n if (windows.size <= CLEANUP_THRESHOLD) return;\n\n const now = Date.now();\n const cutoff = now - config.windowMs;\n\n for (const [key, timestamps] of windows) {\n // Remove entries whose timestamps array is empty or\n // whose most recent timestamp is older than the window\n if (timestamps.length === 0 || timestamps[timestamps.length - 1]! <= cutoff) {\n windows.delete(key);\n }\n }\n }\n\n function resolveKey(key: string): string {\n if (config.keyPrefix !== undefined) {\n return `${config.keyPrefix}:${key}`;\n }\n return key;\n }\n\n async function check(key: string): Promise<RateLimitResult> {\n cleanupStaleEntries();\n const resolvedKey = resolveKey(key);\n const now = Date.now();\n const windowStart = now - config.windowMs;\n\n // Get or create timestamps array\n let timestamps = windows.get(resolvedKey);\n if (!timestamps) {\n timestamps = [];\n windows.set(resolvedKey, timestamps);\n }\n\n // Filter out expired timestamps (outside the window)\n const validTimestamps = timestamps.filter((t) => t > windowStart);\n windows.set(resolvedKey, validTimestamps);\n\n const remaining = Math.max(0, config.maxRequests - validTimestamps.length);\n const resetAt = now + config.windowMs;\n\n if (validTimestamps.length >= config.maxRequests) {\n // Find the earliest timestamp — that determines when the window slides enough\n // validTimestamps is non-empty here since length >= maxRequests (>= 1)\n const earliest = validTimestamps[0] as number;\n const retryAfter = earliest + config.windowMs - now;\n\n return {\n allowed: false,\n remaining: 0,\n resetAt,\n retryAfter: Math.max(0, retryAfter),\n };\n }\n\n // Allow the request, record the timestamp\n validTimestamps.push(now);\n\n return {\n allowed: true,\n remaining: remaining - 1,\n resetAt,\n };\n }\n\n async function reset(key: string): Promise<void> {\n const resolvedKey = resolveKey(key);\n windows.delete(resolvedKey);\n }\n\n return {\n config,\n check,\n reset,\n };\n}\n","/**\n * Security auditor implementation.\n *\n * Factory function that creates a SecurityAuditor capable of running\n * security configuration checks for encryption, headers, and more.\n */\n\nimport type { SecurityAuditor, SecurityAuditConfig } from '@uniforge/platform-core/security';\nimport type {\n SecurityAuditResult,\n SecurityAuditFinding,\n SecurityFindingSeverity,\n} from '@uniforge/platform-core/security';\n\n/** Severity weights used for score calculation. */\nconst SEVERITY_PENALTY: Record<SecurityFindingSeverity, number> = {\n critical: 25,\n high: 15,\n medium: 10,\n low: 5,\n info: 0,\n};\n\nfunction createFinding(\n id: string,\n severity: SecurityFindingSeverity,\n category: string,\n title: string,\n description: string,\n recommendation: string,\n): SecurityAuditFinding {\n return { id, severity, category, title, description, recommendation };\n}\n\nfunction calculateScore(findings: SecurityAuditFinding[]): number {\n let score = 100;\n\n for (const finding of findings) {\n score -= SEVERITY_PENALTY[finding.severity];\n }\n\n return Math.max(0, score);\n}\n\nfunction buildResult(findings: SecurityAuditFinding[]): SecurityAuditResult {\n const score = calculateScore(findings);\n const hasCriticalOrHigh = findings.some(\n (f) => f.severity === 'critical' || f.severity === 'high',\n );\n\n return {\n passed: !hasCriticalOrHigh && score >= 70,\n score,\n findings,\n timestamp: new Date().toISOString(),\n };\n}\n\n/** Create a SecurityAuditor that checks encryption and header configurations. */\nexport function createSecurityAuditor(): SecurityAuditor {\n function checkEncryption(config: Record<string, unknown>): SecurityAuditResult {\n const findings: SecurityAuditFinding[] = [];\n\n // Check for encryption key presence\n if (!config['encryptionKey'] && !config['ENCRYPTION_KEY']) {\n findings.push(\n createFinding(\n 'ENC-001',\n 'critical',\n 'encryption',\n 'Missing encryption key',\n 'No encryption key is configured in the application.',\n 'Set the ENCRYPTION_KEY environment variable or encryptionKey config property.',\n ),\n );\n } else {\n const key = (config['encryptionKey'] ?? config['ENCRYPTION_KEY']) as string;\n\n // Check key length (AES-256 requires 32 bytes = 64 hex chars or 44 base64 chars)\n if (key.length < 32) {\n findings.push(\n createFinding(\n 'ENC-002',\n 'high',\n 'encryption',\n 'Encryption key too short',\n `Encryption key is ${key.length} characters, which may be insufficient for AES-256.`,\n 'Use a key of at least 32 characters (256 bits) for AES-256-GCM encryption.',\n ),\n );\n }\n }\n\n // Check algorithm configuration\n if (config['algorithm'] && config['algorithm'] !== 'aes-256-gcm') {\n findings.push(\n createFinding(\n 'ENC-003',\n 'medium',\n 'encryption',\n 'Non-standard encryption algorithm',\n `Encryption algorithm \"${String(config['algorithm'])}\" is not the recommended AES-256-GCM.`,\n 'Use AES-256-GCM for authenticated encryption.',\n ),\n );\n }\n\n return buildResult(findings);\n }\n\n function checkHeaders(headers: Record<string, string>): SecurityAuditResult {\n const findings: SecurityAuditFinding[] = [];\n\n // Check HSTS\n if (!headers['strict-transport-security'] && !headers['Strict-Transport-Security']) {\n findings.push(\n createFinding(\n 'HDR-001',\n 'high',\n 'headers',\n 'Missing HSTS header',\n 'Strict-Transport-Security header is not configured.',\n 'Enable HSTS with a minimum max-age of 31536000 (1 year).',\n ),\n );\n }\n\n // Check X-Content-Type-Options\n if (!headers['x-content-type-options'] && !headers['X-Content-Type-Options']) {\n findings.push(\n createFinding(\n 'HDR-002',\n 'medium',\n 'headers',\n 'Missing X-Content-Type-Options header',\n 'X-Content-Type-Options header is not set to nosniff.',\n 'Set X-Content-Type-Options: nosniff to prevent MIME type sniffing.',\n ),\n );\n }\n\n // Check X-Frame-Options\n if (!headers['x-frame-options'] && !headers['X-Frame-Options']) {\n findings.push(\n createFinding(\n 'HDR-003',\n 'medium',\n 'headers',\n 'Missing X-Frame-Options header',\n 'X-Frame-Options header is not configured.',\n 'Set X-Frame-Options to DENY or SAMEORIGIN to prevent clickjacking.',\n ),\n );\n }\n\n // Check Content-Security-Policy\n if (!headers['content-security-policy'] && !headers['Content-Security-Policy']) {\n findings.push(\n createFinding(\n 'HDR-004',\n 'high',\n 'headers',\n 'Missing Content-Security-Policy header',\n 'Content-Security-Policy header is not configured.',\n 'Define a Content Security Policy to mitigate XSS and data injection attacks.',\n ),\n );\n }\n\n return buildResult(findings);\n }\n\n function audit(config: SecurityAuditConfig): SecurityAuditResult {\n const allFindings: SecurityAuditFinding[] = [];\n\n if (config.checkEncryption) {\n // Without actual config data, report as info finding\n allFindings.push(\n createFinding(\n 'AUD-001',\n 'info',\n 'audit',\n 'Encryption check enabled',\n 'Encryption configuration check is enabled. Use checkEncryption() for detailed results.',\n 'Run checkEncryption() with your application config for detailed findings.',\n ),\n );\n }\n\n if (config.checkHeaders) {\n allFindings.push(\n createFinding(\n 'AUD-002',\n 'info',\n 'audit',\n 'Headers check enabled',\n 'Security headers check is enabled. Use checkHeaders() for detailed results.',\n 'Run checkHeaders() with your response headers for detailed findings.',\n ),\n );\n }\n\n if (config.checkRateLimiting) {\n allFindings.push(\n createFinding(\n 'AUD-003',\n 'info',\n 'audit',\n 'Rate limiting check enabled',\n 'Rate limiting configuration check is enabled.',\n 'Ensure rate limiting is configured for all public-facing endpoints.',\n ),\n );\n }\n\n if (config.checkInputValidation) {\n allFindings.push(\n createFinding(\n 'AUD-004',\n 'info',\n 'audit',\n 'Input validation check enabled',\n 'Input validation check is enabled.',\n 'Ensure all user input is validated and sanitized before processing.',\n ),\n );\n }\n\n if (config.checkWebhookSecurity) {\n allFindings.push(\n createFinding(\n 'AUD-005',\n 'info',\n 'audit',\n 'Webhook security check enabled',\n 'Webhook security check is enabled.',\n 'Ensure webhook payloads are validated with HMAC signatures.',\n ),\n );\n }\n\n if (config.checkSessionSecurity) {\n allFindings.push(\n createFinding(\n 'AUD-006',\n 'info',\n 'audit',\n 'Session security check enabled',\n 'Session security check is enabled.',\n 'Ensure sessions are encrypted at rest and use secure cookie flags.',\n ),\n );\n }\n\n return buildResult(allFindings);\n }\n\n return {\n audit,\n checkEncryption,\n checkHeaders,\n };\n}\n"],"mappings":"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","names":[]}
|