Skip to content

Quiz: Spot the Vulnerability

intermediate12 min read

Think Like an Attacker

Security bugs hide in code that looks completely normal. The function works, the tests pass, the feature ships. Then someone crafts a URL, sends a message, or pollutes a prototype — and your app is compromised.

This quiz trains the one skill that separates secure engineers from everyone else: reading code with adversarial eyes. For every snippet below, assume an attacker is watching and ask yourself: what would they do with this?

Mental Model

Every piece of user-controlled data is a loaded weapon. URLs, query params, form inputs, cookies, postMessage events, imported JSON — treat them all as hostile until proven safe. The security mindset is simple: never trust, always validate, minimize blast radius. Before you write any code that touches external data, ask three questions: Where does this data come from? What is the worst thing someone could put in it? What happens if they do?


Question 1: The Rendering Shortcut

function renderUserBio(bio) {
  const container = document.getElementById('bio');
  container.innerHTML = bio;
}

// Called with data from the server:
renderUserBio(userData.bio);
Quiz
What is the vulnerability in this code?

Question 2: The Open Door

// server.js
app.use(cors({
  origin: true,
  credentials: true,
}));
Quiz
What is wrong with this CORS configuration?

Question 3: The Convenient Storage

// After successful login
function handleLogin(response) {
  const { token } = response.data;
  localStorage.setItem('auth_token', token);
}

// On every API call
function getAuthHeader() {
  return {
    Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
  };
}
Quiz
What is the primary security concern with storing JWTs this way?

Question 4: The Missing Header

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My Banking App</title>
  <link rel="stylesheet" href="/styles.css" />
</head>
Quiz
What critical security measure is missing from this page?

Question 5: The Innocent Merge

function deepMerge(target, source) {
  for (const key in source) {
    if (typeof source[key] === 'object' && source[key] !== null) {
      if (!target[key]) target[key] = {};
      deepMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

// Merging user preferences from an API response
const config = deepMerge(defaults, userPreferences);
Quiz
What vulnerability does this deepMerge function introduce?

// Express.js session setup
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    secure: true,
    maxAge: 24 * 60 * 60 * 1000,
  },
}));
Quiz
What security attribute is missing from this cookie configuration?

Question 7: The Trusting Listener

// In your app's iframe communication handler
window.addEventListener('message', (event) => {
  const { action, payload } = event.data;

  if (action === 'updateProfile') {
    updateUserProfile(payload);
  }

  if (action === 'deleteAccount') {
    deleteUserAccount();
  }
});
Quiz
What makes this postMessage handler dangerous?

Question 8: The Dynamic Evaluator

function calculateUserFormula(formula) {
  return eval(formula);
}

// Used in a spreadsheet-like feature:
const result = calculateUserFormula(cellInput.value);
Quiz
Why is this code a critical security vulnerability?

// React component
function SearchResults() {
  const params = new URLSearchParams(window.location.search);
  const query = params.get('q') || '';

  return (
    <div>
      <h1>Results for: {query}</h1>
      <p dangerouslySetInnerHTML={{ __html: highlightMatches(query) }} />
    </div>
  );
}
Quiz
Where is the XSS vulnerability in this component?

Question 10: The Leaky Redirect

function handleOAuthCallback() {
  const params = new URLSearchParams(window.location.search);
  const token = params.get('token');
  const redirect = params.get('redirect') || '/dashboard';

  if (token) {
    sessionStorage.setItem('token', token);
    window.location.href = redirect;
  }
}
Quiz
What is the vulnerability in this redirect logic?

Question 11: The Shared Secret

// config.js — bundled with the frontend
const config = {
  apiUrl: 'https://api.myapp.com',
  apiKey: 'sk_live_a1b2c3d4e5f6',
  stripePublishableKey: 'pk_live_x9y8z7',
  databaseUrl: 'postgres://admin:password@db.myapp.com:5432/prod',
};

export default config;
Quiz
What is the most critical security issue in this configuration file?

Question 12: The Template Builder

function renderTemplate(template, data) {
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
    return data[key] || '';
  });
}

// User creates custom email templates
const html = renderTemplate(userTemplate, {
  name: userName,
  email: userEmail,
});
document.getElementById('preview').innerHTML = html;
Quiz
This code has multiple security issues. What is the primary vulnerability?

Scoring Guide

ScoreAssessment
11-12You think like an attacker. Exceptional security instincts.
8-10Strong foundations. Review the ones you missed — each gap is a real attack vector.
5-7You know the common ones but miss subtler issues. Study OWASP Top 10 for frontend.
0-4Security needs focused attention. Start with XSS and CSRF — they account for most real-world frontend attacks.

The Security Checklist Mindset

Key Rules
  1. 1Never trust user input — validate and sanitize at every boundary, even data from your own server
  2. 2Never use innerHTML, eval, or dangerouslySetInnerHTML with unsanitized data
  3. 3Store secrets on the server only — anything in the client bundle is public
  4. 4Always validate event.origin in postMessage handlers and redirect URLs
  5. 5Set all cookie security attributes: HttpOnly, Secure, SameSite, and appropriate expiration
  6. 6Deploy a strict Content Security Policy as your last line of defense against XSS
  7. 7Guard deep merge and object spread operations against prototype pollution
  8. 8Whitelist CORS origins explicitly — never reflect arbitrary origins with credentials
What developers doWhat they should do
Using innerHTML to render dynamic content because it is simpler than DOM APIs
innerHTML parses and executes embedded scripts and event handlers, enabling XSS
Use textContent for plain text, or sanitize with DOMPurify before innerHTML
Storing JWTs in localStorage because cookies feel old-fashioned
localStorage is readable by any script on the page — one XSS and the token is stolen
Use HttpOnly Secure SameSite cookies for authentication tokens
Setting CORS origin to true or * with credentials for convenience during development
Any website can make authenticated requests to your API and read the response
Whitelist specific trusted origins and never ship wildcard credentials
Trusting postMessage events without checking origin
Any window with a reference to yours can send messages with arbitrary data
Always validate event.origin against a whitelist before processing the message
Putting API secret keys in frontend config files or environment variables without the server-only prefix
The entire client bundle is visible in browser DevTools — secrets are not secret
Keep secrets in server-only env vars and never import them in client code