@getstrata/core 0.5.5 → 0.5.6
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 +3 -0
- package/dist/core/crypto/nonCryptographicHash.d.ts +2 -0
- package/dist/core/http/cookies.d.ts +4 -0
- package/dist/core/http/csrfProtection.d.ts +12 -0
- package/dist/entries/auth/guard.js +20 -20
- package/dist/entries/database.js +20 -20
- package/dist/entries/http/csrfToken.js +49 -65
- package/dist/entries/http/etag.js +6 -4
- package/dist/entries/http/webErrorResponse.js +73 -87
- package/dist/entries/http.js +80 -90
- package/dist/entries/queue/createAppQueue.js +20 -20
- package/dist/entries/queue/publicQueue.js +20 -20
- package/dist/entries/queue/queueMetrics.js +20 -20
- package/dist/entries/view.js +73 -87
- package/dist/framework/public-api.d.ts +4 -0
- package/dist/index.js +121 -107
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1487,6 +1487,14 @@ var baseRepository_default = BaseRepository;
|
|
|
1487
1487
|
function bindDatabaseConnection2(connection) {
|
|
1488
1488
|
bindDatabaseConnection(connection);
|
|
1489
1489
|
}
|
|
1490
|
+
// ../../src/core/database/connection.ts
|
|
1491
|
+
function createDatabaseConnection2(source) {
|
|
1492
|
+
return {
|
|
1493
|
+
async unsafe(query, params = []) {
|
|
1494
|
+
return await source.unsafe(query, params);
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1490
1498
|
// ../../src/core/database/migrations/advisoryLock.ts
|
|
1491
1499
|
var MIGRATION_LOCK_KEY = 42424242;
|
|
1492
1500
|
async function withMigrationLock(db2, callback, lockKey = MIGRATION_LOCK_KEY) {
|
|
@@ -1739,26 +1747,26 @@ class Model {
|
|
|
1739
1747
|
throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
|
|
1740
1748
|
}
|
|
1741
1749
|
static primaryKeyField() {
|
|
1742
|
-
return resolveModelRepository(
|
|
1750
|
+
return resolveModelRepository(Model).getTable().primaryKey;
|
|
1743
1751
|
}
|
|
1744
1752
|
static hydrateAttributes(attributes) {
|
|
1745
|
-
const casts = modelStatics(
|
|
1753
|
+
const casts = modelStatics(Model).$casts ?? {};
|
|
1746
1754
|
return applyCasts(attributes, casts, "hydrate");
|
|
1747
1755
|
}
|
|
1748
1756
|
static dehydrateAttributes(attributes) {
|
|
1749
|
-
const casts = modelStatics(
|
|
1757
|
+
const casts = modelStatics(Model).$casts ?? {};
|
|
1750
1758
|
return applyCasts(attributes, casts, "dehydrate");
|
|
1751
1759
|
}
|
|
1752
1760
|
static fromRecord(record, repository, exists = true) {
|
|
1753
|
-
const statics = modelStatics(
|
|
1761
|
+
const statics = modelStatics(Model);
|
|
1754
1762
|
const hydrated = statics.hydrateAttributes(record);
|
|
1755
1763
|
return new statics(hydrated, repository, exists);
|
|
1756
1764
|
}
|
|
1757
1765
|
static boot() {}
|
|
1758
1766
|
static addGlobalScope(_name, scope) {
|
|
1759
|
-
ensureBooted(
|
|
1760
|
-
const existing = modelGlobalScopes.get(
|
|
1761
|
-
modelGlobalScopes.set(
|
|
1767
|
+
ensureBooted(Model);
|
|
1768
|
+
const existing = modelGlobalScopes.get(Model) ?? [];
|
|
1769
|
+
modelGlobalScopes.set(Model, [
|
|
1762
1770
|
...existing,
|
|
1763
1771
|
scope
|
|
1764
1772
|
]);
|
|
@@ -1768,17 +1776,17 @@ class Model {
|
|
|
1768
1776
|
}
|
|
1769
1777
|
static query() {
|
|
1770
1778
|
ensureBooted(this);
|
|
1771
|
-
const repository = resolveModelRepository(
|
|
1779
|
+
const repository = resolveModelRepository(Model);
|
|
1772
1780
|
let query = repository.query();
|
|
1773
|
-
for (const scope of getGlobalScopes(
|
|
1781
|
+
for (const scope of getGlobalScopes(Model)) {
|
|
1774
1782
|
query = scope(query);
|
|
1775
1783
|
}
|
|
1776
1784
|
return query;
|
|
1777
1785
|
}
|
|
1778
1786
|
static async create(attributes) {
|
|
1779
1787
|
const statics = modelStatics(this);
|
|
1780
|
-
ensureBooted(
|
|
1781
|
-
const repository = resolveModelRepository(
|
|
1788
|
+
ensureBooted(Model);
|
|
1789
|
+
const repository = resolveModelRepository(Model);
|
|
1782
1790
|
const table = repository.getTable();
|
|
1783
1791
|
const timestamps = statics.$timestamps ?? true;
|
|
1784
1792
|
const assignable = filterMassAssignable(statics.$fillable, statics.$guarded, attributes);
|
|
@@ -1788,23 +1796,23 @@ class Model {
|
|
|
1788
1796
|
return statics.fromRecord(record, repository, true);
|
|
1789
1797
|
}
|
|
1790
1798
|
static async find(id) {
|
|
1791
|
-
const statics = modelStatics(
|
|
1792
|
-
const repository = resolveModelRepository(
|
|
1799
|
+
const statics = modelStatics(Model);
|
|
1800
|
+
const repository = resolveModelRepository(Model);
|
|
1793
1801
|
const primaryKey = repository.getTable().primaryKey;
|
|
1794
|
-
const record = await Model.query.call(
|
|
1802
|
+
const record = await Model.query.call(Model).where({ [primaryKey]: id }).first();
|
|
1795
1803
|
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1796
1804
|
}
|
|
1797
1805
|
static async findOrFail(id, errorFactory) {
|
|
1798
|
-
const model = await Model.find.call(
|
|
1806
|
+
const model = await Model.find.call(Model, id);
|
|
1799
1807
|
if (model) {
|
|
1800
1808
|
return model;
|
|
1801
1809
|
}
|
|
1802
|
-
throw errorFactory?.(id) ?? new NotFoundError(`${
|
|
1810
|
+
throw errorFactory?.(id) ?? new NotFoundError(`${Model.name} ${String(id)} not found.`);
|
|
1803
1811
|
}
|
|
1804
1812
|
static async all(options = {}) {
|
|
1805
1813
|
const statics = modelStatics(this);
|
|
1806
|
-
const repository = resolveModelRepository(
|
|
1807
|
-
let query = Model.query.call(
|
|
1814
|
+
const repository = resolveModelRepository(Model);
|
|
1815
|
+
let query = Model.query.call(Model);
|
|
1808
1816
|
if (options.orderBy) {
|
|
1809
1817
|
query = query.orderBy(options.orderBy);
|
|
1810
1818
|
}
|
|
@@ -1816,8 +1824,8 @@ class Model {
|
|
|
1816
1824
|
}
|
|
1817
1825
|
static async firstWhere(where, options = {}) {
|
|
1818
1826
|
const statics = modelStatics(this);
|
|
1819
|
-
const repository = resolveModelRepository(
|
|
1820
|
-
let query = Model.query.call(
|
|
1827
|
+
const repository = resolveModelRepository(Model);
|
|
1828
|
+
let query = Model.query.call(Model).where(where);
|
|
1821
1829
|
if (options.orderBy) {
|
|
1822
1830
|
query = query.orderBy(options.orderBy);
|
|
1823
1831
|
}
|
|
@@ -2564,15 +2572,6 @@ async function runSeedersFromDirectory(directory, db2, options) {
|
|
|
2564
2572
|
function defineTable(definition) {
|
|
2565
2573
|
return definition;
|
|
2566
2574
|
}
|
|
2567
|
-
// ../../src/core/database/connection.ts
|
|
2568
|
-
function createDatabaseConnection2(source) {
|
|
2569
|
-
return {
|
|
2570
|
-
async unsafe(query, params = []) {
|
|
2571
|
-
return await source.unsafe(query, params);
|
|
2572
|
-
}
|
|
2573
|
-
};
|
|
2574
|
-
}
|
|
2575
|
-
|
|
2576
2575
|
// ../../src/core/database/transaction.ts
|
|
2577
2576
|
async function runInTransaction(operation) {
|
|
2578
2577
|
return await connection_default.begin(async (transaction) => {
|
|
@@ -2927,8 +2926,35 @@ function createBodySizeLimitMiddleware(maxBytes = resolveMaxBodyBytes()) {
|
|
|
2927
2926
|
return await next();
|
|
2928
2927
|
};
|
|
2929
2928
|
}
|
|
2929
|
+
// ../../src/core/http/cookies.ts
|
|
2930
|
+
function readRequestCookie(request, name) {
|
|
2931
|
+
const cookies = request.cookies;
|
|
2932
|
+
if (cookies && typeof cookies.get === "function") {
|
|
2933
|
+
const value = cookies.get(name);
|
|
2934
|
+
if (value) {
|
|
2935
|
+
return value;
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
const header = request.headers.get("cookie");
|
|
2939
|
+
if (!header) {
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
for (const part of header.split(";")) {
|
|
2943
|
+
const idx = part.indexOf("=");
|
|
2944
|
+
if (idx === -1)
|
|
2945
|
+
continue;
|
|
2946
|
+
const cookieName = part.slice(0, idx).trim();
|
|
2947
|
+
if (cookieName !== name)
|
|
2948
|
+
continue;
|
|
2949
|
+
return decodeURIComponent(part.slice(idx + 1).trim());
|
|
2950
|
+
}
|
|
2951
|
+
return null;
|
|
2952
|
+
}
|
|
2953
|
+
function readBunRequestCookie(request, name) {
|
|
2954
|
+
return request.cookies.get(name) ?? readRequestCookie(request, name);
|
|
2955
|
+
}
|
|
2930
2956
|
// ../../src/core/http/csrfToken.ts
|
|
2931
|
-
import {
|
|
2957
|
+
import { timingSafeEqual } from "crypto";
|
|
2932
2958
|
|
|
2933
2959
|
// ../../src/core/http/requestMetaContext.ts
|
|
2934
2960
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
@@ -2949,70 +2975,28 @@ var CSRF_TTL_MS = 60 * 60 * 1000;
|
|
|
2949
2975
|
function resolveCsrfSecret() {
|
|
2950
2976
|
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || "workhub-dev-csrf-secret";
|
|
2951
2977
|
}
|
|
2952
|
-
function
|
|
2953
|
-
|
|
2954
|
-
const signature = createHmac("sha256", resolveCsrfSecret()).update(payload).digest("hex");
|
|
2955
|
-
return `${payload}.${signature}`;
|
|
2978
|
+
function csrfVerifyOptions() {
|
|
2979
|
+
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
2956
2980
|
}
|
|
2957
|
-
function
|
|
2958
|
-
const
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
for (const part of cookieHeader.split(";")) {
|
|
2963
|
-
const [name, ...rest] = part.trim().split("=");
|
|
2964
|
-
if (name === CSRF_COOKIE) {
|
|
2965
|
-
return decodeURIComponent(rest.join("="));
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
return null;
|
|
2969
|
-
}
|
|
2970
|
-
function parseSignedCsrfValue(cookieValue) {
|
|
2971
|
-
const parts = cookieValue.split(".");
|
|
2972
|
-
if (parts.length !== 3) {
|
|
2973
|
-
return null;
|
|
2974
|
-
}
|
|
2975
|
-
const [token, issuedAtRaw, cookieSignature] = parts;
|
|
2976
|
-
if (!token || !issuedAtRaw || !cookieSignature) {
|
|
2977
|
-
return null;
|
|
2978
|
-
}
|
|
2979
|
-
const issuedAt = Number.parseInt(issuedAtRaw, 10);
|
|
2980
|
-
if (!Number.isFinite(issuedAt)) {
|
|
2981
|
-
return null;
|
|
2982
|
-
}
|
|
2983
|
-
if (Date.now() - issuedAt > CSRF_TTL_MS) {
|
|
2984
|
-
return null;
|
|
2985
|
-
}
|
|
2986
|
-
const expectedSignature = signCsrfToken(token, issuedAt).split(".").pop();
|
|
2987
|
-
if (!expectedSignature) {
|
|
2988
|
-
return null;
|
|
2989
|
-
}
|
|
2990
|
-
const expectedBuffer = Buffer.from(expectedSignature);
|
|
2991
|
-
const actualBuffer = Buffer.from(cookieSignature);
|
|
2992
|
-
if (expectedBuffer.length !== actualBuffer.length) {
|
|
2993
|
-
return null;
|
|
2994
|
-
}
|
|
2995
|
-
if (!timingSafeEqual(expectedBuffer, actualBuffer)) {
|
|
2996
|
-
return null;
|
|
2981
|
+
function tokensMatch(left, right) {
|
|
2982
|
+
const leftBuffer = Buffer.from(left);
|
|
2983
|
+
const rightBuffer = Buffer.from(right);
|
|
2984
|
+
if (leftBuffer.length !== rightBuffer.length) {
|
|
2985
|
+
return false;
|
|
2997
2986
|
}
|
|
2998
|
-
return
|
|
2987
|
+
return timingSafeEqual(leftBuffer, rightBuffer);
|
|
2999
2988
|
}
|
|
3000
2989
|
function createCsrfTokenCookie() {
|
|
3001
|
-
const token =
|
|
3002
|
-
const issuedAt = Date.now();
|
|
3003
|
-
const value = signCsrfToken(token, issuedAt);
|
|
2990
|
+
const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
|
|
3004
2991
|
return {
|
|
3005
2992
|
token,
|
|
3006
|
-
cookie: `${CSRF_COOKIE}=${encodeURIComponent(
|
|
2993
|
+
cookie: `${CSRF_COOKIE}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}`
|
|
3007
2994
|
};
|
|
3008
2995
|
}
|
|
3009
2996
|
function resolveCsrfToken(request) {
|
|
3010
|
-
const cookieValue =
|
|
3011
|
-
if (cookieValue) {
|
|
3012
|
-
|
|
3013
|
-
if (parsed) {
|
|
3014
|
-
return { token: parsed.token };
|
|
3015
|
-
}
|
|
2997
|
+
const cookieValue = readRequestCookie(request, CSRF_COOKIE);
|
|
2998
|
+
if (cookieValue && Bun.CSRF.verify(cookieValue, csrfVerifyOptions())) {
|
|
2999
|
+
return { token: cookieValue };
|
|
3016
3000
|
}
|
|
3017
3001
|
return createCsrfTokenCookie();
|
|
3018
3002
|
}
|
|
@@ -3035,6 +3019,10 @@ async function readSubmittedCsrfTokenFromBody(request) {
|
|
|
3035
3019
|
if (typeof field === "string" && field.trim().length > 0) {
|
|
3036
3020
|
return field.trim();
|
|
3037
3021
|
}
|
|
3022
|
+
const legacyField = formData.get("_csrf");
|
|
3023
|
+
if (typeof legacyField === "string" && legacyField.trim().length > 0) {
|
|
3024
|
+
return legacyField.trim();
|
|
3025
|
+
}
|
|
3038
3026
|
}
|
|
3039
3027
|
return null;
|
|
3040
3028
|
}
|
|
@@ -3042,20 +3030,14 @@ function verifyCsrfToken(request, submittedToken) {
|
|
|
3042
3030
|
if (!submittedToken) {
|
|
3043
3031
|
return false;
|
|
3044
3032
|
}
|
|
3045
|
-
const cookieValue =
|
|
3033
|
+
const cookieValue = readRequestCookie(request, CSRF_COOKIE);
|
|
3046
3034
|
if (!cookieValue) {
|
|
3047
3035
|
return false;
|
|
3048
3036
|
}
|
|
3049
|
-
|
|
3050
|
-
if (!parsed) {
|
|
3037
|
+
if (!tokensMatch(submittedToken, cookieValue)) {
|
|
3051
3038
|
return false;
|
|
3052
3039
|
}
|
|
3053
|
-
|
|
3054
|
-
const expectedBuffer = Buffer.from(parsed.token);
|
|
3055
|
-
if (submittedBuffer.length !== expectedBuffer.length) {
|
|
3056
|
-
return false;
|
|
3057
|
-
}
|
|
3058
|
-
return timingSafeEqual(submittedBuffer, expectedBuffer);
|
|
3040
|
+
return Bun.CSRF.verify(submittedToken, csrfVerifyOptions());
|
|
3059
3041
|
}
|
|
3060
3042
|
function resolveCsrfTokenForRequest(request) {
|
|
3061
3043
|
const metaToken = currentRequestMeta().csrfToken;
|
|
@@ -3096,8 +3078,30 @@ function createCsrfMiddleware() {
|
|
|
3096
3078
|
return await next();
|
|
3097
3079
|
};
|
|
3098
3080
|
}
|
|
3081
|
+
// ../../src/core/http/csrfProtection.ts
|
|
3082
|
+
var DEFAULT_CSRF_TTL_MS = 60 * 60 * 1000;
|
|
3083
|
+
function createCsrfProtection(secret, options = {}) {
|
|
3084
|
+
const expiresIn = options.expiresIn ?? DEFAULT_CSRF_TTL_MS;
|
|
3085
|
+
const maxAge = options.maxAge ?? expiresIn;
|
|
3086
|
+
return {
|
|
3087
|
+
generate(_sessionKey) {
|
|
3088
|
+
return Bun.CSRF.generate(secret, { expiresIn });
|
|
3089
|
+
},
|
|
3090
|
+
verify(token, _sessionKey) {
|
|
3091
|
+
if (!token) {
|
|
3092
|
+
return false;
|
|
3093
|
+
}
|
|
3094
|
+
return Bun.CSRF.verify(token, { secret, maxAge });
|
|
3095
|
+
},
|
|
3096
|
+
secret
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
// ../../src/core/crypto/nonCryptographicHash.ts
|
|
3100
|
+
function nonCryptographicDigest(input) {
|
|
3101
|
+
return Bun.hash(input).toString(16);
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3099
3104
|
// ../../src/core/http/etag.ts
|
|
3100
|
-
import { createHash } from "crypto";
|
|
3101
3105
|
function isEtagEnabled() {
|
|
3102
3106
|
return (process.env.FEATURE_ETAG ?? "true") !== "false";
|
|
3103
3107
|
}
|
|
@@ -3105,13 +3109,13 @@ function formatWeakEtag(digest) {
|
|
|
3105
3109
|
return `W/"${digest}"`;
|
|
3106
3110
|
}
|
|
3107
3111
|
function computeEtagFromJson(data) {
|
|
3108
|
-
const digest =
|
|
3112
|
+
const digest = nonCryptographicDigest(JSON.stringify(data)).slice(0, 32);
|
|
3109
3113
|
return formatWeakEtag(digest);
|
|
3110
3114
|
}
|
|
3111
3115
|
function etagFromResource(resource) {
|
|
3112
3116
|
const version = resource.updated_at ?? resource.created_at ?? "";
|
|
3113
3117
|
const versionText = version instanceof Date ? version.toISOString() : version ? String(version) : "0";
|
|
3114
|
-
const digest =
|
|
3118
|
+
const digest = nonCryptographicDigest(`${String(resource.id ?? "0")}:${versionText}`).slice(0, 32);
|
|
3115
3119
|
return formatWeakEtag(digest);
|
|
3116
3120
|
}
|
|
3117
3121
|
function normalizeEtag(value) {
|
|
@@ -3598,7 +3602,7 @@ async function verifyPassword(password, passwordHash) {
|
|
|
3598
3602
|
}
|
|
3599
3603
|
|
|
3600
3604
|
// ../../src/core/crypto/fieldEncryption.ts
|
|
3601
|
-
import { createCipheriv, createDecipheriv, createHmac
|
|
3605
|
+
import { createCipheriv, createDecipheriv, createHmac, randomBytes } from "crypto";
|
|
3602
3606
|
var ENCRYPTION_PREFIX = "enc:v1:";
|
|
3603
3607
|
var IV_LENGTH = 12;
|
|
3604
3608
|
var TAG_LENGTH = 16;
|
|
@@ -3627,7 +3631,7 @@ function isFieldEncryptionEnabled() {
|
|
|
3627
3631
|
return (process.env.APP_ENV ?? "local") === "production";
|
|
3628
3632
|
}
|
|
3629
3633
|
function encryptField(plaintext, key) {
|
|
3630
|
-
const iv =
|
|
3634
|
+
const iv = randomBytes(IV_LENGTH);
|
|
3631
3635
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
3632
3636
|
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
3633
3637
|
const tag = cipher.getAuthTag();
|
|
@@ -3647,7 +3651,7 @@ function decryptField(value, key) {
|
|
|
3647
3651
|
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
3648
3652
|
}
|
|
3649
3653
|
function hashLookupValue(normalizedValue, key) {
|
|
3650
|
-
return
|
|
3654
|
+
return createHmac("sha256", key).update(normalizedValue).digest("hex");
|
|
3651
3655
|
}
|
|
3652
3656
|
function normalizeEmail(email) {
|
|
3653
3657
|
return email.trim().toLowerCase();
|
|
@@ -3727,7 +3731,7 @@ function resolveDefaultTokenExpiryDays() {
|
|
|
3727
3731
|
}
|
|
3728
3732
|
|
|
3729
3733
|
// ../../src/core/security/totp.ts
|
|
3730
|
-
import { createHmac as
|
|
3734
|
+
import { createHmac as createHmac2 } from "crypto";
|
|
3731
3735
|
function decodeBase32(input) {
|
|
3732
3736
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
3733
3737
|
const normalized = input.replace(/=+$/u, "").toUpperCase();
|
|
@@ -3749,7 +3753,7 @@ function generateTotp(secret, counter, digits = 6) {
|
|
|
3749
3753
|
const key = decodeBase32(secret);
|
|
3750
3754
|
const buffer = Buffer.alloc(8);
|
|
3751
3755
|
buffer.writeBigUInt64BE(BigInt(counter));
|
|
3752
|
-
const digest =
|
|
3756
|
+
const digest = createHmac2("sha1", key).update(buffer).digest();
|
|
3753
3757
|
const lastByte = digest[digest.length - 1] ?? 0;
|
|
3754
3758
|
const offset = lastByte & 15;
|
|
3755
3759
|
const b0 = digest[offset] ?? 0;
|
|
@@ -3941,30 +3945,30 @@ var userTable = defineTable({
|
|
|
3941
3945
|
});
|
|
3942
3946
|
|
|
3943
3947
|
// ../../src/core/auth/tokenHash.ts
|
|
3944
|
-
import { createHash
|
|
3948
|
+
import { createHash, createHmac as createHmac3 } from "crypto";
|
|
3945
3949
|
function resolveTokenPepper() {
|
|
3946
3950
|
return process.env.TOKEN_HASH_PEPPER?.trim() ?? "workhub-dev-token-pepper";
|
|
3947
3951
|
}
|
|
3948
3952
|
function hashApiToken(token) {
|
|
3949
3953
|
const pepper = resolveTokenPepper();
|
|
3950
3954
|
if (pepper && pepper !== "workhub-dev-token-pepper") {
|
|
3951
|
-
return
|
|
3955
|
+
return createHmac3("sha256", pepper).update(token).digest("hex");
|
|
3952
3956
|
}
|
|
3953
|
-
return
|
|
3957
|
+
return createHash("sha256").update(token).digest("hex");
|
|
3954
3958
|
}
|
|
3955
3959
|
|
|
3956
3960
|
// ../../src/modules/user/provider.ts
|
|
3957
3961
|
var tokenServiceToken = CORE_TOKEN_SERVICE_TOKEN;
|
|
3958
3962
|
|
|
3959
3963
|
// ../../src/core/http/flashSession.ts
|
|
3960
|
-
import { createHmac as
|
|
3964
|
+
import { createHmac as createHmac4, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
3961
3965
|
var FLASH_COOKIE = "workhub_flash";
|
|
3962
3966
|
var FLASH_TTL_MS = 60 * 1000;
|
|
3963
3967
|
function resolveFlashSecret() {
|
|
3964
3968
|
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || "workhub-dev-flash-secret";
|
|
3965
3969
|
}
|
|
3966
3970
|
function signFlashPayload(payload, issuedAt) {
|
|
3967
|
-
const signature =
|
|
3971
|
+
const signature = createHmac4("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|
|
3968
3972
|
return `${payload}.${issuedAt}.${signature}`;
|
|
3969
3973
|
}
|
|
3970
3974
|
function readFlashCookie(request) {
|
|
@@ -5362,6 +5366,7 @@ export {
|
|
|
5362
5366
|
withMigrationLock,
|
|
5363
5367
|
withMiddleware,
|
|
5364
5368
|
withErrorHandling,
|
|
5369
|
+
verifyCsrfToken,
|
|
5365
5370
|
validateObject,
|
|
5366
5371
|
toResourceCollection,
|
|
5367
5372
|
toPaginatedResourceCollection,
|
|
@@ -5381,6 +5386,8 @@ export {
|
|
|
5381
5386
|
resolveWebLayoutData,
|
|
5382
5387
|
resolveService,
|
|
5383
5388
|
resolveDatabaseDriver,
|
|
5389
|
+
resolveCsrfTokenForRequest,
|
|
5390
|
+
resolveCsrfToken,
|
|
5384
5391
|
resolveApplicationQueue,
|
|
5385
5392
|
resolveApplicationPolicyGate,
|
|
5386
5393
|
resolveApplicationLogger,
|
|
@@ -5391,6 +5398,10 @@ export {
|
|
|
5391
5398
|
required,
|
|
5392
5399
|
registerShutdownHandler,
|
|
5393
5400
|
registerModelRepository,
|
|
5401
|
+
readSubmittedCsrfTokenFromBody,
|
|
5402
|
+
readSubmittedCsrfToken,
|
|
5403
|
+
readRequestCookie,
|
|
5404
|
+
readBunRequestCookie,
|
|
5394
5405
|
queue,
|
|
5395
5406
|
prometheusRegistry,
|
|
5396
5407
|
policyGate,
|
|
@@ -5445,6 +5456,9 @@ export {
|
|
|
5445
5456
|
createMemoryThrottleMiddleware,
|
|
5446
5457
|
createLoginThrottleMiddleware,
|
|
5447
5458
|
createFailedJobService,
|
|
5459
|
+
createDatabaseConnection2 as createDatabaseConnection,
|
|
5460
|
+
createCsrfTokenCookie,
|
|
5461
|
+
createCsrfProtection,
|
|
5448
5462
|
createCsrfMiddleware,
|
|
5449
5463
|
createBodySizeLimitMiddleware,
|
|
5450
5464
|
createAuthorizeMiddleware,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getstrata/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Strata — Laravel-inspired Bun framework public API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -317,6 +317,9 @@
|
|
|
317
317
|
"publishConfig": {
|
|
318
318
|
"access": "public"
|
|
319
319
|
},
|
|
320
|
+
"dependencies": {
|
|
321
|
+
"eta": "^4.6.0"
|
|
322
|
+
},
|
|
320
323
|
"peerDependencies": {
|
|
321
324
|
"typescript": "^5.9.0"
|
|
322
325
|
}
|