import { env } from "../env.js";
import { validateUrlResolvesToPublicIp } from "../domains/run/services/blob-storage/file-url-security.js";

//#region src/utils/webhook-url-security.ts
var WebhookUrlSecurityError = class extends Error {
	constructor(message) {
		super(message);
		this.name = "WebhookUrlSecurityError";
	}
};
const MAX_WEBHOOK_REDIRECTS = 5;
function validateWebhookUrl(rawUrl) {
	let parsed;
	try {
		parsed = new URL(rawUrl);
	} catch {
		throw new WebhookUrlSecurityError("Invalid URL");
	}
	const protocol = parsed.protocol.toLowerCase();
	if (protocol !== "https:" && protocol !== "http:") throw new WebhookUrlSecurityError(`Unsupported protocol: ${protocol}`);
	if (parsed.username || parsed.password) throw new WebhookUrlSecurityError("URLs with embedded credentials are not allowed");
	return parsed;
}
async function validateAgainstSsrfPolicy(url) {
	if (env.ENVIRONMENT !== "development") await validateUrlResolvesToPublicIp(url);
}
function isRedirectStatus(status) {
	return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
}
async function fetchWithSsrfProtection(url, init) {
	let currentUrl = validateWebhookUrl(url);
	await validateAgainstSsrfPolicy(currentUrl);
	for (let hopCount = 0; hopCount <= MAX_WEBHOOK_REDIRECTS; hopCount++) {
		const response = await fetch(currentUrl.toString(), {
			...init,
			redirect: "manual"
		});
		if (!isRedirectStatus(response.status)) return response;
		if (hopCount === MAX_WEBHOOK_REDIRECTS) throw new WebhookUrlSecurityError(`Too many redirects (exceeded ${MAX_WEBHOOK_REDIRECTS})`);
		const location = response.headers.get("location");
		if (!location) throw new WebhookUrlSecurityError("Redirect response missing Location header");
		currentUrl = validateWebhookUrl(new URL(location, currentUrl).toString());
		await validateAgainstSsrfPolicy(currentUrl);
	}
	throw new WebhookUrlSecurityError("Unexpected redirect handling state");
}

//#endregion
export { WebhookUrlSecurityError, fetchWithSsrfProtection, validateWebhookUrl };