DEV Community

Cover image for Creating a Local XSS Payload Tester Using Node.js
Sam Bishop
Sam Bishop

Posted on

Creating a Local XSS Payload Tester Using Node.js

Cross-Site Scripting (XSS) vulnerabilities remain one of the most common and dangerous flaws in web applications today. Yet many developers don’t get hands-on experience with how they work—until it’s too late. In this article, we’ll build a small, local XSS payload tester using Node.js. The goal is to help you understand how XSS works, test your own input fields, and build safer apps as a result.

Let’s dive in.

🧠 What You’ll Learn

  • What XSS is and how it works
  • How to build a simple Node.js app that reflects user input
  • How to test various XSS payloads in a safe environment
  • Basic techniques to defend against XSS

⚙️ What You’ll Need

  • Node.js installed on your machine
  • Basic knowledge of JavaScript and Express.js
  • A modern browser (Chrome, Firefox, etc.)

📦 Step 1: Set Up Your Project

Let’s start with a basic Express server.

mkdir xss-tester
cd xss-tester
npm init -y
npm install express
Enter fullscreen mode Exit fullscreen mode

Now, create a file called app.js:

const express = require('express');
const app = express();
const port = 3000;

// Enable parsing of POST data
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  const input = req.query.input || '';
  res.send(`
    <h1>XSS Tester</h1>
    <form method="GET" action="/">
      <input name="input" placeholder="Enter some text" />
      <button type="submit">Submit</button>
    </form>
    <p>Output:</p>
    <div>${input}</div>
  `);
});

app.listen(port, () => {
  console.log(`XSS tester running at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Run it using:

node app.js
Enter fullscreen mode Exit fullscreen mode

Then visit: http://localhost:3000

This simple app takes user input from the query string and reflects it back in the response. That’s all it takes to create a basic XSS lab.

⚠️ Step 2: Try Out XSS Payloads

Now that your local tester is running, let’s explore how XSS works. Try entering this into the input field:

<script>alert('XSS')</script>
Enter fullscreen mode Exit fullscreen mode

These work because the application is directly injecting user input into the DOM without sanitization or encoding. In a real-world app, that could lead to session theft, defacement, or more dangerous outcomes.

🛡️ Step 3: Preventing XSS

Now that we’ve seen how easy it is to create a vulnerable app, let’s talk about fixes.

✅ Basic Fix: Escape User Input
Modify the line where user input is rendered:

const escapeHtml = (unsafe) =>
  unsafe
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');

res.send(`
  <h1>XSS Tester</h1>
  <form method="GET" action="/">
    <input name="input" placeholder="Enter some text" />
    <button type="submit">Submit</button>
  </form>
  <p>Output:</p>
  <div>${escapeHtml(input)}</div>
`);
Enter fullscreen mode Exit fullscreen mode

Now when you enter <script>alert('XSS')</script>, it shows up as plain text—no alert, no script execution.

🔐 More Security Tips:

  • Use templating engines (like Pug or Handlebars) which auto-escape by default.
  • Sanitize input using libraries like DOMPurify (on the frontend) or validator libraries on the backend.
  • Apply Content Security Policy (CSP) headers to limit what scripts are allowed to run.

💡 Why Build a Tester Like This?

Many devs read about XSS but never actually see it in action. A simple tool like this helps you:

  • Test real payloads safely, without risking your main app
  • Understand how different contexts (HTML, JavaScript, attributes) behave differently
  • Validate that your security defenses actually work

You can even modify this script to test POST requests or simulate user cookies, making it a great educational sandbox.

🧪 Bonus: Add a POST Version

If you want to test XSS through POST data (not just URL params), update your app like this:

app.get('/post', (req, res) => {
  res.send(`
    <h1>XSS Tester (POST)</h1>
    <form method="POST" action="/post">
      <input name="input" placeholder="Enter some text" />
      <button type="submit">Submit</button>
    </form>
  `);
});

app.post('/post', (req, res) => {
  const input = req.body.input || '';
  res.send(`
    <h1>XSS Tester (POST)</h1>
    <form method="POST" action="/post">
      <input name="input" placeholder="Enter some text" />
      <button type="submit">Submit</button>
    </form>
    <p>Output:</p>
    <div>${input}</div>
  `);
});
Enter fullscreen mode Exit fullscreen mode

Try visiting /post and submitting payloads there too.

🧵 Wrapping Up

Cross-Site Scripting is dangerous precisely because it’s so easy to introduce. Hopefully, building this local XSS payload tester helped you see how XSS works in real time—and more importantly, how to stop it.

As developers, we’re often the first (and last) line of defense. Understanding how vulnerabilities happen isn’t just for security experts—it’s part of writing good code.

Try tweaking the app. Add pages, test escaping logic, or plug it into a template engine. The more you experiment, the more you'll learn.

Top comments (0)