Merge pull request #1 from navaneeth-mysore-sonatype/DI-4993-Demo

Get runtime-agent working with pet clinic tests
This commit is contained in:
Navaneeth Mysore Govindarajan 2024-02-22 12:36:57 -05:00 committed by GitHub
commit 969c6d0aba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 235 additions and 11 deletions

1
.java-version Normal file
View file

@ -0,0 +1 @@
17

79
pom.xml
View file

@ -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>

View file

@ -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/>.

View file

@ -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);

View file

@ -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
}

View file

@ -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
}