mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-30 21:01:54 +08:00
Better error handling
This commit is contained in:
parent
2474b600de
commit
014437f478
@ -1,6 +1,21 @@
|
||||
import { StatusError } from "@hexclave/shared/dist/utils/errors";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { assertSafeOAuthResolvedAddress, assertSafeOAuthUrlWithoutDns, isBlockedOAuthIpAddress } from "./ssrf-protection";
|
||||
import dns from "node:dns";
|
||||
import { assertSafeOAuthResolvedAddress, assertSafeOAuthUrlWithoutDns, isBlockedOAuthIpAddress, safeOAuthDnsLookup } from "./ssrf-protection";
|
||||
|
||||
async function withProductionNodeEnv<T>(callback: () => Promise<T>): Promise<T> {
|
||||
const previousNodeEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = "production";
|
||||
try {
|
||||
return await callback();
|
||||
} finally {
|
||||
if (previousNodeEnv === undefined) {
|
||||
delete process.env.NODE_ENV;
|
||||
} else {
|
||||
process.env.NODE_ENV = previousNodeEnv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("isBlockedOAuthIpAddress", () => {
|
||||
it("blocks AWS metadata, loopback, and private IPv4 ranges", () => {
|
||||
@ -50,3 +65,25 @@ describe("assertSafeOAuthResolvedAddress", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("safeOAuthDnsLookup", () => {
|
||||
it("reports blocked single-address lookup results through the callback", async () => {
|
||||
const error = await withProductionNodeEnv(async () => await new Promise<NodeJS.ErrnoException | null>((resolve) => {
|
||||
safeOAuthDnsLookup("127.0.0.1", {}, (lookupError) => {
|
||||
resolve(lookupError);
|
||||
});
|
||||
}));
|
||||
|
||||
expect(error).toBeInstanceOf(StatusError);
|
||||
});
|
||||
|
||||
it("reports blocked all-address lookup results through the callback", async () => {
|
||||
const error = await withProductionNodeEnv(async () => await new Promise<NodeJS.ErrnoException | null>((resolve) => {
|
||||
safeOAuthDnsLookup("127.0.0.1", { all: true, verbatim: true } satisfies dns.LookupAllOptions, (lookupError) => {
|
||||
resolve(lookupError);
|
||||
});
|
||||
}));
|
||||
|
||||
expect(error).toBeInstanceOf(StatusError);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -119,6 +119,18 @@ type DnsLookupCallback = (
|
||||
family?: number,
|
||||
) => void;
|
||||
|
||||
function getLookupValidationError(validate: () => void): NodeJS.ErrnoException | null {
|
||||
try {
|
||||
validate();
|
||||
return null;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return error;
|
||||
}
|
||||
return new Error("OAuth DNS lookup failed while validating resolved address.");
|
||||
}
|
||||
}
|
||||
|
||||
export function safeOAuthDnsLookup(hostname: string, options: dns.LookupOptions, callback: DnsLookupCallback): void {
|
||||
if (!shouldEnforceOAuthSsrfProtection()) {
|
||||
dns.lookup(hostname, options, callback);
|
||||
@ -133,8 +145,14 @@ export function safeOAuthDnsLookup(hostname: string, options: dns.LookupOptions,
|
||||
return;
|
||||
}
|
||||
|
||||
for (const address of addresses) {
|
||||
assertSafeOAuthResolvedAddress(address.address);
|
||||
const validationError = getLookupValidationError(() => {
|
||||
for (const address of addresses) {
|
||||
assertSafeOAuthResolvedAddress(address.address);
|
||||
}
|
||||
});
|
||||
if (validationError !== null) {
|
||||
callback(validationError, []);
|
||||
return;
|
||||
}
|
||||
callback(null, addresses);
|
||||
});
|
||||
@ -148,7 +166,11 @@ export function safeOAuthDnsLookup(hostname: string, options: dns.LookupOptions,
|
||||
return;
|
||||
}
|
||||
|
||||
assertSafeOAuthResolvedAddress(address);
|
||||
const validationError = getLookupValidationError(() => assertSafeOAuthResolvedAddress(address));
|
||||
if (validationError !== null) {
|
||||
callback(validationError, "", 0);
|
||||
return;
|
||||
}
|
||||
callback(null, address, family);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user