security: auth redesign, SSRF protection, CORS lockdown, and 13 other fixes
- Auth (#2/#3): UUID PK, 256-bit keys, SHA-256 lookup + bcrypt hash - SSRF (#1): validate URLs, block private IPs, cloud metadata endpoints - CORS (#4): lock to pingql.com origins, not wildcard - SSE limit (#6): 10 connections per monitor max - ReDoS (#7): cap $regex patterns at 200 chars - Monitor limit (#8): 100 per account default - Cookie env config (#9): secure/domain from env vars - Bearer parsing (#10): case-insensitive RFC 6750 - Pings retention (#11): 90-day pruner, hourly interval - monitors.enabled index (#12): partial index for /internal/due - Runner locking (#14): locked_until for horizontal scale safety - COALESCE nullable bug (#17): dynamic PATCH with explicit undefined checks - MONITOR_TOKEN null guard (#18): startup validation + middleware hardening - reset-key cookie fix (#16): sets new cookie in response
This commit is contained in:
@@ -230,6 +230,7 @@ function evalOp(op: string, fieldVal: unknown, opVal: unknown): boolean {
|
||||
return typeof fieldVal === "string" && typeof opVal === "string" && fieldVal.endsWith(opVal);
|
||||
case "$regex": {
|
||||
if (typeof fieldVal !== "string" || typeof opVal !== "string") return false;
|
||||
if (opVal.length > 200) return false;
|
||||
try {
|
||||
return new RegExp(opVal).test(fieldVal);
|
||||
} catch {
|
||||
@@ -311,11 +312,15 @@ export function validateQuery(query: unknown, path = ""): ValidationError[] {
|
||||
errors.push({ path: keyPath, message: `Unknown field: ${key}. Use status, body, or headers.*` });
|
||||
}
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
for (const op of Object.keys(value as Record<string, unknown>)) {
|
||||
const ops = value as Record<string, unknown>;
|
||||
for (const op of Object.keys(ops)) {
|
||||
if (!op.startsWith("$")) continue;
|
||||
if (!VALID_OPS.has(op)) {
|
||||
errors.push({ path: `${keyPath}.${op}`, message: `Unknown operator: ${op}` });
|
||||
}
|
||||
if (op === "$regex" && typeof ops[op] === "string" && (ops[op] as string).length > 200) {
|
||||
errors.push({ path: `${keyPath}.${op}`, message: "Regex pattern too long (max 200 characters)" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user