@digilogiclabs/platform-core 1.3.0 → 1.4.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/README.md +79 -3
- package/dist/{ConsoleEmail-hUDFsKoA.d.mts → ConsoleEmail-ubSVWgTa.d.mts} +187 -1
- package/dist/{ConsoleEmail-hUDFsKoA.d.ts → ConsoleEmail-ubSVWgTa.d.ts} +187 -1
- package/dist/index.d.mts +76 -4
- package/dist/index.d.ts +76 -4
- package/dist/index.js +700 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +698 -147
- package/dist/index.mjs.map +1 -1
- package/dist/migrate.js +3 -2
- package/dist/migrate.js.map +1 -1
- package/dist/security-headers.d.mts +75 -0
- package/dist/security-headers.d.ts +75 -0
- package/dist/security-headers.js +137 -0
- package/dist/security-headers.js.map +1 -0
- package/dist/security-headers.mjs +111 -0
- package/dist/security-headers.mjs.map +1 -0
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +194 -15
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +193 -14
- package/dist/testing.mjs.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security-headers.ts"],"sourcesContent":["/**\r\n * Shared Security Headers Utility\r\n * Generates consistent security headers for all Next.js apps.\r\n * Zero heavy dependencies — pure utility function.\r\n */\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// TYPES\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nexport interface SecurityHeadersConfig {\r\n /** Enable Content-Security-Policy (default: true) */\r\n csp?: boolean;\r\n /** Additional allowed script-src domains */\r\n cspScriptSrc?: string[];\r\n /** Additional allowed connect-src domains */\r\n cspConnectSrc?: string[];\r\n /** Additional allowed frame-src domains */\r\n cspFrameSrc?: string[];\r\n /** Additional allowed style-src domains */\r\n cspStyleSrc?: string[];\r\n /** Additional allowed img-src domains */\r\n cspImgSrc?: string[];\r\n /** X-Frame-Options value (default: 'DENY') */\r\n frameOptions?: \"DENY\" | \"SAMEORIGIN\";\r\n /** Enable HSTS in production (default: true) */\r\n hsts?: boolean;\r\n /** HSTS max-age in seconds (default: 31536000 = 1 year) */\r\n hstsMaxAge?: number;\r\n /** Whether this is a production build (default: auto-detect from NODE_ENV) */\r\n isProduction?: boolean;\r\n}\r\n\r\nexport interface NextHeaderEntry {\r\n source: string;\r\n headers: Array<{ key: string; value: string }>;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// PRESETS\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nexport const SecurityHeaderPresets = {\r\n /** Minimal: basic headers only, no CSP */\r\n minimal: {\r\n csp: false,\r\n hsts: false,\r\n } satisfies SecurityHeadersConfig,\r\n\r\n /** Standard: full CSP + HSTS for most apps */\r\n standard: {\r\n csp: true,\r\n hsts: true,\r\n frameOptions: \"DENY\",\r\n } satisfies SecurityHeadersConfig,\r\n\r\n /** Strict: deny all permissions, strict CSP, no frame embedding */\r\n strict: {\r\n csp: true,\r\n hsts: true,\r\n hstsMaxAge: 63072000, // 2 years\r\n frameOptions: \"DENY\",\r\n } satisfies SecurityHeadersConfig,\r\n} as const;\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// MAIN FUNCTION\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Generate security headers array compatible with Next.js `headers()` config.\r\n *\r\n * Usage in next.config.mjs:\r\n * ```js\r\n * const { generateSecurityHeaders } = require('@digilogiclabs/platform-core');\r\n *\r\n * module.exports = {\r\n * async headers() {\r\n * return generateSecurityHeaders({\r\n * isProduction: process.env.NODE_ENV === 'production',\r\n * cspScriptSrc: ['https://js.stripe.com'],\r\n * cspConnectSrc: ['https://api.stripe.com'],\r\n * });\r\n * },\r\n * };\r\n * ```\r\n */\r\nexport function generateSecurityHeaders(\r\n config: SecurityHeadersConfig = {},\r\n): NextHeaderEntry[] {\r\n const isProduction =\r\n config.isProduction ?? process.env.NODE_ENV === \"production\";\r\n const frameOptions = config.frameOptions ?? \"DENY\";\r\n const enableCsp = config.csp ?? true;\r\n const enableHsts = config.hsts ?? true;\r\n const hstsMaxAge = config.hstsMaxAge ?? 31536000;\r\n\r\n // Base headers applied to all routes in all environments\r\n const baseHeaders: Array<{ key: string; value: string }> = [\r\n { key: \"X-Frame-Options\", value: frameOptions },\r\n { key: \"X-Content-Type-Options\", value: \"nosniff\" },\r\n // Modern browsers use CSP, not XSS-Protection. Value '0' disables the\r\n // legacy filter which can itself introduce vulnerabilities.\r\n { key: \"X-XSS-Protection\", value: \"0\" },\r\n {\r\n key: \"Referrer-Policy\",\r\n value: \"strict-origin-when-cross-origin\",\r\n },\r\n {\r\n key: \"Permissions-Policy\",\r\n value: \"camera=(), microphone=(), geolocation=()\",\r\n },\r\n ];\r\n\r\n const entries: NextHeaderEntry[] = [\r\n { source: \"/:path*\", headers: baseHeaders },\r\n ];\r\n\r\n // Production-only headers\r\n if (isProduction) {\r\n const prodHeaders: Array<{ key: string; value: string }> = [];\r\n\r\n // HSTS\r\n if (enableHsts) {\r\n prodHeaders.push({\r\n key: \"Strict-Transport-Security\",\r\n value: `max-age=${hstsMaxAge}; includeSubDomains`,\r\n });\r\n }\r\n\r\n // Content-Security-Policy\r\n if (enableCsp) {\r\n const csp = buildCsp(config);\r\n prodHeaders.push({ key: \"Content-Security-Policy\", value: csp });\r\n }\r\n\r\n if (prodHeaders.length > 0) {\r\n entries.push({ source: \"/:path*\", headers: prodHeaders });\r\n }\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// CSP BUILDER\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nfunction buildCsp(config: SecurityHeadersConfig): string {\r\n const scriptSrc = [\r\n \"'self'\",\r\n \"'unsafe-inline'\",\r\n \"'unsafe-eval'\",\r\n ...(config.cspScriptSrc ?? []),\r\n ];\r\n\r\n const styleSrc = [\r\n \"'self'\",\r\n \"'unsafe-inline'\",\r\n \"https://fonts.googleapis.com\",\r\n ...(config.cspStyleSrc ?? []),\r\n ];\r\n\r\n const imgSrc = [\r\n \"'self'\",\r\n \"data:\",\r\n \"https:\",\r\n \"blob:\",\r\n ...(config.cspImgSrc ?? []),\r\n ];\r\n\r\n const fontSrc = [\"'self'\", \"data:\", \"https://fonts.gstatic.com\"];\r\n\r\n const connectSrc = [\"'self'\", ...(config.cspConnectSrc ?? [])];\r\n\r\n const frameSrc = [...(config.cspFrameSrc ?? [])];\r\n\r\n const directives = [\r\n `default-src 'self'`,\r\n `script-src ${scriptSrc.join(\" \")}`,\r\n `style-src ${styleSrc.join(\" \")}`,\r\n `img-src ${imgSrc.join(\" \")}`,\r\n `font-src ${fontSrc.join(\" \")}`,\r\n `connect-src ${connectSrc.join(\" \")}`,\r\n ];\r\n\r\n if (frameSrc.length > 0) {\r\n directives.push(`frame-src ${frameSrc.join(\" \")}`);\r\n }\r\n\r\n directives.push(\r\n `object-src 'none'`,\r\n `base-uri 'self'`,\r\n `form-action 'self'`,\r\n `frame-ancestors 'none'`,\r\n );\r\n\r\n return directives.join(\"; \");\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0CO,IAAM,wBAAwB;AAAA;AAAA,EAEnC,SAAS;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAwBO,SAAS,wBACd,SAAgC,CAAC,GACd;AACnB,QAAM,eACJ,OAAO,gBAAgB,QAAQ,IAAI,aAAa;AAClD,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,YAAY,OAAO,OAAO;AAChC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,cAAc;AAGxC,QAAM,cAAqD;AAAA,IACzD,EAAE,KAAK,mBAAmB,OAAO,aAAa;AAAA,IAC9C,EAAE,KAAK,0BAA0B,OAAO,UAAU;AAAA;AAAA;AAAA,IAGlD,EAAE,KAAK,oBAAoB,OAAO,IAAI;AAAA,IACtC;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,UAA6B;AAAA,IACjC,EAAE,QAAQ,WAAW,SAAS,YAAY;AAAA,EAC5C;AAGA,MAAI,cAAc;AAChB,UAAM,cAAqD,CAAC;AAG5D,QAAI,YAAY;AACd,kBAAY,KAAK;AAAA,QACf,KAAK;AAAA,QACL,OAAO,WAAW,UAAU;AAAA,MAC9B,CAAC;AAAA,IACH;AAGA,QAAI,WAAW;AACb,YAAM,MAAM,SAAS,MAAM;AAC3B,kBAAY,KAAK,EAAE,KAAK,2BAA2B,OAAO,IAAI,CAAC;AAAA,IACjE;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ,KAAK,EAAE,QAAQ,WAAW,SAAS,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,SAAS,QAAuC;AACvD,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,gBAAgB,CAAC;AAAA,EAC9B;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,eAAe,CAAC;AAAA,EAC7B;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,CAAC,UAAU,SAAS,2BAA2B;AAE/D,QAAM,aAAa,CAAC,UAAU,GAAI,OAAO,iBAAiB,CAAC,CAAE;AAE7D,QAAM,WAAW,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE;AAE/C,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,cAAc,UAAU,KAAK,GAAG,CAAC;AAAA,IACjC,aAAa,SAAS,KAAK,GAAG,CAAC;AAAA,IAC/B,WAAW,OAAO,KAAK,GAAG,CAAC;AAAA,IAC3B,YAAY,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC7B,eAAe,WAAW,KAAK,GAAG,CAAC;AAAA,EACrC;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,eAAW,KAAK,aAAa,SAAS,KAAK,GAAG,CAAC,EAAE;AAAA,EACnD;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;","names":[]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/security-headers.ts
|
|
2
|
+
var SecurityHeaderPresets = {
|
|
3
|
+
/** Minimal: basic headers only, no CSP */
|
|
4
|
+
minimal: {
|
|
5
|
+
csp: false,
|
|
6
|
+
hsts: false
|
|
7
|
+
},
|
|
8
|
+
/** Standard: full CSP + HSTS for most apps */
|
|
9
|
+
standard: {
|
|
10
|
+
csp: true,
|
|
11
|
+
hsts: true,
|
|
12
|
+
frameOptions: "DENY"
|
|
13
|
+
},
|
|
14
|
+
/** Strict: deny all permissions, strict CSP, no frame embedding */
|
|
15
|
+
strict: {
|
|
16
|
+
csp: true,
|
|
17
|
+
hsts: true,
|
|
18
|
+
hstsMaxAge: 63072e3,
|
|
19
|
+
// 2 years
|
|
20
|
+
frameOptions: "DENY"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function generateSecurityHeaders(config = {}) {
|
|
24
|
+
const isProduction = config.isProduction ?? process.env.NODE_ENV === "production";
|
|
25
|
+
const frameOptions = config.frameOptions ?? "DENY";
|
|
26
|
+
const enableCsp = config.csp ?? true;
|
|
27
|
+
const enableHsts = config.hsts ?? true;
|
|
28
|
+
const hstsMaxAge = config.hstsMaxAge ?? 31536e3;
|
|
29
|
+
const baseHeaders = [
|
|
30
|
+
{ key: "X-Frame-Options", value: frameOptions },
|
|
31
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
32
|
+
// Modern browsers use CSP, not XSS-Protection. Value '0' disables the
|
|
33
|
+
// legacy filter which can itself introduce vulnerabilities.
|
|
34
|
+
{ key: "X-XSS-Protection", value: "0" },
|
|
35
|
+
{
|
|
36
|
+
key: "Referrer-Policy",
|
|
37
|
+
value: "strict-origin-when-cross-origin"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "Permissions-Policy",
|
|
41
|
+
value: "camera=(), microphone=(), geolocation=()"
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
const entries = [
|
|
45
|
+
{ source: "/:path*", headers: baseHeaders }
|
|
46
|
+
];
|
|
47
|
+
if (isProduction) {
|
|
48
|
+
const prodHeaders = [];
|
|
49
|
+
if (enableHsts) {
|
|
50
|
+
prodHeaders.push({
|
|
51
|
+
key: "Strict-Transport-Security",
|
|
52
|
+
value: `max-age=${hstsMaxAge}; includeSubDomains`
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (enableCsp) {
|
|
56
|
+
const csp = buildCsp(config);
|
|
57
|
+
prodHeaders.push({ key: "Content-Security-Policy", value: csp });
|
|
58
|
+
}
|
|
59
|
+
if (prodHeaders.length > 0) {
|
|
60
|
+
entries.push({ source: "/:path*", headers: prodHeaders });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return entries;
|
|
64
|
+
}
|
|
65
|
+
function buildCsp(config) {
|
|
66
|
+
const scriptSrc = [
|
|
67
|
+
"'self'",
|
|
68
|
+
"'unsafe-inline'",
|
|
69
|
+
"'unsafe-eval'",
|
|
70
|
+
...config.cspScriptSrc ?? []
|
|
71
|
+
];
|
|
72
|
+
const styleSrc = [
|
|
73
|
+
"'self'",
|
|
74
|
+
"'unsafe-inline'",
|
|
75
|
+
"https://fonts.googleapis.com",
|
|
76
|
+
...config.cspStyleSrc ?? []
|
|
77
|
+
];
|
|
78
|
+
const imgSrc = [
|
|
79
|
+
"'self'",
|
|
80
|
+
"data:",
|
|
81
|
+
"https:",
|
|
82
|
+
"blob:",
|
|
83
|
+
...config.cspImgSrc ?? []
|
|
84
|
+
];
|
|
85
|
+
const fontSrc = ["'self'", "data:", "https://fonts.gstatic.com"];
|
|
86
|
+
const connectSrc = ["'self'", ...config.cspConnectSrc ?? []];
|
|
87
|
+
const frameSrc = [...config.cspFrameSrc ?? []];
|
|
88
|
+
const directives = [
|
|
89
|
+
`default-src 'self'`,
|
|
90
|
+
`script-src ${scriptSrc.join(" ")}`,
|
|
91
|
+
`style-src ${styleSrc.join(" ")}`,
|
|
92
|
+
`img-src ${imgSrc.join(" ")}`,
|
|
93
|
+
`font-src ${fontSrc.join(" ")}`,
|
|
94
|
+
`connect-src ${connectSrc.join(" ")}`
|
|
95
|
+
];
|
|
96
|
+
if (frameSrc.length > 0) {
|
|
97
|
+
directives.push(`frame-src ${frameSrc.join(" ")}`);
|
|
98
|
+
}
|
|
99
|
+
directives.push(
|
|
100
|
+
`object-src 'none'`,
|
|
101
|
+
`base-uri 'self'`,
|
|
102
|
+
`form-action 'self'`,
|
|
103
|
+
`frame-ancestors 'none'`
|
|
104
|
+
);
|
|
105
|
+
return directives.join("; ");
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
SecurityHeaderPresets,
|
|
109
|
+
generateSecurityHeaders
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=security-headers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security-headers.ts"],"sourcesContent":["/**\r\n * Shared Security Headers Utility\r\n * Generates consistent security headers for all Next.js apps.\r\n * Zero heavy dependencies — pure utility function.\r\n */\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// TYPES\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nexport interface SecurityHeadersConfig {\r\n /** Enable Content-Security-Policy (default: true) */\r\n csp?: boolean;\r\n /** Additional allowed script-src domains */\r\n cspScriptSrc?: string[];\r\n /** Additional allowed connect-src domains */\r\n cspConnectSrc?: string[];\r\n /** Additional allowed frame-src domains */\r\n cspFrameSrc?: string[];\r\n /** Additional allowed style-src domains */\r\n cspStyleSrc?: string[];\r\n /** Additional allowed img-src domains */\r\n cspImgSrc?: string[];\r\n /** X-Frame-Options value (default: 'DENY') */\r\n frameOptions?: \"DENY\" | \"SAMEORIGIN\";\r\n /** Enable HSTS in production (default: true) */\r\n hsts?: boolean;\r\n /** HSTS max-age in seconds (default: 31536000 = 1 year) */\r\n hstsMaxAge?: number;\r\n /** Whether this is a production build (default: auto-detect from NODE_ENV) */\r\n isProduction?: boolean;\r\n}\r\n\r\nexport interface NextHeaderEntry {\r\n source: string;\r\n headers: Array<{ key: string; value: string }>;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// PRESETS\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nexport const SecurityHeaderPresets = {\r\n /** Minimal: basic headers only, no CSP */\r\n minimal: {\r\n csp: false,\r\n hsts: false,\r\n } satisfies SecurityHeadersConfig,\r\n\r\n /** Standard: full CSP + HSTS for most apps */\r\n standard: {\r\n csp: true,\r\n hsts: true,\r\n frameOptions: \"DENY\",\r\n } satisfies SecurityHeadersConfig,\r\n\r\n /** Strict: deny all permissions, strict CSP, no frame embedding */\r\n strict: {\r\n csp: true,\r\n hsts: true,\r\n hstsMaxAge: 63072000, // 2 years\r\n frameOptions: \"DENY\",\r\n } satisfies SecurityHeadersConfig,\r\n} as const;\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// MAIN FUNCTION\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Generate security headers array compatible with Next.js `headers()` config.\r\n *\r\n * Usage in next.config.mjs:\r\n * ```js\r\n * const { generateSecurityHeaders } = require('@digilogiclabs/platform-core');\r\n *\r\n * module.exports = {\r\n * async headers() {\r\n * return generateSecurityHeaders({\r\n * isProduction: process.env.NODE_ENV === 'production',\r\n * cspScriptSrc: ['https://js.stripe.com'],\r\n * cspConnectSrc: ['https://api.stripe.com'],\r\n * });\r\n * },\r\n * };\r\n * ```\r\n */\r\nexport function generateSecurityHeaders(\r\n config: SecurityHeadersConfig = {},\r\n): NextHeaderEntry[] {\r\n const isProduction =\r\n config.isProduction ?? process.env.NODE_ENV === \"production\";\r\n const frameOptions = config.frameOptions ?? \"DENY\";\r\n const enableCsp = config.csp ?? true;\r\n const enableHsts = config.hsts ?? true;\r\n const hstsMaxAge = config.hstsMaxAge ?? 31536000;\r\n\r\n // Base headers applied to all routes in all environments\r\n const baseHeaders: Array<{ key: string; value: string }> = [\r\n { key: \"X-Frame-Options\", value: frameOptions },\r\n { key: \"X-Content-Type-Options\", value: \"nosniff\" },\r\n // Modern browsers use CSP, not XSS-Protection. Value '0' disables the\r\n // legacy filter which can itself introduce vulnerabilities.\r\n { key: \"X-XSS-Protection\", value: \"0\" },\r\n {\r\n key: \"Referrer-Policy\",\r\n value: \"strict-origin-when-cross-origin\",\r\n },\r\n {\r\n key: \"Permissions-Policy\",\r\n value: \"camera=(), microphone=(), geolocation=()\",\r\n },\r\n ];\r\n\r\n const entries: NextHeaderEntry[] = [\r\n { source: \"/:path*\", headers: baseHeaders },\r\n ];\r\n\r\n // Production-only headers\r\n if (isProduction) {\r\n const prodHeaders: Array<{ key: string; value: string }> = [];\r\n\r\n // HSTS\r\n if (enableHsts) {\r\n prodHeaders.push({\r\n key: \"Strict-Transport-Security\",\r\n value: `max-age=${hstsMaxAge}; includeSubDomains`,\r\n });\r\n }\r\n\r\n // Content-Security-Policy\r\n if (enableCsp) {\r\n const csp = buildCsp(config);\r\n prodHeaders.push({ key: \"Content-Security-Policy\", value: csp });\r\n }\r\n\r\n if (prodHeaders.length > 0) {\r\n entries.push({ source: \"/:path*\", headers: prodHeaders });\r\n }\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// CSP BUILDER\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nfunction buildCsp(config: SecurityHeadersConfig): string {\r\n const scriptSrc = [\r\n \"'self'\",\r\n \"'unsafe-inline'\",\r\n \"'unsafe-eval'\",\r\n ...(config.cspScriptSrc ?? []),\r\n ];\r\n\r\n const styleSrc = [\r\n \"'self'\",\r\n \"'unsafe-inline'\",\r\n \"https://fonts.googleapis.com\",\r\n ...(config.cspStyleSrc ?? []),\r\n ];\r\n\r\n const imgSrc = [\r\n \"'self'\",\r\n \"data:\",\r\n \"https:\",\r\n \"blob:\",\r\n ...(config.cspImgSrc ?? []),\r\n ];\r\n\r\n const fontSrc = [\"'self'\", \"data:\", \"https://fonts.gstatic.com\"];\r\n\r\n const connectSrc = [\"'self'\", ...(config.cspConnectSrc ?? [])];\r\n\r\n const frameSrc = [...(config.cspFrameSrc ?? [])];\r\n\r\n const directives = [\r\n `default-src 'self'`,\r\n `script-src ${scriptSrc.join(\" \")}`,\r\n `style-src ${styleSrc.join(\" \")}`,\r\n `img-src ${imgSrc.join(\" \")}`,\r\n `font-src ${fontSrc.join(\" \")}`,\r\n `connect-src ${connectSrc.join(\" \")}`,\r\n ];\r\n\r\n if (frameSrc.length > 0) {\r\n directives.push(`frame-src ${frameSrc.join(\" \")}`);\r\n }\r\n\r\n directives.push(\r\n `object-src 'none'`,\r\n `base-uri 'self'`,\r\n `form-action 'self'`,\r\n `frame-ancestors 'none'`,\r\n );\r\n\r\n return directives.join(\"; \");\r\n}\r\n"],"mappings":";AA0CO,IAAM,wBAAwB;AAAA;AAAA,EAEnC,SAAS;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAwBO,SAAS,wBACd,SAAgC,CAAC,GACd;AACnB,QAAM,eACJ,OAAO,gBAAgB,QAAQ,IAAI,aAAa;AAClD,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,YAAY,OAAO,OAAO;AAChC,QAAM,aAAa,OAAO,QAAQ;AAClC,QAAM,aAAa,OAAO,cAAc;AAGxC,QAAM,cAAqD;AAAA,IACzD,EAAE,KAAK,mBAAmB,OAAO,aAAa;AAAA,IAC9C,EAAE,KAAK,0BAA0B,OAAO,UAAU;AAAA;AAAA;AAAA,IAGlD,EAAE,KAAK,oBAAoB,OAAO,IAAI;AAAA,IACtC;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,UAA6B;AAAA,IACjC,EAAE,QAAQ,WAAW,SAAS,YAAY;AAAA,EAC5C;AAGA,MAAI,cAAc;AAChB,UAAM,cAAqD,CAAC;AAG5D,QAAI,YAAY;AACd,kBAAY,KAAK;AAAA,QACf,KAAK;AAAA,QACL,OAAO,WAAW,UAAU;AAAA,MAC9B,CAAC;AAAA,IACH;AAGA,QAAI,WAAW;AACb,YAAM,MAAM,SAAS,MAAM;AAC3B,kBAAY,KAAK,EAAE,KAAK,2BAA2B,OAAO,IAAI,CAAC;AAAA,IACjE;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,cAAQ,KAAK,EAAE,QAAQ,WAAW,SAAS,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,SAAS,QAAuC;AACvD,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,gBAAgB,CAAC;AAAA,EAC9B;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,eAAe,CAAC;AAAA,EAC7B;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,OAAO,aAAa,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,CAAC,UAAU,SAAS,2BAA2B;AAE/D,QAAM,aAAa,CAAC,UAAU,GAAI,OAAO,iBAAiB,CAAC,CAAE;AAE7D,QAAM,WAAW,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE;AAE/C,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,cAAc,UAAU,KAAK,GAAG,CAAC;AAAA,IACjC,aAAa,SAAS,KAAK,GAAG,CAAC;AAAA,IAC/B,WAAW,OAAO,KAAK,GAAG,CAAC;AAAA,IAC3B,YAAY,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC7B,eAAe,WAAW,KAAK,GAAG,CAAC;AAAA,EACrC;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,eAAW,KAAK,aAAa,SAAS,KAAK,GAAG,CAAC,EAAE;AAAA,EACnD;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;","names":[]}
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-
|
|
2
|
-
export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-
|
|
1
|
+
import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-ubSVWgTa.mjs';
|
|
2
|
+
export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-ubSVWgTa.mjs';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-
|
|
2
|
-
export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-
|
|
1
|
+
import { I as IPlatform, M as MemoryDatabase, a as MemoryCache, b as MemoryStorage, c as MemoryEmail, d as MemoryQueue, e as IDatabase } from './ConsoleEmail-ubSVWgTa.js';
|
|
2
|
+
export { C as ConsoleEmail, h as ConsoleLogger, t as EmailMessage, E as EnvSecrets, l as ICache, n as IEmail, p as ILogger, q as IMetrics, k as IQueryBuilder, o as IQueue, r as ISecrets, m as IStorage, J as Job, s as JobOptions, i as MemoryMetrics, f as MemorySecrets, N as NoopLogger, j as NoopMetrics, Q as QueryResult, S as StorageFile, g as createPlatform } from './ConsoleEmail-ubSVWgTa.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/testing.js
CHANGED
|
@@ -363,10 +363,11 @@ var init_IAI = __esm({
|
|
|
363
363
|
});
|
|
364
364
|
|
|
365
365
|
// src/interfaces/IRAG.ts
|
|
366
|
-
var ChunkingPresets, MemoryRAG;
|
|
366
|
+
var import_crypto6, ChunkingPresets, MemoryRAG;
|
|
367
367
|
var init_IRAG = __esm({
|
|
368
368
|
"src/interfaces/IRAG.ts"() {
|
|
369
369
|
"use strict";
|
|
370
|
+
import_crypto6 = require("crypto");
|
|
370
371
|
ChunkingPresets = {
|
|
371
372
|
default: {
|
|
372
373
|
strategy: "recursive",
|
|
@@ -492,7 +493,7 @@ var init_IRAG = __esm({
|
|
|
492
493
|
}
|
|
493
494
|
async ingestOne(collection, document, options) {
|
|
494
495
|
const startTime = Date.now();
|
|
495
|
-
const docId = `doc_${Date.now()}_${
|
|
496
|
+
const docId = `doc_${Date.now()}_${(0, import_crypto6.randomBytes)(4).toString("hex")}`;
|
|
496
497
|
const now = /* @__PURE__ */ new Date();
|
|
497
498
|
try {
|
|
498
499
|
const col = await this.getCollection(collection);
|
|
@@ -1399,6 +1400,7 @@ var MemoryEmail = class {
|
|
|
1399
1400
|
};
|
|
1400
1401
|
|
|
1401
1402
|
// src/interfaces/IQueue.ts
|
|
1403
|
+
var import_crypto = require("crypto");
|
|
1402
1404
|
function calculateBackoff(attempt, options) {
|
|
1403
1405
|
if (options.type === "fixed") {
|
|
1404
1406
|
return options.delay;
|
|
@@ -1409,19 +1411,20 @@ function calculateBackoff(attempt, options) {
|
|
|
1409
1411
|
}
|
|
1410
1412
|
function generateJobId() {
|
|
1411
1413
|
const timestamp = Date.now().toString(36);
|
|
1412
|
-
const random =
|
|
1414
|
+
const random = (0, import_crypto.randomBytes)(4).toString("hex");
|
|
1413
1415
|
return `job_${timestamp}_${random}`;
|
|
1414
1416
|
}
|
|
1415
1417
|
|
|
1416
1418
|
// src/context/CorrelationContext.ts
|
|
1417
1419
|
var import_async_hooks = require("async_hooks");
|
|
1420
|
+
var import_crypto2 = require("crypto");
|
|
1418
1421
|
var CorrelationContextManager = class {
|
|
1419
1422
|
storage = new import_async_hooks.AsyncLocalStorage();
|
|
1420
1423
|
idGenerator;
|
|
1421
1424
|
constructor() {
|
|
1422
1425
|
this.idGenerator = () => {
|
|
1423
1426
|
const timestamp = Date.now().toString(36);
|
|
1424
|
-
const random =
|
|
1427
|
+
const random = (0, import_crypto2.randomBytes)(4).toString("hex");
|
|
1425
1428
|
return `${timestamp}-${random}`;
|
|
1426
1429
|
};
|
|
1427
1430
|
}
|
|
@@ -2027,10 +2030,11 @@ var MemoryQueue = class {
|
|
|
2027
2030
|
};
|
|
2028
2031
|
|
|
2029
2032
|
// src/adapters/console/ConsoleEmail.ts
|
|
2033
|
+
var import_crypto3 = require("crypto");
|
|
2030
2034
|
var ConsoleEmail = class {
|
|
2031
2035
|
sentEmails = [];
|
|
2032
2036
|
async send(message) {
|
|
2033
|
-
const id = `console_${Date.now()}_${
|
|
2037
|
+
const id = `console_${Date.now()}_${(0, import_crypto3.randomBytes)(4).toString("hex")}`;
|
|
2034
2038
|
console.log("\n" + "=".repeat(60));
|
|
2035
2039
|
console.log("\u{1F4E7} EMAIL SENT (Console Adapter)");
|
|
2036
2040
|
console.log("=".repeat(60));
|
|
@@ -2105,6 +2109,7 @@ var ConsoleEmail = class {
|
|
|
2105
2109
|
};
|
|
2106
2110
|
|
|
2107
2111
|
// src/interfaces/ISecrets.ts
|
|
2112
|
+
var import_crypto4 = require("crypto");
|
|
2108
2113
|
var EnvSecrets = class {
|
|
2109
2114
|
prefix;
|
|
2110
2115
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -2305,12 +2310,7 @@ var MemorySecrets = class {
|
|
|
2305
2310
|
return true;
|
|
2306
2311
|
}
|
|
2307
2312
|
generateSecureValue(length = 32) {
|
|
2308
|
-
|
|
2309
|
-
let result = "";
|
|
2310
|
-
for (let i = 0; i < length; i++) {
|
|
2311
|
-
result += chars[Math.floor(Math.random() * chars.length)];
|
|
2312
|
-
}
|
|
2313
|
-
return result;
|
|
2313
|
+
return (0, import_crypto4.randomBytes)(length).toString("base64url").slice(0, length);
|
|
2314
2314
|
}
|
|
2315
2315
|
/**
|
|
2316
2316
|
* Clear all secrets (for testing)
|
|
@@ -2520,6 +2520,25 @@ var RAGConfigSchema = import_zod.z.object({
|
|
|
2520
2520
|
message: "Pinecone requires apiKey and indexName; Weaviate requires host"
|
|
2521
2521
|
}
|
|
2522
2522
|
);
|
|
2523
|
+
var CryptoConfigSchema = import_zod.z.object({
|
|
2524
|
+
enabled: import_zod.z.boolean().default(false).describe("Enable field-level encryption"),
|
|
2525
|
+
masterKey: import_zod.z.string().optional().describe("256-bit master key as hex (64 chars). Required when enabled."),
|
|
2526
|
+
hmacKey: import_zod.z.string().optional().describe("HMAC key for deterministic hashing (derived from master key if not provided)")
|
|
2527
|
+
}).refine(
|
|
2528
|
+
(data) => {
|
|
2529
|
+
if (data.enabled) {
|
|
2530
|
+
return data.masterKey && data.masterKey.length >= 64;
|
|
2531
|
+
}
|
|
2532
|
+
return true;
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
message: "Crypto requires a 256-bit master key (64 hex characters) when enabled"
|
|
2536
|
+
}
|
|
2537
|
+
);
|
|
2538
|
+
var SecurityConfigSchema = import_zod.z.object({
|
|
2539
|
+
enforceTls: import_zod.z.boolean().default(true).describe("Enforce TLS for production connections"),
|
|
2540
|
+
tlsWarnOnly: import_zod.z.boolean().default(false).describe("Warn instead of throwing when TLS is missing in production")
|
|
2541
|
+
});
|
|
2523
2542
|
var RetryConfigSchema = import_zod.z.object({
|
|
2524
2543
|
enabled: import_zod.z.boolean().default(true).describe("Enable retry for failed operations"),
|
|
2525
2544
|
maxAttempts: import_zod.z.number().int().min(1).max(10).default(3).describe("Maximum retry attempts"),
|
|
@@ -2609,6 +2628,10 @@ var PlatformConfigSchema = import_zod.z.object({
|
|
|
2609
2628
|
// AI configurations
|
|
2610
2629
|
ai: AIConfigSchema.default({ enabled: false }),
|
|
2611
2630
|
rag: RAGConfigSchema.default({ enabled: false }),
|
|
2631
|
+
// Crypto configuration
|
|
2632
|
+
crypto: CryptoConfigSchema.default({ enabled: false }),
|
|
2633
|
+
// Security configuration
|
|
2634
|
+
security: SecurityConfigSchema.default({}),
|
|
2612
2635
|
// Resilience configuration
|
|
2613
2636
|
resilience: ResilienceConfigSchema.default({}),
|
|
2614
2637
|
// Observability configuration
|
|
@@ -2686,6 +2709,15 @@ function loadConfig() {
|
|
|
2686
2709
|
embeddingApiKey: process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY,
|
|
2687
2710
|
embeddingModel: process.env.EMBEDDING_MODEL
|
|
2688
2711
|
},
|
|
2712
|
+
crypto: {
|
|
2713
|
+
enabled: process.env.CRYPTO_ENABLED === "true",
|
|
2714
|
+
masterKey: process.env.CRYPTO_MASTER_KEY,
|
|
2715
|
+
hmacKey: process.env.CRYPTO_HMAC_KEY
|
|
2716
|
+
},
|
|
2717
|
+
security: {
|
|
2718
|
+
enforceTls: process.env.SECURITY_ENFORCE_TLS !== "false",
|
|
2719
|
+
tlsWarnOnly: process.env.SECURITY_TLS_WARN_ONLY === "true"
|
|
2720
|
+
},
|
|
2689
2721
|
resilience: {
|
|
2690
2722
|
retry: {
|
|
2691
2723
|
enabled: process.env.RESILIENCE_RETRY_ENABLED !== "false",
|
|
@@ -2736,6 +2768,7 @@ function loadConfig() {
|
|
|
2736
2768
|
}
|
|
2737
2769
|
|
|
2738
2770
|
// src/interfaces/ITracing.ts
|
|
2771
|
+
var import_crypto5 = require("crypto");
|
|
2739
2772
|
var MemorySpan = class {
|
|
2740
2773
|
name;
|
|
2741
2774
|
context;
|
|
@@ -2755,7 +2788,7 @@ var MemorySpan = class {
|
|
|
2755
2788
|
};
|
|
2756
2789
|
}
|
|
2757
2790
|
generateSpanId() {
|
|
2758
|
-
return
|
|
2791
|
+
return (0, import_crypto5.randomBytes)(8).toString("hex");
|
|
2759
2792
|
}
|
|
2760
2793
|
setAttribute(key, value) {
|
|
2761
2794
|
this._attributes[key] = value;
|
|
@@ -2815,7 +2848,7 @@ var MemoryTracing = class {
|
|
|
2815
2848
|
this.traceId = this.generateTraceId();
|
|
2816
2849
|
}
|
|
2817
2850
|
generateTraceId() {
|
|
2818
|
-
return
|
|
2851
|
+
return (0, import_crypto5.randomBytes)(16).toString("hex");
|
|
2819
2852
|
}
|
|
2820
2853
|
startSpan(name, options) {
|
|
2821
2854
|
const span = new MemorySpan(
|
|
@@ -3255,6 +3288,147 @@ var NoopMetrics = class {
|
|
|
3255
3288
|
// src/factory.ts
|
|
3256
3289
|
init_IAI();
|
|
3257
3290
|
init_IRAG();
|
|
3291
|
+
|
|
3292
|
+
// src/adapters/memory/MemoryCrypto.ts
|
|
3293
|
+
var import_crypto7 = require("crypto");
|
|
3294
|
+
var MemoryCrypto = class {
|
|
3295
|
+
keys = /* @__PURE__ */ new Map();
|
|
3296
|
+
activeKeyId;
|
|
3297
|
+
hmacKey;
|
|
3298
|
+
constructor(options) {
|
|
3299
|
+
const masterKeyBuf = options?.masterKey ? Buffer.from(options.masterKey, "hex") : (0, import_crypto7.randomBytes)(32);
|
|
3300
|
+
this.hmacKey = options?.hmacKey ? Buffer.from(options.hmacKey, "hex") : (0, import_crypto7.randomBytes)(32);
|
|
3301
|
+
const keyId = this.generateKeyId();
|
|
3302
|
+
this.keys.set(keyId, {
|
|
3303
|
+
id: keyId,
|
|
3304
|
+
key: masterKeyBuf,
|
|
3305
|
+
status: "active",
|
|
3306
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3307
|
+
});
|
|
3308
|
+
this.activeKeyId = keyId;
|
|
3309
|
+
}
|
|
3310
|
+
async encrypt(plaintext, options) {
|
|
3311
|
+
const keyId = options?.keyId || this.activeKeyId;
|
|
3312
|
+
const stored = this.keys.get(keyId);
|
|
3313
|
+
if (!stored) {
|
|
3314
|
+
throw new Error(`Key not found: ${keyId}`);
|
|
3315
|
+
}
|
|
3316
|
+
if (stored.status === "retired") {
|
|
3317
|
+
throw new Error(`Key is retired: ${keyId}`);
|
|
3318
|
+
}
|
|
3319
|
+
if (stored.status === "decrypt-only" && !options?.keyId) {
|
|
3320
|
+
throw new Error(`Key is decrypt-only: ${keyId}`);
|
|
3321
|
+
}
|
|
3322
|
+
const iv = (0, import_crypto7.randomBytes)(12);
|
|
3323
|
+
const cipher = (0, import_crypto7.createCipheriv)("aes-256-gcm", stored.key, iv);
|
|
3324
|
+
if (options?.aad) {
|
|
3325
|
+
cipher.setAAD(Buffer.from(options.aad, "utf8"));
|
|
3326
|
+
}
|
|
3327
|
+
const encrypted = Buffer.concat([
|
|
3328
|
+
cipher.update(plaintext, "utf8"),
|
|
3329
|
+
cipher.final()
|
|
3330
|
+
]);
|
|
3331
|
+
const tag = cipher.getAuthTag();
|
|
3332
|
+
return {
|
|
3333
|
+
ciphertext: encrypted.toString("base64"),
|
|
3334
|
+
iv: iv.toString("base64"),
|
|
3335
|
+
tag: tag.toString("base64"),
|
|
3336
|
+
keyId,
|
|
3337
|
+
algorithm: "aes-256-gcm",
|
|
3338
|
+
version: 1
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
async decrypt(field, options) {
|
|
3342
|
+
const stored = this.keys.get(field.keyId);
|
|
3343
|
+
if (!stored) {
|
|
3344
|
+
throw new Error(`Key not found: ${field.keyId}`);
|
|
3345
|
+
}
|
|
3346
|
+
if (stored.status === "retired") {
|
|
3347
|
+
throw new Error(`Key is retired and cannot decrypt: ${field.keyId}`);
|
|
3348
|
+
}
|
|
3349
|
+
const decipher = (0, import_crypto7.createDecipheriv)(
|
|
3350
|
+
"aes-256-gcm",
|
|
3351
|
+
stored.key,
|
|
3352
|
+
Buffer.from(field.iv, "base64")
|
|
3353
|
+
);
|
|
3354
|
+
decipher.setAuthTag(Buffer.from(field.tag, "base64"));
|
|
3355
|
+
if (options?.aad) {
|
|
3356
|
+
decipher.setAAD(Buffer.from(options.aad, "utf8"));
|
|
3357
|
+
}
|
|
3358
|
+
const decrypted = Buffer.concat([
|
|
3359
|
+
decipher.update(Buffer.from(field.ciphertext, "base64")),
|
|
3360
|
+
decipher.final()
|
|
3361
|
+
]);
|
|
3362
|
+
return decrypted.toString("utf8");
|
|
3363
|
+
}
|
|
3364
|
+
async encryptDeterministic(plaintext, options) {
|
|
3365
|
+
const hash = await this.computeHash(plaintext);
|
|
3366
|
+
const encrypted = await this.encrypt(plaintext, options);
|
|
3367
|
+
return { hash, encrypted };
|
|
3368
|
+
}
|
|
3369
|
+
async computeHash(plaintext) {
|
|
3370
|
+
return (0, import_crypto7.createHmac)("sha256", this.hmacKey).update(plaintext, "utf8").digest("hex");
|
|
3371
|
+
}
|
|
3372
|
+
async encryptBatch(fields, options) {
|
|
3373
|
+
const result = {};
|
|
3374
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3375
|
+
result[key] = await this.encrypt(value, options);
|
|
3376
|
+
}
|
|
3377
|
+
return result;
|
|
3378
|
+
}
|
|
3379
|
+
async decryptBatch(fields, options) {
|
|
3380
|
+
const result = {};
|
|
3381
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3382
|
+
result[key] = await this.decrypt(value, options);
|
|
3383
|
+
}
|
|
3384
|
+
return result;
|
|
3385
|
+
}
|
|
3386
|
+
async rotateKey() {
|
|
3387
|
+
const previousKeyId = this.activeKeyId;
|
|
3388
|
+
const currentKey = this.keys.get(previousKeyId);
|
|
3389
|
+
if (currentKey) {
|
|
3390
|
+
currentKey.status = "decrypt-only";
|
|
3391
|
+
}
|
|
3392
|
+
const newKeyId = this.generateKeyId();
|
|
3393
|
+
this.keys.set(newKeyId, {
|
|
3394
|
+
id: newKeyId,
|
|
3395
|
+
key: (0, import_crypto7.randomBytes)(32),
|
|
3396
|
+
status: "active",
|
|
3397
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3398
|
+
});
|
|
3399
|
+
this.activeKeyId = newKeyId;
|
|
3400
|
+
return { newKeyId, previousKeyId };
|
|
3401
|
+
}
|
|
3402
|
+
async reEncrypt(field, options) {
|
|
3403
|
+
const plaintext = await this.decrypt(field);
|
|
3404
|
+
return this.encrypt(plaintext, options);
|
|
3405
|
+
}
|
|
3406
|
+
async listKeys() {
|
|
3407
|
+
return Array.from(this.keys.values()).map((k) => ({
|
|
3408
|
+
keyId: k.id,
|
|
3409
|
+
createdAt: k.createdAt,
|
|
3410
|
+
status: k.status
|
|
3411
|
+
}));
|
|
3412
|
+
}
|
|
3413
|
+
async getActiveKeyId() {
|
|
3414
|
+
return this.activeKeyId;
|
|
3415
|
+
}
|
|
3416
|
+
async healthCheck() {
|
|
3417
|
+
try {
|
|
3418
|
+
const testPlain = "health-check-test";
|
|
3419
|
+
const encrypted = await this.encrypt(testPlain);
|
|
3420
|
+
const decrypted = await this.decrypt(encrypted);
|
|
3421
|
+
return decrypted === testPlain;
|
|
3422
|
+
} catch {
|
|
3423
|
+
return false;
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
generateKeyId() {
|
|
3427
|
+
return `key_${(0, import_crypto7.randomBytes)(8).toString("hex")}`;
|
|
3428
|
+
}
|
|
3429
|
+
};
|
|
3430
|
+
|
|
3431
|
+
// src/factory.ts
|
|
3258
3432
|
function createLogger(config) {
|
|
3259
3433
|
if (!config.observability.logging) {
|
|
3260
3434
|
return new NoopLogger();
|
|
@@ -3289,6 +3463,7 @@ function createPlatform(config) {
|
|
|
3289
3463
|
const tracing = finalConfig.observability.tracing.provider === "memory" ? new MemoryTracing() : new NoopTracing();
|
|
3290
3464
|
const ai = finalConfig.ai.enabled ? new MemoryAI() : null;
|
|
3291
3465
|
const rag = finalConfig.rag.enabled ? new MemoryRAG() : null;
|
|
3466
|
+
const crypto2 = finalConfig.crypto.enabled ? new MemoryCrypto() : null;
|
|
3292
3467
|
return createPlatformFromAdapters(
|
|
3293
3468
|
db,
|
|
3294
3469
|
cache,
|
|
@@ -3299,10 +3474,11 @@ function createPlatform(config) {
|
|
|
3299
3474
|
metrics,
|
|
3300
3475
|
tracing,
|
|
3301
3476
|
ai,
|
|
3302
|
-
rag
|
|
3477
|
+
rag,
|
|
3478
|
+
crypto2
|
|
3303
3479
|
);
|
|
3304
3480
|
}
|
|
3305
|
-
function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag) {
|
|
3481
|
+
function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag, crypto2) {
|
|
3306
3482
|
const platform = {
|
|
3307
3483
|
db,
|
|
3308
3484
|
cache,
|
|
@@ -3356,6 +3532,9 @@ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, me
|
|
|
3356
3532
|
if (rag) {
|
|
3357
3533
|
platform.rag = rag;
|
|
3358
3534
|
}
|
|
3535
|
+
if (crypto2) {
|
|
3536
|
+
platform.crypto = crypto2;
|
|
3537
|
+
}
|
|
3359
3538
|
return platform;
|
|
3360
3539
|
}
|
|
3361
3540
|
function deepMerge(target, source) {
|