# 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)
```
