Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

A Spring Boot application containing intentionally vulnerable code patterns for testing application security tools. Each pattern exercises a distinct data-flow complexity, making this a practical benchmark for taint analysis engines.

## Vulnerability Patterns

Intentionally vulnerable patterns, grouped by category:

### XSS Complexity

1. Direct user input return
2. Local variable assignment
3. Inter-procedural flow
4. Constructor chains and field sensitivity
5. Builder pattern and virtual method calls

## Scanning with OpenTaint

Detect vulnerabilities using [OpenTaint](https://opentaint.org/):
Expand Down
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
id("io.spring.dependency-management") version "1.1.6"
}

group = "org.example"
group = "org.seqra"
version = "1.0-SNAPSHOT"

java {
Expand All @@ -18,7 +18,6 @@ repositories {

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/seqra/complexity/DefaultFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.seqra.complexity;

public class DefaultFormatter implements IFormatter {
@Override
public String format(String value) {
return value;
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/seqra/complexity/EscapeFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.seqra.complexity;

import org.springframework.web.util.HtmlUtils;

public class EscapeFormatter implements IFormatter {
@Override
public String format(String value) {
return HtmlUtils.htmlEscape(value);
}
}
27 changes: 27 additions & 0 deletions src/main/java/org/seqra/complexity/HtmlPageBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.seqra.complexity;

import org.springframework.web.util.HtmlUtils;

public class HtmlPageBuilder {

private String message = "";

public HtmlPageBuilder message(String message) {
this.message = message;
return this;
}

public HtmlPageBuilder escape() {
this.message = HtmlUtils.htmlEscape(this.message);
return this;
}

public HtmlPageBuilder format(IFormatter formatter) {
this.message = formatter.format(this.message);
return this;
}

public String buildPage() {
return "<html><body><h1>Profile Message: " + message + "</h1></body></html>";
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/seqra/complexity/IFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.seqra.complexity;

public interface IFormatter {
String format(String value);
}
76 changes: 76 additions & 0 deletions src/main/java/org/seqra/complexity/Profile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.seqra.complexity;

import org.springframework.web.util.HtmlUtils;

public class Profile {
// User profile data structure
public static class UserProfile {
public UserSettings settings;

public UserProfile(UserSettings settings) {
this.settings = settings;
}

public UserProfile(String text) {
this.settings = new UserSettings(text);
}
}

public static class UserSettings {
public NotificationConfig config;

public UserSettings(NotificationConfig config) {
this.config = config;
}

public UserSettings(String text) {
this.config = new NotificationConfig(text);
}
}

public static class NotificationConfig {
public MessageTemplate template;

public NotificationConfig(MessageTemplate template) {
this.template = template;
}

public NotificationConfig(String text) {
this.template = new MessageTemplate(text);
}
}

public static class MessageTemplate {
public MessageBody body;

public MessageTemplate(MessageBody body) {
this.body = body;
}

public MessageTemplate(String text) {
this.body = new MessageBody("<html>" + text + "</html>");
}
}

public static class MessageBody {
public MessageContent content;

public MessageBody(MessageContent content) {
this.content = content;
}

public MessageBody(String text) {
this.content = new MessageContent("<body>" + text + "</body>");
}
}

public static class MessageContent {
public String text;
public String secureText;

public MessageContent(String text) {
this.text = "<h1>Notification: " + text + "</h1>";
this.secureText = "<h1>Notification: " + HtmlUtils.htmlEscape(text) + "</h1>";
}
}
}
171 changes: 171 additions & 0 deletions src/main/java/org/seqra/complexity/UserProfileController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package org.seqra.complexity;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.HtmlUtils;

@Controller
public class UserProfileController {

// Display user profile with custom message
@GetMapping("/profile/display")
@ResponseBody
public String displayUserProfile(
@RequestParam(defaultValue = "Welcome") String message) {
// Direct output without escaping
return "<html><body><h1>Profile Message: " + message + "</h1></body></html>";

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Display user profile with escaped message
@GetMapping("/profile/secureDisplay")
@ResponseBody
public String displaySecureUserProfile(
@RequestParam(defaultValue = "Welcome") String message) {
// Properly escaped output
return "<html><body><h1>Profile Message: " +
HtmlUtils.htmlEscape(message) + "</h1></body></html>";
}

// Display user status with local variable assignment
@GetMapping("/profile/status")
@ResponseBody
public String displayUserStatus(
@RequestParam(defaultValue = "Active") String message) {
// Assign to local variable
String htmlContent = "<html><body><h1>User Status: " +
message + "</h1></body></html>";
return htmlContent;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Display escaped user status with local variable assignment
@GetMapping("/profile/secureStatus")
@ResponseBody
public String displaySecureUserStatus(
@RequestParam(defaultValue = "Active") String message) {
// Assign to local variable
String htmlContent = "<html><body><h1>User Status: " +
HtmlUtils.htmlEscape(message) + "</h1></body></html>";
return htmlContent;
}

// Generate user dashboard with escaped greeting
@GetMapping("/dashboard/greeting")
@ResponseBody
public String generateDashboard(
@RequestParam(defaultValue = "Welcome") String greeting) {
String htmlContent = buildDashboardContent(greeting);
return htmlContent;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

private static String buildDashboardContent(String greeting) {
// Generate dashboard HTML content
return "<html><body><h1>Dashboard: " + greeting + "</h1></body></html>";
}

// Generate user dashboard with custom greeting
@GetMapping("/dashboard/secureGreeting")
@ResponseBody
public String generateSecureDashboard(
@RequestParam(defaultValue = "Welcome") String greeting) {
String htmlContent = buildSecureDashboardContent(greeting);
return htmlContent;
}

private static String buildSecureDashboardContent(String greeting) {
// Generate dashboard HTML content with escaped greeting
return "<html><body><h1>Dashboard: " +
HtmlUtils.htmlEscape(greeting) + "</h1></body></html>";
}

// Generate message template
@GetMapping("/notifications/template")
@ResponseBody
public String generateTemplate(
@RequestParam(defaultValue = "New Message") String content) {
Profile.MessageTemplate template = new Profile.MessageTemplate(content);
// Return nested content
return template.body.content.text;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Generate message template
@GetMapping("/notifications/secureTemplate")
@ResponseBody
public String generateSecureTemplate(
@RequestParam(defaultValue = "New Message") String content) {
Profile.MessageTemplate template = new Profile.MessageTemplate(content);
// Return nested escaped content
return template.body.content.secureText;
}

// Generate user notification with complex data structure
@GetMapping("/notifications/generate")
@ResponseBody
public String generateNotification(
@RequestParam(defaultValue = "New Message") String content) {
// Create user profile with nested message structure using constructors
Profile.UserProfile profile = new Profile.UserProfile(content);

// Return nested content
return profile.settings.config.template.body.content.text;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Generate user notification with complex data structure
@GetMapping("/notifications/secureGenerate")
@ResponseBody
public String generateSecureNotification(
@RequestParam(defaultValue = "New Message") String content) {
// Create user profile with nested message structure using constructors
Profile.UserProfile profile = new Profile.UserProfile(content);

// Return nested content
return profile.settings.config.template.body.content.secureText;
}

// Display custom message
@GetMapping("/message/display")
@ResponseBody
public String displayMessage(
@RequestParam(defaultValue = "Welcome") String message) {
// Construct a page using a chain of builders
String page = new HtmlPageBuilder().message(message).buildPage();

return page;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Display custom message
@GetMapping("/message/secureDisplay")
@ResponseBody
public String displaySecureMessage(
@RequestParam(defaultValue = "Welcome") String message) {
// Construct a page using a chain of builders
String page = new HtmlPageBuilder().message(message).escape().buildPage();

return page;
}

// Display formatted message
@GetMapping("/message/format")
@ResponseBody
public String formatMessage(
@RequestParam(defaultValue = "Welcome") String message) {
// Construct a page using a formatter as a parameter for a chain of builders
String page = new HtmlPageBuilder().message(message)
.format(new DefaultFormatter()).buildPage();

return page;

Check failure

Code scanning / OpenTaint

Potential cross-site scripting (XSS) Error

Potential XSS: writing user input directly to a web page.
}

// Display escaped message
@GetMapping("/message/escape")
@ResponseBody
public String escapeMessage(
@RequestParam(defaultValue = "Welcome") String message) {
// Construct a page using a formatter as a parameter for a chain of builders
String page = new HtmlPageBuilder().message(message)
.format(new EscapeFormatter()).buildPage();

return page;
}
}
Loading