Skip to content

Commit 36f6bae

Browse files
committed
Add XSS protection example
1 parent 2124b12 commit 36f6bae

File tree

11 files changed

+194
-0
lines changed

11 files changed

+194
-0
lines changed

web-thymeleaf-xss/.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/gradlew text eol=lf
2+
*.bat text eol=crlf
3+
*.jar binary

web-thymeleaf-xss/.gitignore

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
HELP.md
2+
.gradle
3+
build/
4+
!gradle/wrapper/gradle-wrapper.jar
5+
!**/src/main/**/build/
6+
!**/src/test/**/build/
7+
8+
### STS ###
9+
.apt_generated
10+
.classpath
11+
.factorypath
12+
.project
13+
.settings
14+
.springBeans
15+
.sts4-cache
16+
bin/
17+
!**/src/main/**/bin/
18+
!**/src/test/**/bin/
19+
20+
### IntelliJ IDEA ###
21+
.idea
22+
*.iws
23+
*.iml
24+
*.ipr
25+
out/
26+
!**/src/main/**/out/
27+
!**/src/test/**/out/
28+
29+
### NetBeans ###
30+
/nbproject/private/
31+
/nbbuild/
32+
/dist/
33+
/nbdist/
34+
/.nb-gradle/
35+
36+
### VS Code ###
37+
.vscode/

web-thymeleaf-xss/build.gradle.kts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
plugins {
2+
java
3+
id("org.springframework.boot") version "3.4.5"
4+
id("io.spring.dependency-management") version "1.1.7"
5+
}
6+
7+
group = "zin.rashidi"
8+
version = "0.0.1-SNAPSHOT"
9+
10+
java {
11+
toolchain {
12+
languageVersion = JavaLanguageVersion.of(21)
13+
}
14+
}
15+
16+
repositories {
17+
mavenCentral()
18+
}
19+
20+
dependencies {
21+
implementation("org.springframework.boot:spring-boot-starter-security")
22+
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
23+
implementation("org.springframework.boot:spring-boot-starter-web")
24+
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
25+
testImplementation("org.springframework.boot:spring-boot-starter-test")
26+
testImplementation("org.springframework.security:spring-security-test")
27+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
28+
}
29+
30+
tasks.withType<Test> {
31+
useJUnitPlatform()
32+
}

web-thymeleaf-xss/settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = "web-thymeleaf-xss"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package zin.rashidi.web.xss;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class WebThymeleafXssApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(WebThymeleafXssApplication.class, args);
11+
}
12+
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package zin.rashidi.web.xss.greet;
2+
3+
import org.springframework.stereotype.Controller;
4+
import org.springframework.ui.Model;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.RequestParam;
7+
8+
/**
9+
* @author Rashidi Zin
10+
*/
11+
@Controller
12+
class GreetResource {
13+
14+
@GetMapping("/greet")
15+
public String greet(@RequestParam String name, Model model) {
16+
model.addAttribute("name", name);
17+
18+
return "greet";
19+
}
20+
21+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package zin.rashidi.web.xss.security;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7+
import org.springframework.security.web.SecurityFilterChain;
8+
9+
import static org.springframework.security.web.header.writers.XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK;
10+
11+
/**
12+
* @author Rashidi Zin
13+
*/
14+
@Configuration
15+
@EnableWebSecurity
16+
class SecurityConfiguration {
17+
18+
@Bean
19+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
20+
return http
21+
.headers(headers -> headers
22+
.contentSecurityPolicy(policy -> policy.policyDirectives("default-src 'self'"))
23+
.xssProtection(xss -> xss.headerValue(ENABLED_MODE_BLOCK))
24+
)
25+
.build();
26+
}
27+
28+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spring.application.name=web-thymeleaf-xss
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5+
<title>Greeting</title>
6+
</head>
7+
<body>
8+
<p th:id="greet" th:text="|Hello, ${name}!|" />
9+
</body>
10+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package zin.rashidi.web.xss;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.boot.test.context.SpringBootTest;
5+
6+
@SpringBootTest
7+
class WebThymeleafXssApplicationTests {
8+
9+
@Test
10+
void contextLoads() {
11+
}
12+
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package zin.rashidi.web.xss.greet;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.springframework.test.web.servlet.assertj.MockMvcTester;
9+
10+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
11+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
12+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
13+
14+
/**
15+
* @author Rashidi Zin
16+
*/
17+
@SpringBootTest(webEnvironment = RANDOM_PORT)
18+
@AutoConfigureMockMvc
19+
class GreetResourceTests {
20+
21+
@Autowired
22+
private MockMvcTester mvc;
23+
24+
@Test
25+
@DisplayName("Given XSS protection is enabled Then response header should contain information about X-XSS-Protection and Content-Security-Policy")
26+
void headers() {
27+
mvc.get()
28+
.uri("/greet?name={name}", "rashidi")
29+
.assertThat()
30+
.matches(status().isOk())
31+
.matches(header().string("Content-Security-Policy", "default-src 'self'"))
32+
.matches(header().string("X-XSS-Protection", "1; mode=block"));
33+
}
34+
35+
}

0 commit comments

Comments
 (0)