This demo application partially covers the vulnerability CVE-2024-38828
This commit is contained in:
89
pom.xml
Normal file
89
pom.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.memlastic</groupId>
|
||||
<artifactId>app</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>app</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
||||
13
src/main/java/com/memlastic/App.java
Normal file
13
src/main/java/com/memlastic/App.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.memlastic;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class, args);
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/memlastic/config/WebConfig.java
Normal file
26
src/main/java/com/memlastic/config/WebConfig.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.memlastic.config;
|
||||
|
||||
import com.memlastic.converter.ByteArrayConverter;
|
||||
import com.memlastic.properties.AppConfig;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A configuration class that adapts the context of the spring application. Adds a custom version of the converter.
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
@Autowired
|
||||
private AppConfig appConfig;
|
||||
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.add(0, new ByteArrayConverter(appConfig.getLimit()));
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/memlastic/controller/TestController.java
Normal file
17
src/main/java/com/memlastic/controller/TestController.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.memlastic.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* A simple controller
|
||||
*/
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@PostMapping("/upload")
|
||||
public String upload(@RequestBody byte[] data) {
|
||||
return "Ok";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.memlastic.converter;
|
||||
|
||||
import com.memlastic.exception.DataLimitExceededException;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A converter with improved logic for checking the maximum allowable size of the received file,
|
||||
* which converts incoming content into an array of bytes.
|
||||
*/
|
||||
public class ByteArrayConverter extends AbstractHttpMessageConverter<byte[]> {
|
||||
private final Integer limit;
|
||||
|
||||
public ByteArrayConverter(Integer limit) {
|
||||
super(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return byte[].class == clazz;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] readInternal(Class<? extends byte[]> clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
long contentLength = inputMessage.getHeaders().getContentLength();
|
||||
if (contentLength > limit) {
|
||||
throw new DataLimitExceededException("Exceeding the limit");
|
||||
}
|
||||
ByteArrayOutputStream bos =
|
||||
new ByteArrayOutputStream(contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);
|
||||
StreamUtils.copy(inputMessage.getBody(), bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getContentLength(byte[] bytes, @Nullable MediaType contentType) {
|
||||
return (long) bytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(byte[] bytes, HttpOutputMessage outputMessage) throws IOException {
|
||||
StreamUtils.copy(bytes, outputMessage.getBody());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.memlastic.exception;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DataLimitExceededException extends IOException {
|
||||
public DataLimitExceededException(String text){
|
||||
super(text);
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/memlastic/properties/AppConfig.java
Normal file
21
src/main/java/com/memlastic/properties/AppConfig.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.memlastic.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* A configuration class that sets up file upload limits in mb
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "app")
|
||||
public class AppConfig {
|
||||
private Integer limit = 1;
|
||||
|
||||
@PostConstruct
|
||||
void postConstructor() {
|
||||
limit = limit * 1024;
|
||||
limit = limit * 1024;
|
||||
}
|
||||
}
|
||||
2
src/main/resources/application.yml
Normal file
2
src/main/resources/application.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
app:
|
||||
limit: 1
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.memlastic.controller;
|
||||
|
||||
import com.memlastic.exception.DataLimitExceededException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class TestControllerTest {
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
void testUploadTextPlainSuccess() throws Exception {
|
||||
mockMvc.perform(post("/upload").content("This text message is designed specifically for the test"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("Ok"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadFileSuccess() throws Exception {
|
||||
ClassLoader classLoader = TestControllerTest.class.getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream("smallFile.json");
|
||||
mockMvc.perform(post("/upload").content(inputStream.readAllBytes()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("Ok"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadFileFailed() throws Exception {
|
||||
ClassLoader classLoader = TestControllerTest.class.getClassLoader();
|
||||
InputStream inputStream = classLoader.getResourceAsStream("bigFile.json");
|
||||
mockMvc.perform(post("/upload").content(inputStream.readAllBytes()))
|
||||
.andExpect(result -> assertTrue(result.getResolvedException().fillInStackTrace().getCause() instanceof DataLimitExceededException));
|
||||
|
||||
}
|
||||
}
|
||||
2
src/test/resources/application.yml
Normal file
2
src/test/resources/application.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
app:
|
||||
limit: 1
|
||||
1
src/test/resources/bigFile.json
Normal file
1
src/test/resources/bigFile.json
Normal file
File diff suppressed because one or more lines are too long
30875
src/test/resources/smallFile.json
Normal file
30875
src/test/resources/smallFile.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user