Every Report Includes
Executive Summary
High-level overview for founders and stakeholders
Scope & Methodology
What was tested and how
Risk Summary
Finding count by severity with risk score
Detailed Findings
Each vulnerability with reproduction & fix
Remediation Roadmap
Prioritized fix order based on impact
Penetration Test Report
SABLE-RPT-2026-XXXX
Executive Summary
SableOffensive conducted a penetration test of [REDACTED]'s web application during February 2026. The assessment identified 2 critical, 1 high, 1 medium, and 1 low severity findings. The most critical issues involve unauthorized data access (IDOR) and exposed service credentials that bypass database security policies.
Detailed Findings
Broken Object Level Authorization (IDOR) on User Profile API
Description
The user profile API endpoint allows any authenticated user to access other users' profile data by modifying the user ID parameter. No server-side authorization check validates that the requesting user owns the requested resource.
Impact
An attacker can enumerate and access all user profiles including email addresses, billing information, and connected accounts. With approximately 2,400 users in the database, this exposes the complete user dataset.
Steps to Reproduce
- 1.Authenticate as User A (test account)
- 2.Send GET /api/users/{USER_A_ID} -- returns own profile (expected)
- 3.Change ID to User B: GET /api/users/{USER_B_ID}
- 4.Full profile data for User B is returned (unauthorized)
- 5.Increment IDs to enumerate all users
Remediation
Add server-side authorization middleware that validates the requesting user's session matches the requested resource. Example for Next.js API route:
// Before (vulnerable)
export async function GET(req, { params }) {
const user = await db.users.findById(params.id);
return Response.json(user);
}
// After (fixed)
export async function GET(req, { params }) {
const session = await getSession(req);
if (session.userId !== params.id) {
return new Response('Forbidden', { status: 403 });
}
const user = await db.users.findById(params.id);
return Response.json(user);
}Exposed Supabase Service Role Key in Client Bundle
Description
The Supabase service_role key is hardcoded in the client-side JavaScript bundle. This key bypasses all Row Level Security (RLS) policies and provides full database access.
Impact
An attacker with this key can read, modify, and delete any data in the Supabase database, bypassing all security policies. This is equivalent to direct database admin access.
Steps to Reproduce
- 1.View page source or open DevTools > Sources
- 2.Search for "supabase" in JavaScript bundles
- 3.Find service_role key: eyJhbGci...[REDACTED]
- 4.Use key with Supabase client to bypass RLS
- 5.Full CRUD access to all tables confirmed
Remediation
Move the service_role key to server-side only. Use the anon key for client-side operations and ensure RLS policies are properly configured.
// .env.local (never expose to client)
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
// next.config.js - ensure server-only
// DO NOT prefix with NEXT_PUBLIC_
// Server-side usage only:
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY! // server only
)Cross-Site Scripting (XSS) via User Display Name
Description
The user display name field does not sanitize HTML/JavaScript input. When displayed in the team dashboard, the unsanitized name executes in other users' browsers.
Impact
An attacker can inject JavaScript that executes in other team members' browsers, enabling session hijacking, data exfiltration, and account takeover of admin users.
Steps to Reproduce
- 1.Navigate to Profile Settings
- 2.Set display name to: <img src=x onerror=alert(document.cookie)>
- 3.Save profile
- 4.Open team dashboard as another user
- 5.XSS payload executes in victim's browser
Remediation
Sanitize all user input before rendering. Use React's built-in XSS protection (avoid dangerouslySetInnerHTML) and add server-side input validation.
// Server-side validation
import { z } from 'zod';
const profileSchema = z.object({
displayName: z.string()
.min(1)
.max(50)
.regex(/^[a-zA-Z0-9 _-]+$/,
'Only letters, numbers, spaces, hyphens')
});
// Validate before saving
const validated = profileSchema.parse(req.body);Missing Rate Limiting on Authentication Endpoints
Description
The login endpoint has no rate limiting. An attacker can make unlimited authentication attempts, enabling brute force and credential stuffing attacks.
Impact
Attackers can perform credential stuffing using leaked password databases, or brute force weak passwords without any throttling or account lockout.
Steps to Reproduce
- 1.Send 100+ login requests in rapid succession
- 2.No rate limiting or CAPTCHA triggered
- 3.All requests processed normally
- 4.No account lockout after failed attempts
Remediation
Implement rate limiting with progressive delays and account lockout after failed attempts.
// Using upstash/ratelimit for Next.js
import { Ratelimit } from '@upstash/ratelimit';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, '60 s'),
});
// In auth handler:
const { success } = await ratelimit.limit(ip);
if (!success) {
return new Response('Too many attempts',
{ status: 429 });
}Security Headers Not Configured
Description
The application does not set critical security headers including Content-Security-Policy, X-Frame-Options, and Strict-Transport-Security.
Impact
Missing headers increase the attack surface for clickjacking, MIME-type sniffing attacks, and reduce the effectiveness of browser security features.
Steps to Reproduce
- 1.curl -I https://[REDACTED].app
- 2.Missing: Content-Security-Policy
- 3.Missing: X-Frame-Options
- 4.Missing: Strict-Transport-Security
- 5.Missing: X-Content-Type-Options
Remediation
Add security headers in next.config.js or middleware.
// next.config.js
const securityHeaders = [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains' },
{ key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'" },
];Full Reports Also Include
Remediation Timeline
Prioritized fix order -- which vulnerabilities to fix first based on exploitability and business impact.
Security Posture Score
Overall security rating from A to F, with specific areas of strength and weakness identified.
Code-Level Fixes
Copy-pasteable code snippets specific to your stack. Not generic advice -- real implementations.
Get Your Own Security Report
Same depth, same quality. Reports delivered within 24-48 hours. Starting at just $29 for MVPs.