Skip to content

Security: uday03meh/your-github-wrapped

Security

SECURITY.md

Security

This document describes the security measures implemented in GitHub Wrapped.

Overview

GitHub Wrapped is designed with security as a priority. All sensitive operations happen server-side, and multiple layers of protection prevent abuse.

Token Handling

GitHub Personal Access Token

Location: Environment variable GITHUB_TOKEN
Access: Server-side only (never sent to client)
Scope: No scopes required (public data only)

Security measures:

  1. Environment Variable: Token stored in .env.local, never committed
  2. Server-Side Only: Used only in API routes, never in client code
  3. No Logging: Token never logged or included in error messages
  4. No Exposure: GitHub errors are sanitized before returning to client

Recommendations

  • Rotate tokens periodically (every 90 days)
  • Use fine-grained PATs when available
  • Never commit tokens to version control
  • Use different tokens for dev/staging/production

Security Headers

All responses include the following security headers (configured in next.config.ts):

X-Frame-Options

Value: SAMEORIGIN
Purpose: Prevents clickjacking attacks by disallowing iframe embedding

X-Content-Type-Options

Value: nosniff
Purpose: Prevents MIME type sniffing, reduces drive-by download attacks

X-XSS-Protection

Value: 1; mode=block
Purpose: Enables browser's XSS filter (legacy, but still useful)

Referrer-Policy

Value: strict-origin-when-cross-origin
Purpose: Controls referrer information sent with requests

Strict-Transport-Security (HSTS)

Value: max-age=63072000; includeSubDomains; preload
Purpose: Forces HTTPS connections
Duration: 2 years

Content-Security-Policy (CSP)

default-src 'self'
script-src 'self' 'unsafe-eval' 'unsafe-inline'  # Required by Next.js
style-src 'self' 'unsafe-inline'                  # Required by Tailwind
img-src 'self' data: https://avatars.githubusercontent.com https://media.giphy.com
font-src 'self' data:
connect-src 'self' https://api.github.com
frame-ancestors 'self'
base-uri 'self'
form-action 'self'

Permissions-Policy

Value: camera=(), microphone=(), geolocation=(), interest-cohort=()
Purpose: Disables unnecessary browser features, opts out of FLoC

Rate Limiting

Implementation

// Location: src/lib/rateLimit.ts

Configuration:
- Window: 60 seconds
- Max requests: 30 per IP
- Algorithm: Sliding window

Protection Against

  • Denial of Service (DoS): Limits request volume
  • Brute Force: Prevents rapid enumeration
  • Resource Exhaustion: Protects backend services

Response Headers

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 25
X-RateLimit-Reset: 1704067200

Rate Limit Error Response

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please try again in 45 seconds.",
    "retryAfterSeconds": 45,
    "requestId": "req_m5k7j9x2"
  }
}

Input Validation

Username Validation

// GitHub username rules enforced:
// - 1-39 characters
// - Alphanumeric characters only (plus single hyphens)
// - Cannot start or end with hyphen
// - No consecutive hyphens

const GITHUB_USERNAME_REGEX = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/;

Year Validation

// Year must be:
// - Integer
// - Between 2008 (GitHub launch) and current year
// - Defaults to current year if not provided

Validation Errors

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid GitHub username. Usernames can only contain alphanumeric characters and single hyphens.",
    "requestId": "req_m5k7j9x2"
  }
}

XSS Prevention

React's Built-in Protection

React automatically escapes values before rendering, preventing XSS:

// Safe - React escapes the value
<p>{user.bio}</p>

// We NEVER use dangerouslySetInnerHTML with user data

Content Security Policy

CSP headers prevent inline script execution from untrusted sources.

Error Handling

Production Error Responses

// Stack traces are NEVER exposed in production
// Generic messages for internal errors
// Specific messages only for known error types

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred. Please try again later.",
    "requestId": "req_m5k7j9x2"
  }
}

Error Codes

Code HTTP Status Description
VALIDATION_ERROR 400 Invalid input
USER_NOT_FOUND 404 GitHub user doesn't exist
RATE_LIMIT_EXCEEDED 429 Too many requests
GITHUB_API_ERROR 502 GitHub API failed
NETWORK_ERROR 503 Network connectivity issue
TIMEOUT_ERROR 504 Request timed out
INTERNAL_ERROR 500 Unexpected server error

API Security

No Direct GitHub Calls from Browser

Browser → /api/wrapped/[username] → GitHub API
        ↑                         ↑
   Your server             Authenticated with token

The browser never calls api.github.com directly. All requests go through your API endpoint, which:

  1. Validates input
  2. Applies rate limiting
  3. Adds authentication
  4. Caches responses
  5. Sanitizes errors

CORS Configuration

// Only OPTIONS requests return CORS headers
// API is designed for same-origin use only
export async function OPTIONS() {
  return new NextResponse(null, {
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, OPTIONS',
    },
  });
}

Dependency Security

Audit

npm audit

Run regularly to check for vulnerabilities in dependencies.

Key Dependencies

Package Purpose Security Notes
next Framework Regular security updates
zod Validation Type-safe validation
html-to-image Screenshot Client-side only

Production Checklist

  • GITHUB_TOKEN is set in production environment
  • .env.local is in .gitignore
  • Source maps are disabled (productionBrowserSourceMaps: false)
  • Security headers are applied (check with securityheaders.com)
  • Rate limiting is working (test with rapid requests)
  • Error messages don't expose internals
  • HTTPS is enforced
  • Dependencies are up to date

Reporting Security Issues

If you discover a security vulnerability, please email the maintainer directly instead of opening a public issue.

OWASP Top 10 Coverage

Vulnerability Mitigation
A01 Broken Access Control N/A - public data only
A02 Cryptographic Failures HTTPS enforced, no sensitive data stored
A03 Injection Zod validation, parameterized requests
A04 Insecure Design Server-side rendering, no direct API exposure
A05 Security Misconfiguration Security headers, strict CSP
A06 Vulnerable Components Regular dependency updates
A07 Auth Failures N/A - no user authentication
A08 Software/Data Integrity NPM lockfile, no arbitrary code execution
A09 Logging Failures Request IDs for tracing
A10 SSRF Input validation, no URL parameters

There aren't any published security advisories