OneShotCV

Security & Trust

Last updated: January 1, 2026

TL;DR: Your resume data never touches our servers. All processing happens in your browser. We've implemented industry-standard security measures to protect your payment information and prevent abuse.

Our Security Architecture

OneShotCV is built with a privacy-first, security-by-design approach. We don't just say we're secure โ€” here's exactly how we protect you:

๐Ÿ”’ Client-Side Processing

Your resume data never leaves your browser.

Why this matters: Even if our servers were compromised, there's nothing to steal. Your data physically doesn't exist in our infrastructure.

Here's the actual code:

// Client-side PDF generation (public/js/export.js)
async function generatePDF() {
  // Collect form data in browser
  const resumeData = collectAllFormData();
  
  // Generate HTML from data (still in browser)
  const html = generateResumeHTML(resumeData);
  
  // Convert to PDF using html2pdf.js (runs in browser)
  const opt = {
    margin: [0.5, 0.5, 0.5, 0.5],
    filename: 'Resume.pdf',
    html2canvas: { scale: 2 },
    jsPDF: { unit: 'in', format: 'letter' }
  };
  
  // This entire conversion happens CLIENT-SIDE
  // No server ever sees this data
  return html2pdf().set(opt).from(html).save();
}

๐Ÿ’ณ Payment Security (PCI Compliant)

When you choose to pay $3 for export, we use Stripe โ€” a PCI Level 1 certified payment processor trusted by millions of businesses.

Security measure: Our payment API only accepts requests from oneshotcv.app and www.oneshotcv.app. This prevents phishing attacks where malicious sites try to use our Stripe account.

Here's the actual code:

// CORS whitelist in api/create-checkout.js
const allowedOrigins = [
  'https://oneshotcv.app',
  'https://www.oneshotcv.app',
  'http://localhost:8000'  // dev only
];

const requestOrigin = req.headers.origin;
if (allowedOrigins.includes(requestOrigin)) {
  res.setHeader('Access-Control-Allow-Origin', requestOrigin);
}

// Origin validation for redirect URLs
const validOrigins = [
  'https://oneshotcv.app',
  'https://www.oneshotcv.app'
];
const successOrigin = validOrigins.includes(origin) 
  ? origin 
  : 'https://oneshotcv.app';  // fallback to prod

Additional CORS protections:

Note: CORS is browser-enforced and prevents client-side attacks. Server-side API abuse is mitigated by rate limiting (see below).

โฑ๏ธ Rate Limiting & DoS Protection

To prevent abuse and denial-of-service attacks, our payment API implements rate limiting:

Why this matters: Even if an attacker bypasses CORS from their server, they can't flood our Stripe account with thousands of checkout sessions. Rate limiting keeps abuse minimal.

Technical note: Rate limiting is best-effort per edge instance and resets on cold starts. This is sufficient for abuse prevention but not designed as a DDoS mitigation system (for that, we rely on Vercel's edge network protections).

Here's the actual code:

// Rate limiting in api/create-checkout.js
const rateLimitMap = new Map();
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
const MAX_REQUESTS = 5; // 5 requests per minute per IP

function checkRateLimit(ip) {
  const now = Date.now();
  const record = rateLimitMap.get(ip) || { 
    count: 0, 
    resetTime: now + RATE_LIMIT_WINDOW 
  };
  
  // Reset counter if window expired
  if (now > record.resetTime) {
    record.count = 0;
    record.resetTime = now + RATE_LIMIT_WINDOW;
  }
  
  record.count++;
  rateLimitMap.set(ip, record);
  return record.count <= MAX_REQUESTS;
}

// In request handler
const clientIp = req.headers['x-forwarded-for']?.split(',')[0] 
  || req.headers['x-real-ip'] || 'unknown';

if (!checkRateLimit(clientIp)) {
  return res.status(429).json({ 
    error: 'Too many requests. Please wait a minute.' 
  });
}

Attack vectors we've addressed:

๐ŸŒ HTTPS Everywhere

All connections to OneShotCV use TLS 1.3 encryption provided by Vercel's edge network.

๐Ÿšซ What We DON'T Do

๐Ÿ“Š Privacy-First Analytics

We use Vercel Analytics โ€” a privacy-friendly alternative to Google Analytics.

Why we track anything: We need to know if the site is working and being used. But we don't need to know who you are.

๐Ÿ’พ Local Storage Only

Your resume auto-saves to your browser's localStorage every 500ms while you type.

Important: If you switch devices or clear your browser, your resume is gone. We can't recover it because we never had it. Export JSON backups regularly.

Here's the actual code:

// Auto-save to localStorage (public/js/form.js)
function saveToLocalStorage() {
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(() => {
    const data = {
      header: resumeData.header,
      summary: resumeData.summary,
      entries: { /* ... */ }
    };
    
    // Saved ONLY in browser, never transmitted
    localStorage.setItem('oneshotcv-resume', JSON.stringify(data));
  }, 500);
}

Transparency & Accountability

We're committed to security transparency:

Found a security issue? Email us at oneshotcv@pm.me โ€” we take security seriously and respond fast.

๐Ÿงช Regular Security Audits

We conduct regular internal security reviews of our codebase and infrastructure:

๐Ÿ“ฑ Mobile Security

On mobile devices, PDF generation isn't supported due to browser limitations. We guide users to:

We disable PDF export on mobile to prevent failed transactions and confusion.

โš–๏ธ Legal Compliance

๐Ÿšจ In Case of Breach

Scenario: What if our servers were compromised?

Impact: Minimal. An attacker could:

What they CANNOT access:

Response plan: We'd notify users via the homepage, rotate all API keys, and investigate the breach. But your personal data would remain secure because it was never at risk.

๐ŸŽฏ Security Principles

  1. Data minimization: Don't collect what you don't need
  2. Client-side processing: Keep sensitive data local
  3. Encryption in transit: HTTPS everywhere
  4. Third-party trust: Use battle-tested providers (Stripe, Vercel)
  5. Transparency: Public documentation
  6. Fail safely: Errors don't leak data

Questions?

If you have security concerns or questions, email us at oneshotcv@pm.me.

We're committed to maintaining the highest security standards while keeping the service simple and privacy-respecting.

Last Internal Security Review: January 1, 2026
All security measures implemented and tested. Zero known vulnerabilities.