Skip to content

Security

Security is built into the OpenScouter infrastructure at multiple layers. This document covers HTTP headers, CORS policy, rate limiting, input validation, SQL injection prevention, and XSS prevention. It also describes the vulnerability disclosure process.

HTTP Security Headers

Every response from the OpenScouter API and web application includes a standard set of security headers. These are applied by Next.js middleware on all routes.

HeaderValuePurpose
Content-Security-PolicySee belowRestricts resource loading to trusted origins
X-Frame-OptionsDENYPrevents clickjacking by blocking iframe embedding
X-Content-Type-OptionsnosniffPrevents MIME-type sniffing
Referrer-Policystrict-origin-when-cross-originLimits referrer information sent to third parties
Permissions-Policycamera=(), microphone=(), geolocation=()Disables browser features not needed by the application
Strict-Transport-Securitymax-age=63072000; includeSubDomainsEnforces HTTPS for 2 years

Content Security Policy

The CSP is constructed to allow only the resources required for the application to function. The policy varies between the web application and the API.

Web application CSP:

default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self' wss://realtime.supabase.co;
font-src 'self' https://fonts.gstatic.com;
frame-src 'none';
object-src 'none';
base-uri 'self';

'unsafe-inline' for styles is an accepted tradeoff for compatibility with CSS-in-JS libraries. Inline scripts remain prohibited.

API CSP:

default-src 'none';
frame-ancestors 'none';

The API serves no HTML content, so a restrictive CSP blocking all resources is appropriate.

CORS Policy

Cross-Origin Resource Sharing is configured to allow requests only from known origins. The allowed origins list is managed via environment variables and is different across environments.

The CORS middleware performs the following checks on every preflight and credentialed request:

  1. The Origin header must match an entry in the allowed origins list
  2. Allowed methods: GET, POST, PATCH, DELETE, OPTIONS
  3. Allowed headers: Content-Type, Authorization, X-Test-Token, X-Api-Key
  4. Credentials are allowed: Access-Control-Allow-Credentials: true

Requests from origins not on the allowed list receive a 403 Forbidden response without CORS headers. The browser will block the response.

Wildcard origins (*) are never used because the API supports credentialed requests. Credentialed requests with a wildcard origin are rejected by browsers.

Rate Limiting

Rate limits are enforced per IP address and per authenticated identity. Limits are applied at the middleware layer before route handlers execute.

ContextLimitWindow
Unauthenticated requests60 requests1 minute
Authenticated API requests300 requests1 minute
Test event ingestion (extension)1000 requests1 minute
AI agent endpoints10 requests1 minute
Authentication endpoints (login, magic link)10 requests15 minutes

Rate limit state is stored in Redis with a sliding window algorithm. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating when the next request will be accepted.

Authentication endpoint limits are stricter to prevent credential stuffing and brute-force attacks.

Input Validation

All input is validated at the API boundary before it reaches business logic or database queries. Validation uses Zod schemas defined in the route handler files.

Every request body, query parameter, and path parameter is validated against a schema. Validation errors return a 400 Bad Request response with a structured error body describing which fields failed and why.

{
"error": "Validation failed",
"details": [
{
"field": "session_id",
"message": "Expected UUID, received 'abc'"
},
{
"field": "events",
"message": "Array must contain at least 1 element"
}
]
}

String inputs are trimmed and length-limited. File uploads are validated for MIME type and size before being processed. Image uploads for facial snapshots are validated as JPEG or PNG and must not exceed 2MB.

SQL Injection Prevention

OpenScouter does not construct SQL strings manually. All database access goes through the Supabase JavaScript client, which uses parameterized queries internally. User input is never interpolated into query strings.

For the small number of complex queries that use raw SQL via Supabase’s RPC functions, all parameters are passed as named bind variables, not concatenated strings.

Additionally, RLS policies provide a second layer of defense. Even if a parameterization error occurred in application code, the resulting query would be constrained by the authenticated user’s RLS context.

XSS Prevention

The primary XSS defense is React’s built-in output encoding. React escapes all string values before inserting them into the DOM. Raw HTML insertion via dangerouslySetInnerHTML is used in exactly two places in the codebase: the Plain English summary renderer and the WCAG criterion description renderer. Both of these render content that originates from AI model output.

AI-generated content is passed through a sanitization step using the DOMPurify library before being stored in the database. The sanitization step runs server-side during the Report Writer agent’s output processing. Content stored in the database is already clean by the time it reaches the frontend.

Stored content is sanitized once on write, not on every read. This avoids double-encoding issues that would corrupt legitimate HTML entities in technical content.

Vulnerability Disclosure

OpenScouter operates a responsible disclosure program. Security researchers who discover vulnerabilities are encouraged to report them before public disclosure.

Contact: security@openscouter.com

PGP key: Available on the OpenScouter security page at https://openscouter.com/.well-known/security.txt

Response SLA: Initial acknowledgement within 48 hours. Severity assessment within 5 business days.

Scope: All production systems at *.openscouter.com. The Chrome extension. The OpenScouter mobile application.

Out of scope: Social engineering. Physical attacks. Denial of service. Issues in third-party services not under OpenScouter’s control.

Researchers who responsibly disclose valid vulnerabilities are acknowledged in the security hall of fame and may be eligible for a bounty depending on severity.