# client_secret_check.py from mitmproxy import http, ctx from urllib.parse import urlparse, parse_qs from typing import Optional, List import lib.target as target from lib.report import save_report class ClientSecretChecker: def is_oauth_uri(self, uri: str) -> bool: qs = parse_qs(urlparse(uri).query) keys = qs.keys() return "client_id" in keys and ("client_secret" in keys or "client_secret" in uri) def has_client_secret_in_uri(self, uri: str) -> bool: qs = parse_qs(urlparse(uri).query) return "client_secret" in qs def is_post_with_client_secret(self, flow: http.HTTPFlow) -> bool: if flow.request.method != "POST": return False content_type = flow.request.headers.get("Content-Type", "") if "application/x-www-form-urlencoded" in content_type: body = parse_qs(flow.request.get_text()) return "client_secret" in body return False def is_exposed_in_referer(self, flow: http.HTTPFlow) -> bool: referer = flow.request.headers.get("Referer", "") return "client_secret" in referer def check_client_secret_leak(self, flow: http.HTTPFlow) -> List[str]: messages = [] if self.has_client_secret_in_uri(flow.request.url): messages.append("client_secret found in URL query string") if self.is_post_with_client_secret(flow): messages.append("client_secret found in POST body") if self.is_exposed_in_referer(flow): messages.append("client_secret exposed in Referer header") return messages def response(self, flow: http.HTTPFlow) -> None: try: if not self.is_oauth_uri(flow.request.url): return issues = self.check_client_secret_leak(flow) if issues: desc = " | ".join(issues) report_data = [{ 'target': target.load(), 'status': "HIGH", 'title': "OAuth Client Secret Exposure", 'description': desc, 'uri': flow.request.url, }] save_report(report_data) print(f"[INFO] Client Secret Check: {desc}") except Exception as e: print(f"[ERROR] Client Secret Check failed: {e}")