mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-21 15:25:49 +00:00
Merge pull request #1 from navaneeth-mysore-sonatype/DI-4993-Demo
Get runtime-agent working with pet clinic tests
This commit is contained in:
commit
969c6d0aba
6 changed files with 235 additions and 11 deletions
1
.java-version
Normal file
1
.java-version
Normal file
|
@ -0,0 +1 @@
|
|||
17
|
79
pom.xml
79
pom.xml
|
@ -4,17 +4,22 @@
|
|||
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>
|
||||
<groupId>org.springframework.samples</groupId>
|
||||
<artifactId>spring-petclinic</artifactId>
|
||||
<artifactId>spring-petclinic-runtime-agent</artifactId>
|
||||
<version>3.2.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<name>petclinic</name>
|
||||
<name>petclinic-runtime-agent</name>
|
||||
|
||||
<properties>
|
||||
<!-- Demo: Custom properties -->
|
||||
<clm.maven.plugin.version>2.45.0-01</clm.maven.plugin.version>
|
||||
<clm.serverUrl>http://ec2-107-23-150-171.compute-1.amazonaws.com:8070/</clm.serverUrl>
|
||||
<clm.serverId>ec2-107-23-150-171.compute-1.amazonaws.com</clm.serverId>
|
||||
<clm.stage>stage-release</clm.stage>
|
||||
|
||||
<!-- Generic properties -->
|
||||
<java.version>17</java.version>
|
||||
|
@ -143,6 +148,65 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Demo: CLM plugin -->
|
||||
<plugin>
|
||||
<groupId>com.sonatype.clm</groupId>
|
||||
<artifactId>clm-maven-plugin</artifactId>
|
||||
<version>${clm.maven.plugin.version}</version>
|
||||
<configuration>
|
||||
<applicationId>${artifactId}</applicationId>
|
||||
<stage>stage-release</stage>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sonatype-prepare</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>evaluate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>sonatype-reevaluate-after-run</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>evaluate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Demo: runtime-agent with tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<argLine>
|
||||
-javaagent:../runtime-agent-1.0.7.jar
|
||||
-Dsonatype.runtime.agent.enabled=true
|
||||
-Dsonatype.runtime.agent.debugMode=false
|
||||
-Dsonatype.runtime.agent.iq.protocol=http
|
||||
-Dsonatype.runtime.agent.iq.host=ec2-107-23-150-171.compute-1.amazonaws.com
|
||||
-Dsonatype.runtime.agent.iq.port=8070
|
||||
-Dsonatype.runtime.agent.iq.user=${env.IQ_USER}
|
||||
-Dsonatype.runtime.agent.iq.password=${env.IQ_PASSWORD}
|
||||
-Dsonatype.runtime.agent.iq.applicationId=spring-petclinic-runtime-agent
|
||||
-Dsonatype.runtime.agent.isIqApplicationIdPublic=true
|
||||
-Dsonatype.runtime.agent.blockedRunOnStartup=true
|
||||
-Dsonatype.runtime.agent.scanClasspath=false
|
||||
-Dsonatype.runtime.agent.fetchVulnerableClassesFromIQ=true
|
||||
-Dsonatype.runtime.agent.vulnerableMethodDetectionEnabled=true
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
|
@ -201,7 +265,8 @@
|
|||
<suppressionsLocation>src/checkstyle/nohttp-checkstyle-suppressions.xml</suppressionsLocation>
|
||||
<sourceDirectories>${basedir}</sourceDirectories>
|
||||
<includes>**/*</includes>
|
||||
<excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
||||
<!-- Demo: The IQ server we use for the demo uses http, so ignore from pom.xml -->
|
||||
<excludes>pom.xml,**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
|
@ -218,7 +283,7 @@
|
|||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Spring Boot Actuator displays build-related information
|
||||
<!-- Spring Boot Actuator displays build-related information
|
||||
if a META-INF/build-info.properties file is present -->
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
|
@ -376,7 +441,7 @@
|
|||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- This plugin's configuration is used to store Eclipse m2e settings
|
||||
<!-- This plugin's configuration is used to store Eclipse m2e settings
|
||||
only. It has no influence on the Maven build itself. -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
|
@ -434,4 +499,4 @@
|
|||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
30
readme.md
30
readme.md
|
@ -6,15 +6,41 @@
|
|||
|
||||
[See the presentation here](https://speakerdeck.com/michaelisvy/spring-petclinic-sample-application)
|
||||
|
||||
## Run Petclinic locally
|
||||
## Run Petclinic with the runtime agent locally
|
||||
|
||||
Spring Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/) or [Gradle](https://spring.io/guides/gs/gradle/). You can build a jar file and run it from the command line (it should work just as well with Java 17 or newer):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/spring-projects/spring-petclinic.git
|
||||
cd spring-petclinic
|
||||
|
||||
# This runs the integration tests which will invoke some vulnerable methods as part of tests coverage and the
|
||||
# runtime labels set in IQ on:
|
||||
# - ch.qos.logback : logback-core : 1.4.11
|
||||
# - ch.qos.logback : logback-classic : 1.4.11
|
||||
# - org.springframework : spring-core : 6.1.1
|
||||
./mvnw package
|
||||
java -jar target/*.jar
|
||||
|
||||
# Note: Change the path the runtime-agent-1.0.7.jar as necessary
|
||||
# After starting the app, by navigating to http://localhost:8080/vets.html invokes vulnerable method call.
|
||||
# This will log additional vulnerable method calls that were not covered by unit tests and sets the runtime labels in IQ.
|
||||
# If the label is already set due to integration tests, it will log as such.
|
||||
# If needed, the labels can be deleted manually in IQ before starting the app.
|
||||
java -javaagent:../runtime-agent-1.0.7.jar \
|
||||
-Dsonatype.runtime.agent.enabled=true \
|
||||
-Dsonatype.runtime.agent.debugMode=false \
|
||||
-Dsonatype.runtime.agent.iq.protocol=http \
|
||||
-Dsonatype.runtime.agent.iq.host=ec2-107-23-150-171.compute-1.amazonaws.com \
|
||||
-Dsonatype.runtime.agent.iq.port=8070 \
|
||||
-Dsonatype.runtime.agent.iq.user=${IQ_USER} \
|
||||
-Dsonatype.runtime.agent.iq.password=${IQ_PASSWORD} \
|
||||
-Dsonatype.runtime.agent.iq.applicationId=spring-petclinic-runtime-agent \
|
||||
-Dsonatype.runtime.agent.isIqApplicationIdPublic=true \
|
||||
-Dsonatype.runtime.agent.blockedRunOnStartup=true \
|
||||
-Dsonatype.runtime.agent.scanClasspath=false \
|
||||
-Dsonatype.runtime.agent.fetchVulnerableClassesFromIQ=true \
|
||||
-Dsonatype.runtime.agent.vulnerableMethodDetectionEnabled=true \
|
||||
-jar target/*.jar
|
||||
```
|
||||
|
||||
You can then access the Petclinic at <http://localhost:8080/>.
|
||||
|
|
|
@ -17,6 +17,8 @@ package org.springframework.samples.petclinic.vet;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
@ -43,6 +45,9 @@ class VetController {
|
|||
|
||||
@GetMapping("/vets.html")
|
||||
public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
|
||||
// Demo: Inject vulnerable method manually.
|
||||
simulateVulnerableMethodCall();
|
||||
|
||||
// Here we are returning an object of type 'Vets' rather than a collection of Vet
|
||||
// objects so it is simpler for Object-Xml mapping
|
||||
Vets vets = new Vets();
|
||||
|
@ -51,6 +56,57 @@ class VetController {
|
|||
return addPaginationModel(page, paginated, model);
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 4 vulnerable method signatures in this application:
|
||||
* 'ch/qos/logback/classic/spi/LoggingEventVO#readObject(Ljava/io/ObjectInputStream;)V
|
||||
* ',
|
||||
* 'org/springframework/core/io/buffer/DefaultDataBuffer#split(I)Lorg/springframework/
|
||||
* core/io/buffer/DataBuffer;
|
||||
* 'org/h2/tools/Backup#process(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;
|
||||
* Z)V
|
||||
* 'ch/qos/logback/core/net/HardenedObjectInputStream#<init>(Ljava/io/InputStream;[
|
||||
* Ljava/lang/String;)V
|
||||
*
|
||||
* This method simulates a vulnerable method call for demo purposes. It seems like no
|
||||
* other code path call any of the vulnerable method, so manually invoking it.
|
||||
*
|
||||
* The following logs will be printed while navigating to
|
||||
* http://localhost:8080/vets.html: Sonatype Runtime Agent - [TIME]: *** Vulnerable
|
||||
* CLASS LOADED [className=org/springframework/core/io/buffer/DefaultDataBuffer] by
|
||||
* the JVM Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Class-Loaded' to
|
||||
* component 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 3d62858ec88e49e0afd552066cb160ad Sonatype Runtime Agent - [TIME]: *** Class with
|
||||
* vulnerable METHOD LOADED
|
||||
* [className=org/springframework/core/io/buffer/DefaultDataBuffer, methodName=split,
|
||||
* methodDescriptor=(I)Lorg/springframework/core/io/buffer/DataBuffer;] by the JVM
|
||||
* Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Method-Loaded' to
|
||||
* component 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 47fa37da85d8447f8c101d4db35ec797 Sonatype Runtime Agent - [TIME]: *** Vulnerable
|
||||
* METHOD CALLED [className=org/springframework/core/io/buffer/DefaultDataBuffer,
|
||||
* methodName=split,
|
||||
* methodDescriptor=(I)Lorg/springframework/core/io/buffer/DataBuffer;] Sonatype
|
||||
* Runtime Agent - [TIME]: Assigning label 'Runtime-Method-Called' to component
|
||||
* 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b Sonatype
|
||||
* Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 6a969f11748f45abba95870fcd7747bb
|
||||
*/
|
||||
private void simulateVulnerableMethodCall() {
|
||||
DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(1024);
|
||||
defaultDataBuffer.split(0);
|
||||
}
|
||||
|
||||
private String addPaginationModel(int page, Page<Vet> paginated, Model model) {
|
||||
List<Vet> listVets = paginated.getContent();
|
||||
model.addAttribute("currentPage", page);
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package org.springframework.samples.petclinic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -26,6 +24,8 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
|||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -37,6 +37,8 @@ import org.testcontainers.containers.MySQLContainer;
|
|||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles("mysql")
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
|
@ -57,8 +59,50 @@ class MySqlIntegrationTests {
|
|||
@Autowired
|
||||
private RestTemplateBuilder builder;
|
||||
|
||||
void simulateVulnerableMethodCall() {
|
||||
/*
|
||||
* Sonatype Runtime Agent - [TIME]: *** Vulnerable CLASS LOADED
|
||||
* [className=org/springframework/core/io/buffer/DefaultDataBuffer] by the JVM
|
||||
* Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Class-Loaded' to
|
||||
* component 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 3d62858ec88e49e0afd552066cb160ad Sonatype Runtime Agent - [TIME]: *** Class
|
||||
* with vulnerable METHOD LOADED
|
||||
* [className=org/springframework/core/io/buffer/DefaultDataBuffer,
|
||||
* methodName=split,
|
||||
* methodDescriptor=(I)Lorg/springframework/core/io/buffer/DataBuffer;] by the JVM
|
||||
* Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Method-Loaded' to
|
||||
* component 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 47fa37da85d8447f8c101d4db35ec797 Sonatype Runtime Agent - [TIME]: ***
|
||||
* Vulnerable METHOD CALLED
|
||||
* [className=org/springframework/core/io/buffer/DefaultDataBuffer,
|
||||
* methodName=split,
|
||||
* methodDescriptor=(I)Lorg/springframework/core/io/buffer/DataBuffer;] Sonatype
|
||||
* Runtime Agent - [TIME]: Assigning label 'Runtime-Method-Called' to component
|
||||
* 22d73bef97aff8a74a99 in application: a50576c3cd894d20b24dc0d98eea084b Sonatype
|
||||
* Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='22d73bef97aff8a74a99'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 6a969f11748f45abba95870fcd7747bb
|
||||
*/
|
||||
DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(1024);
|
||||
defaultDataBuffer.split(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
// Demo: Simulate Runtime-Method-Called
|
||||
simulateVulnerableMethodCall();
|
||||
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.qos.logback.core.net.HardenedObjectInputStream;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -77,8 +78,39 @@ public class PostgresIntegrationTests {
|
|||
.run(args);
|
||||
}
|
||||
|
||||
void simulateVulnerableClassWithMethodLoaded() {
|
||||
/*
|
||||
* Sonatype Runtime Agent - [TIME]: *** Vulnerable CLASS LOADED
|
||||
* [className=ch/qos/logback/core/net/HardenedObjectInputStream] by the JVM
|
||||
* Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Class-Loaded' to
|
||||
* component 2f9f280219a9922a7420 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='2f9f280219a9922a7420'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 74387681c75446a5924812d032c77cad Sonatype Runtime Agent - [TIME]: *** Class
|
||||
* with vulnerable METHOD LOADED
|
||||
* [className=ch/qos/logback/core/net/HardenedObjectInputStream,
|
||||
* methodName=<init>,
|
||||
* methodDescriptor=(Ljava/io/InputStream;[Ljava/lang/String;)V] by the JVM
|
||||
* Sonatype Runtime Agent - [TIME]: Assigning label 'Runtime-Method-Loaded' to
|
||||
* component 2f9f280219a9922a7420 in application: a50576c3cd894d20b24dc0d98eea084b
|
||||
* Sonatype Runtime Agent - [TIME]: Component evaluation for
|
||||
* [ComponentEvaluation{hash='2f9f280219a9922a7420'}] in application
|
||||
* a50576c3cd894d20b24dc0d98eea084b successful. Result
|
||||
* URL=api/v2/evaluation/applications/a50576c3cd894d20b24dc0d98eea084b/results/
|
||||
* 488f999c6730499a8cd454b37d3201b2 >>
|
||||
* org.springframework.samples.petclinic.PostgresIntegrationTests loaded
|
||||
* ch.qos.logback.core.net.HardenedObjectInputStream
|
||||
*/
|
||||
System.out.println(">> " + getClass().getName() + " loaded " + HardenedObjectInputStream.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
// Demo: Simulate Runtime-Class-Loaded and Runtime-Method-Loaded
|
||||
simulateVulnerableClassWithMethodLoaded();
|
||||
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue