# Security Audit Report — app.closebot.com **Date:** February 7, 2026 **Auditor:** Clawdbot Security Scanner **Scope:** app.closebot.com, api.closebot.com, closebot.com **Authorization:** Fully authorized pentest by site owner (Jake Shore) --- ## 1. Executive Summary ### Severity Matrix | Severity | Count | Findings | |----------|-------|----------| | 🔴 **CRITICAL** | 1 | API CORS wildcard `Access-Control-Allow-Origin: *` on authenticated endpoints | | 🟠 **HIGH** | 3 | Missing security headers on API, Azure infrastructure info leak, No rate limiting on API | | 🟡 **MEDIUM** | 4 | WordPress readme/license exposed, NEXT_LOCALE cookie lacks Secure flag, API Azure AppId leak, /bot endpoint method disclosure | | đŸ”ĩ **LOW** | 3 | WordPress version exposed, Clerk auth status header leak, Build ID exposed in 404 pages | | â„šī¸ **INFO** | 6 | Tech stack details, positive findings | ### Overall Risk Rating: **HIGH** The most critical finding is that **api.closebot.com has `Access-Control-Allow-Origin: *` on ALL endpoints**, including authenticated ones like `/bot`, `/bot/list`, `/bot/create`, `/bot/config`, `/bot/settings`, `/lead`, `/agency`. This is the same class of vulnerability found in SuperFunnels. While `Access-Control-Allow-Credentials` is NOT set (which prevents cookie-based CSRF), any token-based auth (Bearer tokens) sent from JS is still vulnerable to being used in cross-origin attacks if tokens are predictable or leaked. --- ## 2. Tech Stack | Component | Technology | Details | |-----------|-----------|---------| | **Frontend (app)** | Next.js (App Router) | Build ID: `SJe3XKo4eqvdUfxjtlLi9`, deployment: `dpl_9XCbPqSSm7KFrRoZoDtTN7Cu7QhM` | | **Frontend Hosting** | Vercel | IAD1 region (us-east-1), Vercel Security Checkpoint enabled | | **Auth Provider** | Clerk | Headers: `x-clerk-auth-status`, `x-clerk-auth-reason` | | **API Backend** | ASP.NET (Kestrel) | Running on Azure App Service | | **API Hosting** | Azure App Service | `cb-api-zarqcgo3sph6q.azurewebsites.net` (West US 2) | | **Marketing Site** | WordPress 6.9.1 | Hosted on Kinsta, behind Cloudflare | | **CDN (marketing)** | Cloudflare | CF-Ray headers present | | **DNS** | GoDaddy | `ns77.domaincontrol.com`, `ns78.domaincontrol.com` | | **SSL** | Let's Encrypt (R13) | Valid Feb 5 – May 6, 2026 | | **i18n** | Multi-language | EN, ES, PT, NL supported | ### Subdomain Map | Subdomain | Status | Resolves To | |-----------|--------|-------------| | `app.closebot.com` | ✅ Active | Vercel (`592229a97db57f91.vercel-dns-017.com`) | | `api.closebot.com` | ✅ Active | Azure (`cb-api-zarqcgo3sph6q.azurewebsites.net` → `20.115.232.12`) | | `www.closebot.com` | ✅ Redirect | → `closebot.com` (Cloudflare `162.159.134.42`) | | `closebot.com` | ✅ Active | Cloudflare/Kinsta WordPress | | `admin.closebot.com` | ❌ No DNS | - | | `dashboard.closebot.com` | ❌ No DNS | - | | `ws.closebot.com` | ❌ No DNS | - | | `mail.closebot.com` | ❌ No DNS | - | | `staging.closebot.com` | ❌ No DNS | - | | `dev.closebot.com` | ❌ No DNS | - | ### DNS Records - **TXT:** Google site verification (`A_X4u_WKhr2Mp6maxNXC71DUBr-RlWzxCjrvyxnVrZo`), Microsoft verification (`MS=ms24389368`) - **MX:** None configured (no email hosting on root domain) - **NS:** GoDaddy (`ns77/ns78.domaincontrol.com`) --- ## 3. HTTP Security Headers ### app.closebot.com (Vercel/Next.js) | Header | Status | Value | |--------|--------|-------| | `Strict-Transport-Security` | ✅ Present | `max-age=63072000` (~2 years) | | `Content-Security-Policy` | ❌ **MISSING** | Not set | | `X-Frame-Options` | ❌ **MISSING** | Not set | | `X-Content-Type-Options` | ❌ **MISSING** | Not set | | `X-XSS-Protection` | ❌ **MISSING** | Not set (deprecated, but still recommended) | | `Referrer-Policy` | ❌ **MISSING** | Not set | | `Permissions-Policy` | ❌ **MISSING** | Not set | | `X-Powered-By` | âš ī¸ Leaking | `Next.js` | | `Server` | âš ī¸ Leaking | `Vercel` | **Severity: HIGH** — Missing CSP, X-Frame-Options, and X-Content-Type-Options on the frontend. ### api.closebot.com (Azure/Kestrel) | Header | Status | Value | |--------|--------|-------| | `Strict-Transport-Security` | ❌ **MISSING** | Not set | | `Content-Security-Policy` | ❌ **MISSING** | Not set | | `X-Frame-Options` | ❌ **MISSING** | Not set | | `X-Content-Type-Options` | ❌ **MISSING** | Not set | | `X-XSS-Protection` | ❌ **MISSING** | Not set | | `Referrer-Policy` | ❌ **MISSING** | Not set | | `Permissions-Policy` | ❌ **MISSING** | Not set | | `Server` | âš ī¸ Leaking | `Kestrel` | | `Request-Context` | âš ī¸ Leaking | `appId=cid-v1:1593e7a7-a705-4f69-977e-49c3fa1d0aa3` | | `x-ms-middleware-request-id` | âš ī¸ Leaking | `00000000-0000-0000-0000-000000000000` | **Severity: HIGH** — API has ZERO security headers. No HSTS, no CSP, nothing. ### closebot.com (WordPress/Cloudflare) | Header | Status | Value | |--------|--------|-------| | `X-Content-Type-Options` | ✅ Present | `nosniff` | | `Strict-Transport-Security` | ❌ **MISSING** | Not set | | `Content-Security-Policy` | ❌ **MISSING** | Not set | | `X-Frame-Options` | ❌ **MISSING** | Not set | | `Referrer-Policy` | ❌ **MISSING** | Not set | | `Permissions-Policy` | ❌ **MISSING** | Not set | --- ## 4. CORS Analysis — 🔴 CRITICAL ### api.closebot.com — WILDCARD CORS ON ALL ENDPOINTS This is the **#1 finding** of the entire audit. | Endpoint | Origin Tested | ACAO Header | Result | |----------|--------------|-------------|--------| | `/` (root) | `https://evil.com` | `*` | 🔴 **OPEN** | | `/` (root) | `null` | `*` | 🔴 **OPEN** | | `/` (root) | `https://evil.closebot.com` | `*` | 🔴 **OPEN** | | `/bot` | `https://evil.com` | `*` | 🔴 **OPEN** | | `/bot` (OPTIONS) | `https://evil.com` | `*` | 🔴 **OPEN** | | Preflight | Any | Allows `Authorization,Content-Type` | 🔴 **OPEN** | **Key facts:** - `Access-Control-Allow-Origin: *` is returned for ANY origin - Preflight `OPTIONS` returns `Access-Control-Allow-Headers: Authorization,Content-Type` — meaning cross-origin JS can send auth headers - `Access-Control-Allow-Credentials` is NOT set (mitigating cookie-based attacks) - However, if the API uses Bearer tokens (JWT) passed via `Authorization` header, any malicious website can make authenticated API calls if it obtains the token **Impact:** - If a user visits a malicious site while logged into Closebot, and the malicious site can obtain/guess the auth token, it can make full API calls to read/modify bot data, leads, agency settings - The preflight allows `Authorization` header, so even "complex" requests are permitted cross-origin **Recommendation:** Replace `*` with an explicit allowlist: `https://app.closebot.com`, `https://closebot.com` ### app.closebot.com — No CORS headers on most responses The frontend (Vercel/Next.js) does NOT return CORS headers on normal pages. The 404 page returns `Access-Control-Allow-Origin: *` but this is a static error page and lower risk. --- ## 5. Cookie/Session Analysis ### app.closebot.com Cookies | Cookie | Secure | HttpOnly | SameSite | Domain | Notes | |--------|--------|----------|----------|--------|-------| | `NEXT_LOCALE` | ❌ No | ❌ No | `Lax` | `/` path | âš ī¸ Language preference — missing Secure flag | | Clerk session cookies | Not directly observed (behind Vercel checkpoint) | — | — | — | Clerk typically handles this well | **Severity: MEDIUM** — `NEXT_LOCALE` cookie is set without `Secure` flag, allowing it to be sent over HTTP if user visits `http://` URL. While HSTS mitigates this on the app domain, it's still a best practice violation. ### api.closebot.com Cookies | Cookie | Secure | HttpOnly | SameSite | Domain | Notes | |--------|--------|----------|----------|--------|-------| | `TiPMix` | ✅ Yes | ✅ Yes | `None` | `.api.closebot.com` | Azure routing cookie | | `x-ms-routing-name` | ✅ Yes | ✅ Yes | `None` | `.api.closebot.com` | Azure routing — value: `self` | **Note:** `SameSite=None` on Azure routing cookies means they're sent cross-site. Combined with the CORS `*`, this is concerning — though these are infrastructure cookies, not auth tokens. ### closebot.com Cookies | Cookie | Secure | HttpOnly | SameSite | Domain | |--------|--------|----------|----------|--------| | `__cf_bm` | ✅ Yes | ✅ Yes | `None` | `.closebot.com` | Cloudflare bot management cookie — properly secured. ### Auth Mechanism - **Clerk** is used for authentication (confirmed by `x-clerk-auth-status` and `x-clerk-auth-reason` headers) - Unauthenticated requests get `x-clerk-auth-status: signed-out` and `x-clerk-auth-reason: session-token-and-uat-missing` - Root `/` redirects to `/login?redirect_url=%2F` (307 redirect) - `/register` exists and returns 200 — **open registration is available** --- ## 6. API/Endpoint Map ### app.closebot.com (Vercel/Next.js) | Path | Status | Notes | |------|--------|-------| | `/` | 307 | Redirects to `/login` | | `/login` | 200 | Login page (Clerk) | | `/register` | 200 | Registration page (open signup) | | `/signup` | 404 | Not used | | `/pricing` | 403* | Protected (behind Vercel checkpoint after rate limit) | | `/settings` | 403* | Protected | | `/billing` | 403* | Protected | | `/account` | 403* | Protected | | `/bots` | 403* | Protected | | `/conversations` | 403* | Protected | | `/analytics` | 403* | Protected | | `/docs` | 403* | Protected | | `/.env` | 404 | ✅ Not exposed | | `/.git` | 404 | ✅ Not exposed | | `/robots.txt` | 404 | No robots.txt | | `/sitemap.xml` | 404 | No sitemap | | `/.well-known/security.txt` | 404 | No security.txt | *403 responses are from Vercel Security Checkpoint (bot challenge), not actual authorization blocks. ### api.closebot.com (Azure/Kestrel) | Path | Status | Methods | Notes | |------|--------|---------|-------| | `/bot` | 401/405 | GET, POST | **Core bot endpoint** — requires auth | | `/bot/list` | 401 | — | List bots | | `/bot/create` | 401 | — | Create bot | | `/bot/all` | 401 | — | All bots | | `/bot/settings` | 401 | — | Bot settings | | `/bot/config` | 401 | — | Bot config | | `/lead` | 401 | — | Lead management | | `/agency` | 401 | — | Agency management | | `/swagger` | 301 | — | Swagger redirect (leads to 404 — likely disabled in prod) | | `/` | 404 | — | No root handler | | All other paths | 404 | — | — | **Severity: MEDIUM** — The `/bot` endpoint returns `Allow: GET, POST` in the 405 response, revealing accepted HTTP methods. The `/swagger` 301 redirect suggests Swagger UI was enabled at some point. ### closebot.com (WordPress) | Path | Status | Notes | |------|--------|-------| | `/wp-admin` | 301 | Redirects to login — **exposed** | | `/wp-login.php` | 200 | **Login page accessible** | | `/wp-json` | 200 | REST API root — **accessible** | | `/wp-json/wp/v2/users` | 401 | User enumeration blocked ✅ | | `/.env` | 403 | Blocked ✅ | | `/xmlrpc.php` | 403 | Blocked ✅ | | `/wp-config.php` | 403 | Blocked ✅ | | `/readme.html` | 200 | âš ī¸ **Exposed** — WordPress readme | | `/license.txt` | 200 | âš ī¸ **Exposed** — WordPress license | | `/feed/` | 200 | âš ī¸ Exposes WordPress version `6.9.1` | --- ## 7. Client-Side Analysis ### Findings from HTML Source 1. **Vercel Security Checkpoint** — app.closebot.com serves a JS-based browser verification challenge (Astro framework) before allowing access. This is an effective bot protection layer. 2. **Next.js Build ID exposed**: `SJe3XKo4eqvdUfxjtlLi9` — visible in error pages and static chunk URLs. Low risk but provides deployment fingerprinting. 3. **Deployment ID exposed**: `dpl_9XCbPqSSm7KFrRoZoDtTN7Cu7QhM` — in chunk URLs. 4. **Internationalization**: Site supports EN, ES, PT, NL via `x-matched-path: /[locale]/login` routing. 5. **Clerk Auth Headers Leaked in Every Response**: - `x-clerk-auth-status: signed-out` - `x-clerk-auth-reason: session-token-and-uat-missing` These headers confirm the auth provider and current auth state to any observer. ### Source Maps - All `_next/static` resources are behind Vercel Security Checkpoint (return 403 challenge page to curl) - Could not confirm/deny source map availability without browser access - **Recommendation:** Ensure `.map` files are not deployed to production ### JS Bundles Analysis - Could not analyze JS bundles directly due to Vercel Security Checkpoint blocking curl requests - The checkpoint uses a Web Worker-based challenge system - **Recommendation:** Verify no API keys, Firebase configs, or secrets are hardcoded in client bundles --- ## 8. Injection Test Results ### Host Header Injection - **app.closebot.com**: ✅ **Protected** — Vercel returns 404 `DEPLOYMENT_NOT_FOUND` for invalid Host headers - **X-Forwarded-Host**: âš ī¸ Accepted without error (200 response) — Vercel passes it through to Next.js ### CRLF Injection - **app.closebot.com**: ✅ **Not vulnerable** — URL-encoded `%0d%0a` is preserved in `Link` header as-is (not decoded), no header injection ### Path Traversal - Standard path traversal payloads return 404 from Next.js catch-all router — **not vulnerable** ### SQL Injection / XSS - Could not test input fields directly (behind Vercel Security Checkpoint) - API endpoints return 401 without processing input — auth layer prevents unauthenticated injection testing - **Recommendation:** Test authenticated injection scenarios during development --- ## 9. Rate Limiting Results ### app.closebot.com | Endpoint | Requests | All Status Codes | Rate Limited? | |----------|----------|-----------------|---------------| | `/login` | 50 rapid | All `200` | 🔴 **NO** | After approximately 50+ requests, Vercel Security Checkpoint started returning 403 challenges — this is Vercel's built-in protection, not application-level rate limiting. **Note:** Vercel's checkpoint eventually kicked in (HTTP 403 with `x-vercel-mitigated: challenge`), providing some protection, but this is infrastructure-level, not application-level rate limiting. ### api.closebot.com | Endpoint | Requests | All Status Codes | Rate Limited? | |----------|----------|-----------------|---------------| | `/` | 50 rapid | All `404` | 🔴 **NO** | **Severity: HIGH** — No rate limiting on the API. This means: - Brute-force attacks on auth tokens are possible - API abuse/scraping is unrestricted - DDoS amplification risk **Recommendation:** Implement rate limiting (e.g., Azure API Management, custom middleware, or Cloudflare in front of the API). --- ## 10. Auth/Firebase Analysis ### Authentication System: Clerk - **Provider**: Clerk (not Firebase) - **Open Registration**: Yes — `/register` returns 200 - **Login**: `/login` with redirect support - **Auth tokens**: Likely JWT via Clerk (standard Clerk flow) - **Session management**: Clerk handles sessions via `__session` cookie and/or Bearer tokens ### No Firebase Found - `/__/firebase/init.json` → 404 - No Firebase references detected in accessible HTML/JS - Not applicable for Firebase-specific testing --- ## 11. Third-Party Risk ### External Scripts (from HTML source) Due to Vercel Security Checkpoint, detailed JS bundle analysis was limited. From what was visible: 1. **Clerk** — Authentication provider (trusted, SOC 2 compliant) 2. **Next.js** — React framework (trusted) 3. **Vercel** — Hosting platform (trusted) ### WordPress Third-Party - **Cloudflare** — CDN and bot protection - **Kinsta** — WordPress hosting ### SRI (Subresource Integrity) - Could not verify SRI on JS bundles due to Vercel checkpoint - **Recommendation:** Ensure all third-party scripts have SRI hashes --- ## 12. Infrastructure Notes ### Origin IP Exposure | Domain | Behind CDN? | Origin Exposed? | |--------|-------------|-----------------| | `app.closebot.com` | Vercel Edge | ✅ Protected (Vercel DNS) | | `api.closebot.com` | ❌ **NO CDN** | 🔴 **YES — `20.115.232.12`** (Azure direct) | | `closebot.com` | Cloudflare | ✅ Protected | **Severity: HIGH** — `api.closebot.com` resolves directly to an Azure App Service IP with no CDN/WAF in front. The full Azure hostname `cb-api-zarqcgo3sph6q.azurewebsites.net` is exposed via DNS CNAME chain. ### Azure Information Leakage - **App ID**: `cid-v1:1593e7a7-a705-4f69-977e-49c3fa1d0aa3` (via `Request-Context` header) - **Azure hostname**: `cb-api-zarqcgo3sph6q.azurewebsites.net` - **Datacenter**: `waws-prod-mwh-117` (West US 2, Microsoft West Hub) - **Server**: Kestrel (ASP.NET) - **Routing cookies**: TiPMix (Azure traffic routing) ### SSL/TLS | Property | Value | |----------|-------| | **Certificate** | Let's Encrypt R13 | | **Valid From** | Feb 5, 2026 | | **Valid Until** | May 6, 2026 | | **Subject** | `CN=app.closebot.com` | | **SANs** | `app.closebot.com` only | | **HSTS** | ✅ `max-age=63072000` (app only) | | **HSTS Preload** | ❌ Not in preload list | --- ## 13. Positive Findings ✅ 1. **Vercel Security Checkpoint** — Effective bot/scraping protection on the frontend 2. **Clerk Authentication** — Industry-standard auth provider, properly handling session states 3. **No sensitive files exposed** — `.env`, `.git`, `wp-config.php` all blocked 4. **WordPress user enumeration blocked** — `/wp-json/wp/v2/users` returns 401 5. **XML-RPC disabled** — Returns 403 6. **HSTS on app.closebot.com** — 2-year max-age 7. **Host header injection blocked** — Vercel properly rejects invalid hosts 8. **CRLF injection blocked** — Properly encoded 9. **No admin/staging subdomains exposed** — Clean subdomain surface 10. **WordPress .env/wp-config blocked** — Server-level blocks in place 11. **Azure API cookies properly secured** — Secure, HttpOnly flags set 12. **Cloudflare protecting WordPress** — Bot management, edge caching --- ## 14. Recommendations ### 🔴 CRITICAL — Fix Immediately #### 1. API CORS Wildcard (`Access-Control-Allow-Origin: *`) **Impact:** Any website can make cross-origin requests to your API, including authenticated endpoints **Fix:** ```csharp // In your ASP.NET API startup/program.cs builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.WithOrigins( "https://app.closebot.com", "https://closebot.com" ) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); ``` ### 🟠 HIGH — Fix This Week #### 2. Add Security Headers to API (api.closebot.com) ```csharp // Middleware or web.config app.Use(async (context, next) => { context.Response.Headers.Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains"); context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); context.Response.Headers.Add("X-Frame-Options", "DENY"); context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin"); context.Response.Headers.Add("Content-Security-Policy", "default-src 'none'"); context.Response.Headers.Remove("Server"); context.Response.Headers.Remove("Request-Context"); context.Response.Headers.Remove("x-ms-middleware-request-id"); await next(); }); ``` #### 3. Put API Behind CDN/WAF - Add Cloudflare or Azure Front Door in front of `api.closebot.com` - Hide the origin Azure IP - Get DDoS protection and rate limiting #### 4. Implement API Rate Limiting ```csharp // ASP.NET rate limiting middleware builder.Services.AddRateLimiter(options => { options.AddFixedWindowLimiter("api", opt => { opt.Window = TimeSpan.FromMinutes(1); opt.PermitLimit = 60; opt.QueueLimit = 0; }); }); ``` ### 🟡 MEDIUM — Fix This Sprint #### 5. Add Security Headers to app.closebot.com In `next.config.js`: ```javascript const securityHeaders = [ { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.clerk.accounts.dev; style-src 'self' 'unsafe-inline';" }, { key: 'X-Frame-Options', value: 'DENY' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, ]; module.exports = { headers: async () => [{ source: '/:path*', headers: securityHeaders }], poweredByHeader: false, // Remove X-Powered-By: Next.js }; ``` #### 6. Remove WordPress readme.html and license.txt ```bash # On WordPress server rm /path/to/wordpress/readme.html rm /path/to/wordpress/license.txt ``` #### 7. Hide Azure Infrastructure Headers Remove `Request-Context` and `x-ms-middleware-request-id` headers from API responses. #### 8. Add `Secure` Flag to NEXT_LOCALE Cookie In Next.js middleware or cookie configuration, ensure `Secure` flag is set. ### đŸ”ĩ LOW — Fix When Convenient #### 9. Add `security.txt` Create `/.well-known/security.txt` with responsible disclosure contact. #### 10. Add `robots.txt` Even if you don't want indexing, an explicit `robots.txt` is good practice. #### 11. Hide WordPress Version ```php // In functions.php remove_action('wp_head', 'wp_generator'); ``` #### 12. Remove Clerk Auth Headers from Responses Configure Clerk middleware to not expose `x-clerk-auth-status` and `x-clerk-auth-reason` to external responses. #### 13. HSTS Preload Submit `app.closebot.com` to the HSTS preload list and add `includeSubDomains; preload` to the HSTS header. --- ## Appendix: Raw Test Data ### CORS Test Results (api.closebot.com) ``` Origin: https://evil.com → Access-Control-Allow-Origin: * Origin: null → Access-Control-Allow-Origin: * Origin: https://evil.closebot.com → Access-Control-Allow-Origin: * OPTIONS (preflight) → Access-Control-Allow-Origin: *, Allow-Headers: Authorization,Content-Type ``` ### API Endpoint Discovery Summary ``` Authenticated (401): /bot, /bot/list, /bot/create, /bot/all, /bot/settings, /bot/config, /lead, /agency Redirect (301): /swagger Not Found (404): everything else ``` ### Rate Limiting Test (50 rapid requests) ``` app.closebot.com/login: 200 200 200 200 200 ... (all 200, then Vercel checkpoint kicks in) api.closebot.com/: 404 404 404 404 404 ... (all 404, never rate limited) ```