diff --git a/.java-version b/.java-version
new file mode 100644
index 000000000..98d9bcb75
--- /dev/null
+++ b/.java-version
@@ -0,0 +1 @@
+17
diff --git a/pom.xml b/pom.xml
index 8a3d6f151..1ab4d96ac 100644
--- a/pom.xml
+++ b/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">
4.0.0
org.springframework.samples
- spring-petclinic
+ spring-petclinic-runtime-agent
3.2.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
- 3.2.1
+ 3.2.0
- petclinic
+ petclinic-runtime-agent
+
+ 2.45.0-01
+ http://ec2-107-23-150-171.compute-1.amazonaws.com:8070/
+ ec2-107-23-150-171.compute-1.amazonaws.com
+ stage-release
17
@@ -143,6 +148,65 @@
+
+
+ com.sonatype.clm
+ clm-maven-plugin
+ ${clm.maven.plugin.version}
+
+ ${artifactId}
+ stage-release
+
+
+
+ sonatype-prepare
+ test-compile
+
+ evaluate
+
+
+
+ sonatype-reevaluate-after-run
+ prepare-package
+
+ evaluate
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+ test
+
+
+
+
+
+ -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
+
+
+
+
+
org.apache.maven.plugins
maven-enforcer-plugin
@@ -201,7 +265,8 @@
src/checkstyle/nohttp-checkstyle-suppressions.xml
${basedir}
**/*
- **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class
+
+ pom.xml,**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class
check
@@ -218,7 +283,7 @@
spring-boot-maven-plugin
-
build-info
@@ -376,7 +441,7 @@
-
org.eclipse.m2e
@@ -434,4 +499,4 @@
-
\ No newline at end of file
+
diff --git a/readme.md b/readme.md
index 79b0fb692..bfd6ff3ee 100644
--- a/readme.md
+++ b/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 .
diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java
index 3240814a6..c096321fb 100644
--- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java
+++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java
@@ -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#(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 paginated, Model model) {
List listVets = paginated.getContent();
model.addAttribute("currentPage", page);
diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java
index 867ef905b..4046589f0 100644
--- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java
+++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java
@@ -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
}
diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java
index 18945a570..4cdad9a2c 100644
--- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java
+++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java
@@ -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=,
+ * 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
}