Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
David Archer committed Sep 4, 2020
1 parent c535d47 commit 177bea1
Show file tree
Hide file tree
Showing 18 changed files with 443 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contrast.jar
terraform.tfstate
terraform.tfstate.backup
.terraform
contrast_security.yaml
.terraform.tfstate.lock.info
node_modules
.idea
package-lock.json
terraform.tfvars
3 changes: 3 additions & 0 deletions 1-Build-Docker-Image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

docker build . -t spring-petclinic:1.5.1 --no-cache
6 changes: 6 additions & 0 deletions 2-Deploy-Docker-Image-To-Docker-Hub.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

echo "Please log in using your Docker Hub credentials to update the container image"
docker login
docker tag spring-petclinic:1.5.1 contrastsecuritydemo/spring-petclinic:1.5.1
docker push contrastsecuritydemo/spring-petclinic:1.5.1
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM openjdk:8-jre-alpine
RUN mkdir /spring-petclinic
WORKDIR /spring-petclinic

#Add application
ADD ./spring-petclinic-1.5.1.jar /spring-petclinic/spring-petclinic-1.5.1.jar

#Add Contrast
RUN mkdir /opt/contrast
RUN apk --no-cache add curl
RUN curl --fail --silent --location "https://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.contrastsecurity&a=contrast-agent&v=LATEST" -o /opt/contrast/contrast.jar

#Enable Contrast
ENV JAVA_TOOL_OPTIONS='-javaagent:/opt/contrast/contrast.jar -Dcontrast.agent.java.standalone_app_name=spring-petclinic'

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "spring-petclinic-1.5.1.jar"]
87 changes: 87 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
env.terraform_version = '0.12.3'

pipeline {
agent any

stages {
stage('dependencies') {
steps {
sh """
FILE=/usr/bin/terraform
if [ -f "\$FILE" ]; then
echo "\$FILE exists, skipping download"
else
echo "\$FILE does not exist"
cd /tmp
curl -o terraform.zip https://releases.hashicorp.com/terraform/'$terraform_version'/terraform_'$terraform_version'_linux_amd64.zip
unzip -o terraform.zip
sudo mv terraform /usr/bin
rm -rf terraform.zip
fi
"""
script {
withCredentials([file(credentialsId: env.contrast_yaml, variable: 'path')]) {
def contents = readFile(env.path)
writeFile file: 'contrast_security.yaml', text: "$contents"
}
}
sh """
terraform init
npm i puppeteer
"""
}
}
stage('provision') {
steps {
script {
env.GIT_SHORT_COMMIT = checkout(scm).GIT_COMMIT.take(7)
env.GIT_BRANCH = checkout(scm).GIT_BRANCH

withCredentials([azureServicePrincipal('ContrastAzureSponsored')]) {
try {
sh """
export ARM_CLIENT_ID=$AZURE_CLIENT_ID
export ARM_CLIENT_SECRET=$AZURE_CLIENT_SECRET
export ARM_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID
export ARM_TENANT_ID=$AZURE_TENANT_ID
terraform apply -auto-approve -var 'location=$location' -var 'initials=$initials' -var 'environment=qa' -var 'servername=jenkins' -var 'session_metadata="branchName=${env.GIT_BRANCH},buildNumber=${BUILD_NUMBER},commitHash=${env.GIT_SHORT_COMMIT},version=1.5.1"'
"""
} catch (Exception e) {
echo "Terraform refresh failed, deleting state"
sh "rm -rf terraform.tfstate"
currentBuild.result = "FAILURE"
error("Aborting the build.")
}
}
}
}
}
stage('sleeping') {
steps {
sleep 120
}
}
stage('exercise') {
steps {
timeout(20) {
sh """
FQDN=\$(terraform output fqdn)
BASEURL=\$FQDN node exercise.js
"""
}
}
}
stage('destroy') {
steps {
withCredentials([azureServicePrincipal('ContrastAzureSponsored')]) {
sh """export ARM_CLIENT_ID=$AZURE_CLIENT_ID
export ARM_CLIENT_SECRET=$AZURE_CLIENT_SECRET
export ARM_SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID
export ARM_TENANT_ID=$AZURE_TENANT_ID
terraform destroy -auto-approve"""
}
}
}
}
}
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
# demo-petclinic
# Spring PetClinic: A deliberately insecure Java web application

This sample application is based on https://github.com/Contrast-Security-OSS/spring-petclinic

**Warning**: The computer running this application will be vulnerable to attacks, please take appropriate precautions.

# Running standalone

You can run PetClinic locally on any machine with Java 1.8 RE installed.

1. Place a `contrast_security.yaml` file into the application's root folder.
1. Place a `contrast.jar` into the application's root folder.
1. Run the application using:
```sh
java -javaagent:contrast.jar -Dcontrast.config.path=contrast_security.yaml -Dcontrast.agent.java.standalone_app_name=spring-petclinic -jar spring-petclinic-1.5.1.jar [--server.port=8080] [--server.address=localhost]
```
1. Browse the application at http://localhost:8080/

# Running in Docker

You can run PetClinic within a Docker container.

1. Place a `contrast_security.yaml` file into the application's root folder.
1. Build the PetClinic container image using `./1-Build-Docker-Image.sh`. The Contrast agent is added automatically during the Docker build process.
1. Run the container using `docker run -v $PWD/contrast_security.yaml:/etc/contrast/java/contrast_security.yaml -p 8080:8080 spring-petclinic:1.5.1`
1. Browse the application at http://localhost:8080/

# Running in Azure (Azure Container Instance):

## Pre-Requisites

1. Place a `contrast_security.yaml` file into the application's root folder.
1. Install Terraform from here: https://www.terraform.io/downloads.html.
1. Install PyYAML using `pip install PyYAML`.
1. Install the Azure cli tools using `brew update && brew install azure-cli`.
1. Log into Azure to make sure you cache your credentials using `az login`.
1. Edit the [variables.tf](variables.tf) file (or add a terraform.tfvars) to add your initials, preferred Azure location, app name, server name and environment.
1. Run `terraform init` to download the required plugins.
1. Run `terraform plan` and check the output for errors.
1. Run `terraform apply` to build the infrastructure that you need in Azure, this will output the web address for the application.
1. Run `terraform destroy` when you would like to stop the app service and release the resources.

# Running automated tests

There is a test script which you can use to reveal vulnerabilities which requires node and puppeteer.

1. Install Node, NPM and Chrome.
1. From the app folder run `npm i puppeteer`.
1. Run `BASEURL=https://<your service name>.azurewebsites.net node exercise.js` or `BASEURL=https://<your service name>.azurewebsites.net DEBUG=true node exercise.js` to watch the automated script.

## Updating the Docker Image

You can re-build the docker image (used by Terraform) by running two scripts in order:

* 1-Build-Docker-Image.sh
* 2-Deploy-Docker-Image-To-Docker-Hub.sh
78 changes: 78 additions & 0 deletions exercise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const puppeteer = require('puppeteer');

(async () => {
if (!process.env.BASEURL) {
console.log('Please specify a base url. E.g. `BASEURL=http://example.org node exercise.js`');
} else {
var browser;

if (process.env.DEBUG) {
browser = await puppeteer.launch({
headless: false,
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
});
} else {
browser = await puppeteer.launch();
}

const sqliPayload = "D' OR '1%'='1"

//home page

console.log('visiting home page')
const page = await browser.newPage()
await page.goto(process.env.BASEURL)
await page.waitFor(2000)

//exercising sqli vulnerability
console.log('exercising sqli vulnerability')
const page2 = await browser.newPage()
await page2.goto(process.env.BASEURL + '/owners/find')
await page2.waitFor(2000)
await page2.focus('#lastName.form-control')
await page2.keyboard.type('Davis');
await page2.waitFor(2000)
await page2.click('button.btn.btn-default')
await page2.waitFor(2000)

//attacking sqli vulnerability

console.log('attacking sqli vulnerability')
const page3 = await browser.newPage()
await page3.goto(process.env.BASEURL + '/owners/find')
await page3.waitFor(2000)
await page3.focus('#lastName.form-control')
await page3.keyboard.type(sqliPayload);
await page3.waitFor(2000)
await page3.click('button.btn.btn-default')
await page3.waitFor(2000)

//vets
console.log('visiting vets')
const page4 = await browser.newPage()
await page4.goto(process.env.BASEURL + '/vets.html')
await page4.waitFor(2000)

//owners
console.log('visiting owners')
const page5 = await browser.newPage()
await page5.goto(process.env.BASEURL + '/owners')
await page5.waitFor(2000)

// edit owner

console.log('editing an owner')
const page6 = await browser.newPage()
await page6.goto(process.env.BASEURL + '/owners/1/edit')
await page6.waitFor(2000)
await page6.evaluate( () => document.getElementById("firstName").value = "David")
await page6.waitFor(2000)
await page6.click('button.btn.btn-default')
await page6.waitFor(2000)



browser.close()
console.log('End')
}
})()
40 changes: 40 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#Terraform `provider` section is required since the `azurerm` provider update to 2.0+
provider "azurerm" {
features {}
}

#Extract the connection from the normal yaml file to pass to the app container
data "external" "yaml" {
program = [var.python_binary, "${path.module}/parseyaml.py"]
}

#Set up a personal resource group for the SE local to them
resource "azurerm_resource_group" "personal" {
name = "Sales-Engineer-${var.initials}"
location = var.location
}

#Set up a container group
resource "azurerm_container_group" "app" {
name = "${var.appname}-${var.initials}"
location = azurerm_resource_group.personal.location
resource_group_name = azurerm_resource_group.personal.name
ip_address_type = "public"
dns_name_label = "${replace(var.appname, "/[^-0-9a-zA-Z]/", "-")}-${var.initials}"
os_type = "linux"

container {
name = "web"
image = "contrastsecuritydemo/spring-petclinic:1.5.1"
cpu = "1"
memory = "1.5"
ports {
port = 8080
protocol = "TCP"
}
environment_variables = {
JAVA_TOOL_OPTIONS = "-javaagent:/opt/contrast/contrast.jar -Dcontrast.agent.java.standalone_app_name=spring-petclinic -Dcontrast.api.url=${data.external.yaml.result.url} -Dcontrast.api.api_key=${data.external.yaml.result.api_key} -Dcontrast.api.service_key=${data.external.yaml.result.service_key} -Dcontrast.api.user_name=${data.external.yaml.result.user_name} -Dcontrast.standalone.appname=${var.appname} -Dcontrast.server.name=${var.servername} -Dcontrast.server.environment=${var.environment} -Dcontrast.application.session_metadata=${var.session_metadata} -Dcontrast.application.tags=${var.apptags} -Dcontrast.server.tags=${var.servertags}"
}
}
}

13 changes: 13 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
output "ip_address" {
value = azurerm_container_group.app.ip_address
}

#the dns fqdn of the container group if dns_name_label is set
output "fqdn" {
value = "http://${azurerm_container_group.app.fqdn}:8080"
}

output "contrast" {
value = "This app should appear in the environment ${data.external.yaml.result.url}"
}

4 changes: 4 additions & 0 deletions parseyaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import yaml, json
with open('./contrast_security.yaml') as f:
config = yaml.load(f)
print(json.dumps(config['api']))
Binary file added spring-petclinic-1.5.1.jar
Binary file not shown.
29 changes: 29 additions & 0 deletions terraform-local/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#Terraform `provider` section is required since the `azurerm` provider update to 2.0+
provider "azurerm" {
features {}
}

# Configure the Docker provider
provider "docker" {
host = "unix:///var/run/docker.sock"
}

data "external" "yaml" {
program = [var.python_binary, "${path.module}/parseyaml.py"]
}

# Create a container
resource "docker_container" "spring-petclinic" {
image = "contrastsecuritydemo/spring-petclinic:1.5.1"
name = "spring-petclinic"

ports {
internal = 8080
external = 8081
}

env = [
"JAVA_TOOL_OPTIONS=-Dcontrast.api.url=${data.external.yaml.result.url} -Dcontrast.api.api_key=${data.external.yaml.result.api_key} -Dcontrast.api.service_key=${data.external.yaml.result.service_key} -Dcontrast.api.user_name=${data.external.yaml.result.user_name} -Dcontrast.standalone.appname=${var.appname} -Dcontrast.server.name=${var.servername} -Dcontrast.server.environment=${var.environment} -Dcontrast.application.session_metadata=${var.session_metadata} -Dcontrast.application.tags=${var.apptags} -Dcontrast.server.tags=${var.servertags}"
]
}

5 changes: 5 additions & 0 deletions terraform-local/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#the dns fqdn of the container group if dns_name_label is set
output "fqdn" {
value = "http://localhost:8081/"
}

4 changes: 4 additions & 0 deletions terraform-local/parseyaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import yaml, json
with open('./contrast_security.yaml') as f:
config = yaml.load(f)
print(json.dumps(config['api']))
Loading

0 comments on commit 177bea1

Please sign in to comment.