22 KiB
Raw Blame History

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.net20.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: MEDIUMNEXT_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: HIGHapi.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:

// 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)

// 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

// 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:

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

# 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.

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

// 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)