@getstrata/core 1.0.3 → 1.0.4
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/CHANGELOG.md +9 -0
- package/dist/core/runtime/appEnv.d.ts +16 -2
- package/dist/core/runtime/appKeyPrefix.d.ts +8 -1
- package/dist/entries/audit/exportAuditLogs.js +83 -18
- package/dist/entries/audit/siemFormatter.js +32 -3
- package/dist/entries/auth/intendedUrlCookie.js +33 -4
- package/dist/entries/auth/jwt.js +36 -4
- package/dist/entries/auth/jwtGuard.js +36 -4
- package/dist/entries/auth/oauth/providers.js +32 -3
- package/dist/entries/auth/oauth/samlProvider.js +32 -3
- package/dist/entries/auth/passwordConfirmCookie.js +35 -6
- package/dist/entries/auth/sessionCookie.js +35 -6
- package/dist/entries/auth/sessionGuard.js +35 -6
- package/dist/entries/auth/tokenHash.js +33 -4
- package/dist/entries/cache/createCacheStore.js +32 -3
- package/dist/entries/facades.js +32 -3
- package/dist/entries/http/corsMiddleware.js +16 -1
- package/dist/entries/http/csrfMiddleware.js +34 -5
- package/dist/entries/http/csrfToken.js +34 -5
- package/dist/entries/http/flashMiddleware.js +33 -4
- package/dist/entries/http/flashSession.js +33 -4
- package/dist/entries/http/loginThrottleMiddleware.js +1 -373
- package/dist/entries/http/memoryThrottleMiddleware.js +32 -58
- package/dist/entries/http/response.js +16 -56
- package/dist/entries/http/scimThrottleMiddleware.js +32 -3
- package/dist/entries/http/securityHeadersMiddleware.js +32 -3
- package/dist/entries/http/signedUrl.js +33 -4
- package/dist/entries/http/throttleMiddleware.js +32 -58
- package/dist/entries/http/webErrorResponse.js +1 -401
- package/dist/entries/jobs/exportAuditLogsJob.js +83 -18
- package/dist/entries/lifecycle/gracefulShutdown.js +1 -50
- package/dist/entries/mail/mailer.js +32 -3
- package/dist/entries/openapi/generator.js +32 -3
- package/dist/entries/queue/createAppQueue.js +32 -3
- package/dist/entries/queue/publicQueue.js +32 -3
- package/dist/entries/queue/queueMetrics.js +32 -3
- package/dist/entries/queue/redisQueue.js +32 -3
- package/dist/entries/runtime/appEnv.js +18 -1
- package/dist/entries/runtime/appKeyPrefix.js +1 -70
- package/dist/entries/security/oauthState.js +33 -4
- package/dist/entries/security/safeFetch.js +32 -9
- package/dist/entries/security/safeUrl.js +1 -105
- package/dist/entries/security/totp.js +32 -3
- package/dist/entries/tenant/databaseTenantContext.js +48 -6
- package/dist/entries/tracing/tracingMiddleware.js +32 -3
- package/dist/entries/view.js +1 -778
- package/dist/framework/public-api.d.ts +27 -18
- package/dist/index.js +442 -53
- package/package.json +2 -2
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/auth/sessionCookie.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
|
@@ -74,7 +103,7 @@ function sessionRememberTtlSeconds() {
|
|
|
74
103
|
return parsePositiveSeconds(process.env.SESSION_REMEMBER_TTL_SECONDS, SESSION_REMEMBER_TTL_SECONDS);
|
|
75
104
|
}
|
|
76
105
|
function resolveSessionSecret() {
|
|
77
|
-
return
|
|
106
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "session-secret");
|
|
78
107
|
}
|
|
79
108
|
function signSession(userId, issuedAt, ttlSeconds) {
|
|
80
109
|
const payload = ttlSeconds === undefined ? `${userId}.${issuedAt}` : `${userId}.${issuedAt}.${ttlSeconds}`;
|
|
@@ -153,7 +182,7 @@ function createSessionCookieDetails(userId, options = {}) {
|
|
|
153
182
|
const issuedAt = Date.now();
|
|
154
183
|
const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
|
|
155
184
|
const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
|
|
156
|
-
const secure =
|
|
185
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
157
186
|
return {
|
|
158
187
|
header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
|
|
159
188
|
userId,
|
|
@@ -165,7 +194,7 @@ function createSessionCookie(userId, options = {}) {
|
|
|
165
194
|
return createSessionCookieDetails(userId, options).header;
|
|
166
195
|
}
|
|
167
196
|
function clearSessionCookie() {
|
|
168
|
-
const secure =
|
|
197
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
169
198
|
return `${sessionCookieName()}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`;
|
|
170
199
|
}
|
|
171
200
|
export {
|
|
@@ -69,6 +69,26 @@ function resolveAbilitiesForRole(role) {
|
|
|
69
69
|
// ../../src/core/auth/sessionCookie.ts
|
|
70
70
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
71
71
|
|
|
72
|
+
// ../../src/core/runtime/appEnv.ts
|
|
73
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
74
|
+
function normalizeEnvValue(value) {
|
|
75
|
+
return (value ?? "").trim().toLowerCase();
|
|
76
|
+
}
|
|
77
|
+
function isProductionEnv(env = process.env) {
|
|
78
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
79
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
80
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (appEnv === "") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
87
|
+
}
|
|
88
|
+
function envFlagEnabled(value) {
|
|
89
|
+
return value === "true";
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
73
93
|
function appKeyPrefix() {
|
|
74
94
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -79,6 +99,18 @@ function appCookieName(kind) {
|
|
|
79
99
|
function appDevSecret(kind) {
|
|
80
100
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
81
101
|
}
|
|
102
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
const value = env[name]?.trim();
|
|
105
|
+
if (value) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (isProductionEnv(env)) {
|
|
110
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
111
|
+
}
|
|
112
|
+
return appDevSecret(devKind);
|
|
113
|
+
}
|
|
82
114
|
function namespacedRedisKey(kind) {
|
|
83
115
|
return `${appKeyPrefix()}:${kind}`;
|
|
84
116
|
}
|
|
@@ -96,9 +128,6 @@ function appUserAgent() {
|
|
|
96
128
|
function otelServiceName() {
|
|
97
129
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
98
130
|
}
|
|
99
|
-
function webhookSignatureHeader() {
|
|
100
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
101
|
-
}
|
|
102
131
|
function appDisplayName() {
|
|
103
132
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
104
133
|
}
|
|
@@ -141,7 +170,7 @@ function sessionRememberTtlSeconds() {
|
|
|
141
170
|
return parsePositiveSeconds(process.env.SESSION_REMEMBER_TTL_SECONDS, SESSION_REMEMBER_TTL_SECONDS);
|
|
142
171
|
}
|
|
143
172
|
function resolveSessionSecret() {
|
|
144
|
-
return
|
|
173
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "session-secret");
|
|
145
174
|
}
|
|
146
175
|
function signSession(userId, issuedAt, ttlSeconds) {
|
|
147
176
|
const payload = ttlSeconds === undefined ? `${userId}.${issuedAt}` : `${userId}.${issuedAt}.${ttlSeconds}`;
|
|
@@ -220,7 +249,7 @@ function createSessionCookieDetails(userId, options = {}) {
|
|
|
220
249
|
const issuedAt = Date.now();
|
|
221
250
|
const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
|
|
222
251
|
const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
|
|
223
|
-
const secure =
|
|
252
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
224
253
|
return {
|
|
225
254
|
header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
|
|
226
255
|
userId,
|
|
@@ -232,7 +261,7 @@ function createSessionCookie(userId, options = {}) {
|
|
|
232
261
|
return createSessionCookieDetails(userId, options).header;
|
|
233
262
|
}
|
|
234
263
|
function clearSessionCookie() {
|
|
235
|
-
const secure =
|
|
264
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
236
265
|
return `${sessionCookieName()}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`;
|
|
237
266
|
}
|
|
238
267
|
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/auth/tokenHash.ts
|
|
3
3
|
import { createHash, createHmac } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
|
@@ -58,7 +87,7 @@ function sdkClientClassName() {
|
|
|
58
87
|
|
|
59
88
|
// ../../src/core/auth/tokenHash.ts
|
|
60
89
|
function resolveTokenPepper() {
|
|
61
|
-
return
|
|
90
|
+
return requireConfiguredSecret(["TOKEN_HASH_PEPPER"], "token-pepper");
|
|
62
91
|
}
|
|
63
92
|
function hashApiToken(token) {
|
|
64
93
|
const pepper = resolveTokenPepper();
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/cache/redisCacheStore.ts
|
|
3
3
|
var {RedisClient } = globalThis.Bun;
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
package/dist/entries/facades.js
CHANGED
|
@@ -11,6 +11,26 @@ import {
|
|
|
11
11
|
resolveApplicationQueue
|
|
12
12
|
} from "@getstrata/core/runtime/applicationRegistry";
|
|
13
13
|
|
|
14
|
+
// ../../src/core/runtime/appEnv.ts
|
|
15
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
16
|
+
function normalizeEnvValue(value) {
|
|
17
|
+
return (value ?? "").trim().toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
function isProductionEnv(env = process.env) {
|
|
20
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
21
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
22
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (appEnv === "") {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
29
|
+
}
|
|
30
|
+
function envFlagEnabled(value) {
|
|
31
|
+
return value === "true";
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
15
35
|
function appKeyPrefix() {
|
|
16
36
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -21,6 +41,18 @@ function appCookieName(kind) {
|
|
|
21
41
|
function appDevSecret(kind) {
|
|
22
42
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
23
43
|
}
|
|
44
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
const value = env[name]?.trim();
|
|
47
|
+
if (value) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (isProductionEnv(env)) {
|
|
52
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
53
|
+
}
|
|
54
|
+
return appDevSecret(devKind);
|
|
55
|
+
}
|
|
24
56
|
function namespacedRedisKey(kind) {
|
|
25
57
|
return `${appKeyPrefix()}:${kind}`;
|
|
26
58
|
}
|
|
@@ -38,9 +70,6 @@ function appUserAgent() {
|
|
|
38
70
|
function otelServiceName() {
|
|
39
71
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
40
72
|
}
|
|
41
|
-
function webhookSignatureHeader() {
|
|
42
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
43
|
-
}
|
|
44
73
|
function appDisplayName() {
|
|
45
74
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
46
75
|
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/runtime/appEnv.ts
|
|
3
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
4
|
+
function normalizeEnvValue(value) {
|
|
5
|
+
return (value ?? "").trim().toLowerCase();
|
|
6
|
+
}
|
|
3
7
|
function isProductionEnv(env = process.env) {
|
|
4
|
-
|
|
8
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
9
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
10
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (appEnv === "") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
17
|
+
}
|
|
18
|
+
function envFlagEnabled(value) {
|
|
19
|
+
return value === "true";
|
|
5
20
|
}
|
|
6
21
|
|
|
7
22
|
// ../../src/core/http/corsMiddleware.ts
|
|
@@ -5,6 +5,26 @@ import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
|
5
5
|
// ../../src/core/http/csrfToken.ts
|
|
6
6
|
import { timingSafeEqual } from "crypto";
|
|
7
7
|
|
|
8
|
+
// ../../src/core/runtime/appEnv.ts
|
|
9
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
10
|
+
function normalizeEnvValue(value) {
|
|
11
|
+
return (value ?? "").trim().toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
function isProductionEnv(env = process.env) {
|
|
14
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
15
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
16
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (appEnv === "") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
23
|
+
}
|
|
24
|
+
function envFlagEnabled(value) {
|
|
25
|
+
return value === "true";
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
9
29
|
function appKeyPrefix() {
|
|
10
30
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -15,6 +35,18 @@ function appCookieName(kind) {
|
|
|
15
35
|
function appDevSecret(kind) {
|
|
16
36
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
17
37
|
}
|
|
38
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
39
|
+
for (const name of names) {
|
|
40
|
+
const value = env[name]?.trim();
|
|
41
|
+
if (value) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (isProductionEnv(env)) {
|
|
46
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
47
|
+
}
|
|
48
|
+
return appDevSecret(devKind);
|
|
49
|
+
}
|
|
18
50
|
function namespacedRedisKey(kind) {
|
|
19
51
|
return `${appKeyPrefix()}:${kind}`;
|
|
20
52
|
}
|
|
@@ -32,9 +64,6 @@ function appUserAgent() {
|
|
|
32
64
|
function otelServiceName() {
|
|
33
65
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
34
66
|
}
|
|
35
|
-
function webhookSignatureHeader() {
|
|
36
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
37
|
-
}
|
|
38
67
|
function appDisplayName() {
|
|
39
68
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
40
69
|
}
|
|
@@ -120,7 +149,7 @@ function csrfCookieName() {
|
|
|
120
149
|
return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
|
|
121
150
|
}
|
|
122
151
|
function resolveCsrfSecret() {
|
|
123
|
-
return
|
|
152
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "csrf-secret");
|
|
124
153
|
}
|
|
125
154
|
function csrfVerifyOptions() {
|
|
126
155
|
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
@@ -135,7 +164,7 @@ function tokensMatch(left, right) {
|
|
|
135
164
|
}
|
|
136
165
|
function createCsrfTokenCookie() {
|
|
137
166
|
const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
|
|
138
|
-
const secure =
|
|
167
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
139
168
|
return {
|
|
140
169
|
token,
|
|
141
170
|
cookie: `${csrfCookieName()}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}${secure}`
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/http/csrfToken.ts
|
|
3
3
|
import { timingSafeEqual } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
|
@@ -117,7 +146,7 @@ function csrfCookieName() {
|
|
|
117
146
|
return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
|
|
118
147
|
}
|
|
119
148
|
function resolveCsrfSecret() {
|
|
120
|
-
return
|
|
149
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "csrf-secret");
|
|
121
150
|
}
|
|
122
151
|
function csrfVerifyOptions() {
|
|
123
152
|
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
@@ -132,7 +161,7 @@ function tokensMatch(left, right) {
|
|
|
132
161
|
}
|
|
133
162
|
function createCsrfTokenCookie() {
|
|
134
163
|
const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
|
|
135
|
-
const secure =
|
|
164
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
136
165
|
return {
|
|
137
166
|
token,
|
|
138
167
|
cookie: `${csrfCookieName()}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}${secure}`
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/http/flashSession.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
|
@@ -63,7 +92,7 @@ function flashCookieName() {
|
|
|
63
92
|
return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
|
|
64
93
|
}
|
|
65
94
|
function resolveFlashSecret() {
|
|
66
|
-
return
|
|
95
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET"], "flash-secret");
|
|
67
96
|
}
|
|
68
97
|
function signFlashPayload(payload, issuedAt) {
|
|
69
98
|
const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
// ../../src/core/http/flashSession.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appEnv.ts
|
|
6
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
7
|
+
function normalizeEnvValue(value) {
|
|
8
|
+
return (value ?? "").trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isProductionEnv(env = process.env) {
|
|
11
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
12
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
13
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (appEnv === "") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
20
|
+
}
|
|
21
|
+
function envFlagEnabled(value) {
|
|
22
|
+
return value === "true";
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
26
|
function appKeyPrefix() {
|
|
7
27
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -12,6 +32,18 @@ function appCookieName(kind) {
|
|
|
12
32
|
function appDevSecret(kind) {
|
|
13
33
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
34
|
}
|
|
35
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
36
|
+
for (const name of names) {
|
|
37
|
+
const value = env[name]?.trim();
|
|
38
|
+
if (value) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isProductionEnv(env)) {
|
|
43
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
44
|
+
}
|
|
45
|
+
return appDevSecret(devKind);
|
|
46
|
+
}
|
|
15
47
|
function namespacedRedisKey(kind) {
|
|
16
48
|
return `${appKeyPrefix()}:${kind}`;
|
|
17
49
|
}
|
|
@@ -29,9 +61,6 @@ function appUserAgent() {
|
|
|
29
61
|
function otelServiceName() {
|
|
30
62
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
63
|
}
|
|
32
|
-
function webhookSignatureHeader() {
|
|
33
|
-
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
-
}
|
|
35
64
|
function appDisplayName() {
|
|
36
65
|
return process.env.APP_NAME?.trim() || "Strata";
|
|
37
66
|
}
|
|
@@ -63,7 +92,7 @@ function flashCookieName() {
|
|
|
63
92
|
return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
|
|
64
93
|
}
|
|
65
94
|
function resolveFlashSecret() {
|
|
66
|
-
return
|
|
95
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET"], "flash-secret");
|
|
67
96
|
}
|
|
68
97
|
function signFlashPayload(payload, issuedAt) {
|
|
69
98
|
const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|