Code-1

Vulnerable to Xss. Code is taken from PentesterLabs Java code review 05.

Code

Whole code:

Structure

vulnerable/
├── mvnw.cmd
├── pom.xml
├── .gitignore
├── .mvn/
│   └── wrapper/
│       ├── maven-wrapper.properties
│       └── maven-wrapper.jar
├── mvnw
├── src/
│   ├── test/
│   │   └── java/
│   │       └── com/
│   │           └── pentesterlab/
│   │               └── vulnerable/
│   │                   └── VulnerableApplicationTests.java
│   ├── main/
│   │   ├── resources/
│   │   │   ├── static/
│   │   │   │   ├── css/
│   │   │   │   │   ├── bootstrap.css
│   │   │   │   │   └── pentesterlab.css
│   │   │   ├── templates/
│   │   │   │   ├── index.html
│   │   │   │   └── fragments.html
│   │   │   └── application.properties
│   │   └── java/
│   │       └── com/
│   │           └── pentesterlab/
│   │               └── vulnerable/
│   │                   └── controller/
│   │                       └── MainController.java
│   │                   └── VulnerableApplication.java

VulnerableApplication.java

package com.pentesterlab.vulnerle;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class VulnerableApplication {

	public static void main(String[] args) {
		SpringApplication.run(VulnerableApplication.class, args);
	}

}

MainController.java

package com.pentesterlab.vulnerable.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MainController {
    @GetMapping("/")
    public String showHomePage(HttpServletRequest request, Model model) {
        String name = request.getParameter("name");
        if (name != null) {
            model.addAttribute("name", name);
        }
        return "index";
    }
}

Index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments :: header}">
</head>
<body>
<div class="container">
  <div class="header" th:replace="~{fragments :: menu}">
  </div>

  <div class="row">
    <div class="col-lg-12">
      <h1>Welcome</h1>
      <p th:utext="'Hello, ' + ${name} + '!'" />
      <p>You can start by personalising the page with your name by using <a th:href="@{/?name=hacker}">this link</a></p>
    </div>

    <div class="col-lg-12" th:replace="~{fragments :: footer}">
    </div>
  </div>
</div>
</body>
</html>

First we need to learn this:

SpringApplication.run(...) does:

  1. Builds an ApplicationContext (the Spring IoC container).

  2. Scans for components (@Controller, @Service, @Component, @Repository, @Configuration, etc.).

  3. Instantiates those bean classes (calling their constructors) and performs dependency injection.

  4. Calls lifecycle callbacks (e.g., @PostConstruct, InitializingBean.afterPropertiesSet()), and runs any CommandLineRunner beans after the context is ready.

So the constructors for beans are invoked during context startup, not because main() called them directly.

  • Spring will instantiate MainController at startup (constructor called then).

  • showHomePage(...) will only be invoked when a client requests GET / — e.g., the user opens http://host/?name=alice.

  • The controller’s constructor is not what invokes showHomePage. So handlers don't run automatically.

Vulnerability

  1. The controller reads user input directly from the request:

String name = request.getParameter("name");
if (name != null) {
    model.addAttribute("name", name);
}
  1. The Thymeleaf template uses th:utext:

<p th:utext="'Hello, ' + ${name} + '!'" />
  • th:utext inserts unescaped HTML into the page. If name contains HTML or script, it will be interpreted by the browser.

  1. A user can craft a URL such as:

http://app/?name=<script>alert(1)</script>

Fix

Template Side fix:

<!-- vulnerable -->
-<p th:utext="'Hello, ' + ${name} + '!'" />

+<!-- safe -->
+<p th:text="'Hello, ' + ${name} + '!'" />

th:text escapes HTML entities so "<script>" becomes &lt;script&gt;, so it is shown as text, not executed.

2) Server-side defense (defense-in-depth)

If you want extra safety or are rendering data in other contexts, escape/sanitize on the server:

Escape HTML (Spring utility)

import org.springframework.web.util.HtmlUtils;

String safe = HtmlUtils.htmlEscape(name);
model.addAttribute("name", safe);

or sanitize to allow only safe HTML (if you specifically intend to allow limited HTML) using JSoup:

import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;

String safe = Jsoup.clean(name, Safelist.basic()); // allow only a small, safe subset
model.addAttribute("name", safe);

Last updated