From 2cd58b537a37eeb607e7ae3e408f6583bc98dad4 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Wed, 25 Mar 2026 16:15:48 +0300 Subject: [PATCH] Add XSS with different complexity levels This reverts commit e083d9e36b074185b22f13bd2267ceda269a203c. --- README.md | 12 ++ build.gradle.kts | 3 +- .../seqra/complexity/DefaultFormatter.java | 8 + .../org/seqra/complexity/EscapeFormatter.java | 10 + .../org/seqra/complexity/HtmlPageBuilder.java | 27 +++ .../java/org/seqra/complexity/IFormatter.java | 5 + .../java/org/seqra/complexity/Profile.java | 76 ++++++++ .../complexity/UserProfileController.java | 171 ++++++++++++++++++ 8 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/seqra/complexity/DefaultFormatter.java create mode 100644 src/main/java/org/seqra/complexity/EscapeFormatter.java create mode 100644 src/main/java/org/seqra/complexity/HtmlPageBuilder.java create mode 100644 src/main/java/org/seqra/complexity/IFormatter.java create mode 100644 src/main/java/org/seqra/complexity/Profile.java create mode 100644 src/main/java/org/seqra/complexity/UserProfileController.java diff --git a/README.md b/README.md index 9e9e2f3..dad291d 100644 --- a/README.md +++ b/README.md @@ -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/): diff --git a/build.gradle.kts b/build.gradle.kts index 5357967..46960b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 { @@ -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") diff --git a/src/main/java/org/seqra/complexity/DefaultFormatter.java b/src/main/java/org/seqra/complexity/DefaultFormatter.java new file mode 100644 index 0000000..548a7d9 --- /dev/null +++ b/src/main/java/org/seqra/complexity/DefaultFormatter.java @@ -0,0 +1,8 @@ +package org.seqra.complexity; + +public class DefaultFormatter implements IFormatter { + @Override + public String format(String value) { + return value; + } +} diff --git a/src/main/java/org/seqra/complexity/EscapeFormatter.java b/src/main/java/org/seqra/complexity/EscapeFormatter.java new file mode 100644 index 0000000..2ec1fca --- /dev/null +++ b/src/main/java/org/seqra/complexity/EscapeFormatter.java @@ -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); + } +} diff --git a/src/main/java/org/seqra/complexity/HtmlPageBuilder.java b/src/main/java/org/seqra/complexity/HtmlPageBuilder.java new file mode 100644 index 0000000..1f9b2b3 --- /dev/null +++ b/src/main/java/org/seqra/complexity/HtmlPageBuilder.java @@ -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 "

Profile Message: " + message + "

"; + } +} diff --git a/src/main/java/org/seqra/complexity/IFormatter.java b/src/main/java/org/seqra/complexity/IFormatter.java new file mode 100644 index 0000000..dbbc92d --- /dev/null +++ b/src/main/java/org/seqra/complexity/IFormatter.java @@ -0,0 +1,5 @@ +package org.seqra.complexity; + +public interface IFormatter { + String format(String value); +} diff --git a/src/main/java/org/seqra/complexity/Profile.java b/src/main/java/org/seqra/complexity/Profile.java new file mode 100644 index 0000000..7ab5827 --- /dev/null +++ b/src/main/java/org/seqra/complexity/Profile.java @@ -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("" + text + ""); + } + } + + public static class MessageBody { + public MessageContent content; + + public MessageBody(MessageContent content) { + this.content = content; + } + + public MessageBody(String text) { + this.content = new MessageContent("" + text + ""); + } + } + + public static class MessageContent { + public String text; + public String secureText; + + public MessageContent(String text) { + this.text = "

Notification: " + text + "

"; + this.secureText = "

Notification: " + HtmlUtils.htmlEscape(text) + "

"; + } + } +} diff --git a/src/main/java/org/seqra/complexity/UserProfileController.java b/src/main/java/org/seqra/complexity/UserProfileController.java new file mode 100644 index 0000000..105e1f3 --- /dev/null +++ b/src/main/java/org/seqra/complexity/UserProfileController.java @@ -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 "

Profile Message: " + message + "

"; + } + + // Display user profile with escaped message + @GetMapping("/profile/secureDisplay") + @ResponseBody + public String displaySecureUserProfile( + @RequestParam(defaultValue = "Welcome") String message) { + // Properly escaped output + return "

Profile Message: " + + HtmlUtils.htmlEscape(message) + "

"; + } + + // 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 = "

User Status: " + + message + "

"; + return htmlContent; + } + + // 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 = "

User Status: " + + HtmlUtils.htmlEscape(message) + "

"; + 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; + } + + private static String buildDashboardContent(String greeting) { + // Generate dashboard HTML content + return "

Dashboard: " + greeting + "

"; + } + + // 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 "

Dashboard: " + + HtmlUtils.htmlEscape(greeting) + "

"; + } + + // 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; + } + + // 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; + } + + // 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; + } + + // 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; + } + + // 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; + } +}