@luckystack/server 0.2.5 → 0.2.7
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/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/createServer.ts
|
|
9
9
|
import http from "http";
|
|
10
|
-
import { registerBindAddress, writeBootUuid, getLogger as getLogger13, getProjectConfig as getProjectConfig13, tryCatch as
|
|
10
|
+
import { registerBindAddress, writeBootUuid, getLogger as getLogger13, getProjectConfig as getProjectConfig13, tryCatch as tryCatch10, isProduction as isProduction2 } from "@luckystack/core";
|
|
11
11
|
|
|
12
12
|
// src/httpHandler.ts
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
getProjectConfig as getProjectConfig10,
|
|
21
21
|
hasCookie,
|
|
22
22
|
readSession as readSession3,
|
|
23
|
+
tryCatch as tryCatch7,
|
|
23
24
|
tryCatchSync
|
|
24
25
|
} from "@luckystack/core";
|
|
25
26
|
|
|
@@ -538,6 +539,7 @@ var handleAuthApiRoute = async ({
|
|
|
538
539
|
|
|
539
540
|
// src/httpRoutes/authLogoutRoute.ts
|
|
540
541
|
import { getLogger as getLogger2, getProjectConfig as getProjectConfig6, tryCatch as tryCatch3 } from "@luckystack/core";
|
|
542
|
+
var warnedNonStrictSameSite = false;
|
|
541
543
|
var handleAuthLogoutRoute = async ({ res, routePath, method, token }) => {
|
|
542
544
|
if (routePath !== "/auth/logout") return false;
|
|
543
545
|
if (method !== "POST") {
|
|
@@ -557,7 +559,13 @@ var handleAuthLogoutRoute = async ({ res, routePath, method, token }) => {
|
|
|
557
559
|
}
|
|
558
560
|
}
|
|
559
561
|
const http2 = getProjectConfig6().http;
|
|
560
|
-
|
|
562
|
+
if (http2.sessionCookieSameSite !== "Strict" && !warnedNonStrictSameSite) {
|
|
563
|
+
warnedNonStrictSameSite = true;
|
|
564
|
+
getLogger2().warn(
|
|
565
|
+
`/auth/logout is exempt from CSRF middleware and relies on SameSite=Strict. Current sessionCookieSameSite="${http2.sessionCookieSameSite}" weakens this protection.`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
const secure = resolveCookieSecure(http2.sessionCookieSecure, process.env.SECURE);
|
|
561
569
|
res.setHeader(
|
|
562
570
|
"Set-Cookie",
|
|
563
571
|
`${http2.sessionCookieName}=; HttpOnly; SameSite=${http2.sessionCookieSameSite}; Path=${http2.sessionCookiePath}; Max-Age=0; ${secure ? "Secure;" : ""}`
|
|
@@ -1166,7 +1174,7 @@ var parseRequestParams = async ({
|
|
|
1166
1174
|
}
|
|
1167
1175
|
return params;
|
|
1168
1176
|
};
|
|
1169
|
-
var
|
|
1177
|
+
var handleHttpRequestInner = async (req, res, options) => {
|
|
1170
1178
|
const config = getProjectConfig10();
|
|
1171
1179
|
const shouldLogDev = config.logging.devLogs;
|
|
1172
1180
|
const sessionCookieName = config.http.sessionCookieName;
|
|
@@ -1180,13 +1188,21 @@ var handleHttpRequest = async (req, res, options) => {
|
|
|
1180
1188
|
);
|
|
1181
1189
|
const url = req.url ?? "/";
|
|
1182
1190
|
const [routePathRaw, queryStringRaw] = url.split("?");
|
|
1183
|
-
const
|
|
1191
|
+
const [decodeError, decodedPath] = tryCatchSync(() => decodeURIComponent(routePathRaw ?? "/"));
|
|
1192
|
+
if (decodeError || decodedPath === null) {
|
|
1193
|
+
res.statusCode = 400;
|
|
1194
|
+
res.setHeader("Content-Type", "text/plain");
|
|
1195
|
+
res.end("Bad Request");
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const routePath = decodedPath;
|
|
1184
1199
|
const queryString = queryStringRaw ?? "";
|
|
1185
1200
|
const { origin, rejected } = enforceOriginPolicy(req, res, routePath);
|
|
1186
1201
|
if (rejected) return;
|
|
1187
1202
|
setSecurityHeaders(req, res, origin);
|
|
1188
1203
|
const incomingRequestId = req.headers["x-request-id"];
|
|
1189
|
-
const
|
|
1204
|
+
const rawRequestId = Array.isArray(incomingRequestId) ? incomingRequestId[0] : incomingRequestId;
|
|
1205
|
+
const requestId = rawRequestId && /^[a-zA-Z0-9-]{1,128}$/.test(rawRequestId) ? rawRequestId : randomUUID();
|
|
1190
1206
|
res.setHeader("X-Request-Id", requestId);
|
|
1191
1207
|
const safeHeaders = {};
|
|
1192
1208
|
for (const [k, v] of Object.entries(req.headers)) {
|
|
@@ -1246,6 +1262,15 @@ var handleHttpRequest = async (req, res, options) => {
|
|
|
1246
1262
|
if (params === null) return;
|
|
1247
1263
|
await dispatchRoutes(POST_PARAMS_ROUTES, { ...baseCtx, params });
|
|
1248
1264
|
};
|
|
1265
|
+
var handleHttpRequest = async (req, res, options) => {
|
|
1266
|
+
const [error] = await tryCatch7(() => handleHttpRequestInner(req, res, options));
|
|
1267
|
+
if (error && !res.writableEnded) {
|
|
1268
|
+
getLogger7().error("handleHttpRequest: unhandled error", { err: error });
|
|
1269
|
+
res.statusCode = 500;
|
|
1270
|
+
res.setHeader("Content-Type", "application/json");
|
|
1271
|
+
res.end(JSON.stringify({ status: "error", errorCode: "server.internalError" }));
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1249
1274
|
|
|
1250
1275
|
// src/loadSocket.ts
|
|
1251
1276
|
import { Server as SocketIOServer } from "socket.io";
|
|
@@ -1271,7 +1296,7 @@ import {
|
|
|
1271
1296
|
normalizeErrorResponse,
|
|
1272
1297
|
readSession as readSession4,
|
|
1273
1298
|
writeSession,
|
|
1274
|
-
tryCatch as
|
|
1299
|
+
tryCatch as tryCatch8
|
|
1275
1300
|
} from "@luckystack/core";
|
|
1276
1301
|
import { handleApiRequest } from "@luckystack/api";
|
|
1277
1302
|
var sessionLocks = /* @__PURE__ */ new Map();
|
|
@@ -1279,7 +1304,7 @@ var withSessionLock = async (token, fn) => {
|
|
|
1279
1304
|
const prev = sessionLocks.get(token) ?? Promise.resolve();
|
|
1280
1305
|
const next = prev.then(fn, fn);
|
|
1281
1306
|
sessionLocks.set(token, next);
|
|
1282
|
-
await
|
|
1307
|
+
await tryCatch8(() => next);
|
|
1283
1308
|
if (sessionLocks.get(token) === next) sessionLocks.delete(token);
|
|
1284
1309
|
};
|
|
1285
1310
|
var getVisibleSocketRooms = (socket, token) => {
|
|
@@ -1304,7 +1329,7 @@ var validateRoomRequest = (socket, responseIndex, token, group, preferredLocale,
|
|
|
1304
1329
|
}));
|
|
1305
1330
|
return false;
|
|
1306
1331
|
}
|
|
1307
|
-
if (!group) {
|
|
1332
|
+
if (!group || group.length > 256) {
|
|
1308
1333
|
socket.emit(buildResponseEventName(responseIndex), normalizeErrorResponse({
|
|
1309
1334
|
response: { status: "error", errorCode: "room.invalid" },
|
|
1310
1335
|
preferredLocale
|
|
@@ -1490,6 +1515,15 @@ var registerUpdateLocationEvent = (ctx) => {
|
|
|
1490
1515
|
(newLocation) => {
|
|
1491
1516
|
if (!token) return;
|
|
1492
1517
|
if (!locationProviderEnabled) return;
|
|
1518
|
+
if (typeof newLocation.pathName !== "string" || !newLocation.pathName.startsWith("/") || newLocation.pathName.length > 2048 || newLocation.pathName.includes("\0")) return;
|
|
1519
|
+
if (newLocation.searchParams !== void 0) {
|
|
1520
|
+
if (typeof newLocation.searchParams !== "object" || Array.isArray(newLocation.searchParams)) return;
|
|
1521
|
+
const entries = Object.entries(newLocation.searchParams);
|
|
1522
|
+
if (entries.length > 50) return;
|
|
1523
|
+
for (const [k, v] of entries) {
|
|
1524
|
+
if (typeof k !== "string" || k.length > 256 || typeof v !== "string" || v.length > 1024) return;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1493
1527
|
if (shouldLogDev) {
|
|
1494
1528
|
getLogger8().debug("updating location", { pathName: newLocation.pathName });
|
|
1495
1529
|
}
|
|
@@ -1543,7 +1577,7 @@ var rejoinPersistedRooms = (ctx) => {
|
|
|
1543
1577
|
const { socket, token, shouldLogDev } = ctx;
|
|
1544
1578
|
if (!token) return;
|
|
1545
1579
|
void (async () => {
|
|
1546
|
-
const [rejoinError, codes] = await
|
|
1580
|
+
const [rejoinError, codes] = await tryCatch8(async () => {
|
|
1547
1581
|
await socket.join(token);
|
|
1548
1582
|
const session = await readSession4(token);
|
|
1549
1583
|
const roomCodes = session ? getSessionRoomCodes(session) : [];
|
|
@@ -1853,7 +1887,7 @@ import {
|
|
|
1853
1887
|
dispatchHook as dispatchHook7,
|
|
1854
1888
|
flushErrorTrackers,
|
|
1855
1889
|
getLogger as getLogger11,
|
|
1856
|
-
tryCatch as
|
|
1890
|
+
tryCatch as tryCatch9
|
|
1857
1891
|
} from "@luckystack/core";
|
|
1858
1892
|
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
1859
1893
|
var withTimeout = async (label, timeoutMs, fn) => {
|
|
@@ -1866,7 +1900,7 @@ var withTimeout = async (label, timeoutMs, fn) => {
|
|
|
1866
1900
|
timer.unref();
|
|
1867
1901
|
});
|
|
1868
1902
|
const run = (async () => {
|
|
1869
|
-
const [error] = await
|
|
1903
|
+
const [error] = await tryCatch9(fn);
|
|
1870
1904
|
if (error) getLogger11().warn(`[shutdown] step "${label}" failed: ${error.message}`);
|
|
1871
1905
|
return true;
|
|
1872
1906
|
})();
|
|
@@ -2007,7 +2041,7 @@ var createLuckyStackServer = async (options = {}) => {
|
|
|
2007
2041
|
if (enableDevTools) {
|
|
2008
2042
|
await initDevTools();
|
|
2009
2043
|
}
|
|
2010
|
-
const [bootUuidError] = await
|
|
2044
|
+
const [bootUuidError] = await tryCatch10(() => writeBootUuid());
|
|
2011
2045
|
if (bootUuidError) {
|
|
2012
2046
|
throw new Error(
|
|
2013
2047
|
"Failed to write the boot UUID to Redis. Check REDIS_HOST / REDIS_PORT / REDIS_USER / REDIS_PASSWORD and that Redis is reachable.",
|