or in URL parameters and form fields. The response should contain the encoded form (<script>) not the literal tag — and no alert should fire. Stored XSS: the payload is saved to the database and later rendered for other users. Test by saving a XSS payload in a user-supplied field (comment, username, address), then loading the page where that content is displayed. If the alert fires, the content is being rendered unencoded. DOM-based XSS: the payload never reaches the "}}]}

Q6 of 24 · Security

What is cross-site scripting (XSS) and how do you test that an application prevents it?

SecurityMidsecurityxssinjectionowasptestingmid

Short answer

Short answer: XSS occurs when user-supplied content is rendered in the browser without encoding, allowing injected JavaScript to execute in another user's session. Test by submitting XSS payloads in text inputs and stored fields, then verify the payload is HTML-encoded in the output — not executed as a script.

Detail

Three types with different test approaches:

Reflected XSS: the payload is in the request (URL parameter, form field) and echoed back in the response. Test by submitting <script>alert(1)</script> or <img src=x onerror=alert(1)> in URL parameters and form fields. The response should contain the encoded form (&lt;script&gt;) not the literal tag — and no alert should fire.

Stored XSS: the payload is saved to the database and later rendered for other users. Test by saving a XSS payload in a user-supplied field (comment, username, address), then loading the page where that content is displayed. If the alert fires, the content is being rendered unencoded.

DOM-based XSS: the payload never reaches the server — it's processed by client-side JavaScript that reads from location.hash, document.referrer, or localStorage and writes to innerHTML or similar sinks. Test by crafting URLs with payloads in the fragment identifier (#<img src=x onerror=alert(1)>) and observing whether the script executes.

Testing approach: use a diverse set of payloads because simple payloads are commonly blocked while more obscure ones are not. Test both the content-type of the response (HTML vs JSON — JSON responses should have Content-Type: application/json, preventing the browser from rendering them as HTML) and the actual rendered output. CSP violations in the browser console are a signal that a payload was attempted even if blocked.

// EXAMPLE

xss.test.ts

const xssPayloads = [
  '<script>alert(1)</script>',
  '<img src=x onerror=alert(1)>',
  '"><svg onload=alert(1)>',
];

for (const payload of xssPayloads) {
  await page.goto(`/search?q=${encodeURIComponent(payload)}`);
  // The rendered page should show encoded text, not execute the script
  const bodyText = await page.locator('body').innerText();
  expect(bodyText).not.toContain('alert(1)');  // if executed, would have run
  const bodyHTML = await page.locator('body').innerHTML();
  expect(bodyHTML).not.toContain('<script>');  // must be encoded
}

// WHAT INTERVIEWERS LOOK FOR

Distinguishes reflected, stored, and DOM XSS as separate test scenarios. Verifies encoding in the output — not just absence of an alert dialog. Knows CSP as a complementary defence.

// COMMON PITFALL

Only testing if an alert dialog fires. The correct assertion is that the payload is HTML-encoded in the output. An alert firing is confirmation of failure, but absence of an alert doesn't confirm the input was properly sanitised.