// packages/backend/src/controller/PKCECheck.ts
import { RequestSpec } from "caido:utils";
var PKCECheck = class {
  async test(sdk, req) {
    const method = req.getMethod();
    if (method !== "GET") {
      sdk.console.log("[PKCEDowngradeCheck] Not a GET request. Skipping.");
      return false;
    }
    const query = req.getQuery();
    const searchParams = new URLSearchParams(query);
    const requiredKeys = ["client_id", "response_type", "code_challenge", "code_challenge_method"];
    if (!requiredKeys.every((key) => searchParams.has(key))) {
      sdk.console.log("[PKCEDowngradeCheck] Required PKCE parameters missing. Skipping.");
      return false;
    }
    const url = req.getUrl();
    const isOpenID = searchParams.get("scope")?.includes("openid") || url.includes("id_token");
    const methodVal = searchParams.get("code_challenge_method");
    const challengeVal = searchParams.get("code_challenge");
    if (!methodVal || !challengeVal) {
      sdk.console.log("[PKCEDowngradeCheck] code_challenge or method missing. Skipping.");
      await sdk.findings.create({
        title: isOpenID ? "[WARN] OpenID Flow PKCE Parameters Missing" : "[WARN] OAuth2 Flow PKCE Parameters Missing",
        description: `PKCE parameters are missing or incomplete for ${url}. This may indicate a misconfiguration.`,
        request: req,
        reporter: "PKCE Checker"
      });
      return false;
    }
    if (methodVal === "plain") {
      sdk.console.log("[PKCEDowngradeCheck] code_challenge_method is 'plain'. Skipping.");
      await sdk.findings.create({
        title: isOpenID ? "[WARN] OpenID Flow PKCE Method is 'plain'" : "[WARN] OAuth2 Flow PKCE Method is 'plain'",
        description: `PKCE method is set to 'plain' for ${url}. This may indicate a downgrade vulnerability.`,
        request: req,
        reporter: "PKCE Checker"
      });
      return false;
    }
    searchParams.delete("code_challenge");
    searchParams.delete("code_challenge_method");
    const downgradedQuery = searchParams.toString();
    const scheme = req.getUrl().startsWith("https") ? "https" : "http";
    const downgradedUrl = `${scheme}://${req.getHost()}:${req.getPort()}${req.getPath()}?${downgradedQuery}`;
    sdk.console.log(`${req.getHost()} Original URL: ` + url);
    sdk.console.log(`${req.getHost()} Downgraded URL: ` + downgradedUrl);
    try {
      const spec = new RequestSpec(downgradedUrl);
      spec.setBody(req.getBody());
      for (const [key, value] of Object.entries(req.getHeaders())) {
        if (Array.isArray(value)) {
          spec.setHeader(key, value.join(", "));
        } else {
          spec.setHeader(key, value);
        }
      }
      spec.setHost(req.getHost());
      spec.setMethod(req.getMethod());
      spec.setPath(req.getPath());
      spec.setQuery(downgradedQuery);
      spec.setTls(req.getTls());
      spec.setPort(req.getPort());
      let sendDowngradedRequest = await sdk.requests.send(spec);
      if (sendDowngradedRequest.response) {
        let domain = spec.getHost();
        let port = spec.getPort();
        let path2 = spec.getPath();
        let query2 = spec.getQuery();
        let id = sendDowngradedRequest.response.getId();
        let code = sendDowngradedRequest.response.getCode();
        sdk.console.log(`REQ ${id}: ${domain}:${port}${path2}${query2} received a status code of ${code}`);
      }
      if (sendDowngradedRequest.response?.getCode() === 302) {
        await sdk.findings.create({
          title: isOpenID ? "[CRITICAL] OpenID Flow PKCE Downgrade Vulnerability" : "[CRITICAL] OAuth2 Flow PKCE Downgrade Vulnerability",
          description: `The request to ${url} is vulnerable to a PKCE downgrade attack. This may indicate a configuration error.`,
          request: req,
          reporter: "PKCE Checker"
        });
      }
    } catch (err) {
      sdk.console.error(`PKCE downgrade check failed for ${url}: ${String(err)}`);
    }
    sdk.console.log("[PKCEDowngradeCheck] No PKCE downgrade detected.");
    return false;
  }
};

// packages/backend/src/controller/redirectUriCheck.ts
import { promises as fs } from "fs";
import * as path from "path";
import os from "os";
var redirectUriCheck = class {
  requestMap = /* @__PURE__ */ new Map();
  //   constructor(private sdk: SDK<any>) {}
  async onRequest(sdk, req) {
    try {
      const urlString = req.getUrl();
      const url = new URL(urlString);
      sdk.console.log(`[OAuthPlugin] Intercepted request: ${urlString}`);
      if (!url.pathname.includes("/authorize") && !url.pathname.includes("/auth")) return;
      const params = new URLSearchParams(url.search);
      const redirectUri = params.get("redirect_uri");
      if (!redirectUri) return;
      const reqId = req.getId();
      this.requestMap.set(reqId, redirectUri);
      const clientId = params.get("client_id") ?? "(missing)";
      const responseType = params.get("response_type") ?? "(missing)";
      const isScan = params.has("scan");
      if (isScan) return;
      const output = {
        original_url: urlString,
        client_id: clientId,
        redirect_uri: redirectUri,
        response_type: responseType
      };
      try {
        const filePath = path.join(os.tmpdir(), "oauth-fuzz-input.json");
        await fs.writeFile(filePath, JSON.stringify(output, null, 2));
      } catch (err) {
        await sdk.findings.create({
          title: "[fs] Write Failed",
          description: `Could not write to file: ${err}`,
          request: req,
          reporter: "oauth-open-redirect-detector"
        });
      }
      await sdk.findings.create({
        title: "[ ] OAuth2 Authorization Request Collected",
        description: `client_id: ${clientId}
redirect_uri: ${redirectUri}
response_type: ${responseType}`,
        request: req,
        reporter: "oauth-open-redirect-detector"
      });
    } catch (err) {
      sdk.console.error(`Error in onRequest: ${err}`);
    }
  }
  async onResponse(sdk, req, resp) {
    try {
      const reqId = req.getId();
      const url = new URL(req.getUrl());
      const status = resp.getCode();
      const location = resp.getHeader("location")?.[0];
      const params = new URLSearchParams(url.search);
      const isScan = params.has("scan");
      if (!isScan) {
        this.requestMap.delete(reqId);
        return;
      }
      if (status >= 300 && status < 400 && location) {
        const redirectUri = this.requestMap.get(reqId) ?? "(unknown)";
        await sdk.findings.create({
          title: "[+] Redirect URI Misconfiguration Detected",
          description: `Status: ${status}
Location: ${location}
Request URL: ${url.href}
Redirect URI: ${redirectUri}`,
          request: req,
          reporter: "oauth-open-redirect-detector"
        });
      }
      this.requestMap.delete(reqId);
    } catch (err) {
      sdk.console.error(`Error in onResponse: ${err}`);
    }
  }
};

// packages/backend/src/index.ts
var pkceCheckController = new PKCECheck();
var redirectUriCheckController = new redirectUriCheck();
function init(sdk) {
  sdk.events.onInterceptRequest(
    async (sdk2, req) => {
      await redirectUriCheckController.onRequest(sdk2, req);
    }
  );
  sdk.events.onInterceptResponse(
    async (sdk2, req, resp) => {
      await pkceCheckController.test(sdk2, req);
      await redirectUriCheckController.onResponse(sdk2, req, resp);
    }
  );
}
export {
  init
};
