Using ZAP with Azure DevOps Pipelines (Part 2)

Using ZAP with Azure DevOps Pipelines (Part 2)

Let’s now take a look at the files that make it all happen. I won’t be covering the other files in the repository as they are already covered https://augment1security.com/cicd/cicd-with-owasp-zap-docker-and-pipeline-scripting-part-1/ . The files mentioned below are specific for Azure Devops Pipelines.

dvwa-zap-pipeline-single-job.yaml

This is the main pipeline file that will call the other 2 bash scripts, init_dvwa.sh and stop_remove_container.sh. This pipeline yaml can be made more concise if we want to move some of the bash script code out to a separate file. Please read the inline comments that will explain more on what the file is doing.

variables:
- name: dvwaContainerName # variable name of a dvwa container
  value: dvwacontainer
- name: zapContainerName # variable name of a zap container
  value: zapcontainer
- name: dvwaContainerPortNumber # variable name of the public port number mapped to dvwa container
  value: 8090
- name: zapContainerPortNumber # variable name of the public port number mapped to zap container
  value: 8100
- name: owaspZapNet # variable name of the user defined network that zap and dvwa containers are in
  value: owasp-zap-net
- name: modifiedZap2dockerStableImageName # variable name of the customized zap2docker stable image tag
  value: modified-zap2docker-stable
- name: dvwaContainerUrl # variable name of the url that is used to access the dvwa web application
  value: http://${{variables.dvwaContainerName}}/

steps:
- bash: |
    docker network create ${{variables.owaspZapNet}}
  displayName: Creating user defined network to connect the dvwa and zap containers
- bash: |
    docker run -d -p ${{variables.dvwaContainerPortNumber}}:80 --name=${{variables.dvwaContainerName}} --network ${{variables.owaspZapNet}} vulnerables/web-dvwa

    # making the script executable
    chmod u+x init_dvwa.sh
    ./init_dvwa.sh ${{variables.dvwaContainerPortNumber}}
    if [ $? -eq 1 ]; 
    then 
      echo "##[error] Initialization of dvwa application failed"
      exit 1
    fi
    # displaying the output to show that the dvwa container is registered in the user defined network
    docker network inspect ${{variables.owaspZapNet}}
  displayName: Start up and initialize DVWA container
- bash: |
    # to display who the current user is
    echo "##[debug] Current user is $USER"

    # to display what was checked out from the repo
    ls -al $(Build.Repository.LocalPath)

    # building a new image off the zap2docker stable image, you can change which image to use in your context
    docker build --tag ${{variables.modifiedZap2dockerStableImageName}} --build-arg USER_ID=$(id -u $USER) --build-arg GROUP_ID=$(id -g $USER) .
  displayName: Customizing zap2docker-stable image
- bash: |
    docker run -t -d -p ${{variables.zapContainerPortNumber}}:8080 --name=${{variables.zapContainerName}} -v $(Build.Repository.LocalPath):/zap/wrk/:rw -w /zap/wrk/  --network ${{variables.owaspZapNet}} ${{variables.modifiedZap2dockerStableImageName}}
    docker exec ${{variables.zapContainerName}} cp /zap/wrk/log4j2.properties /zap/xml/
    docker exec ${{variables.zapContainerName}} cp /zap/wrk/config.xml /zap/xml/    
    docker exec ${{variables.zapContainerName}} ls -al /zap/xml/
    docker exec ${{variables.zapContainerName}} ping -c 2 ${{variables.dvwaContainerName}}
    if [ $? -eq 0 ]; 
    then 
      echo "Able to reach dvwa container" 
    else 
      echo "##[error] Not able to reach dvwa container"
      exit 1
    fi
    docker exec ${{variables.zapContainerName}} curl -v -L ${{variables.dvwaContainerUrl}}
    if [ $? -eq 0 ]; 
    then 
      echo "Able to reach dvwa url" 
    else 
      echo "Not able to reach dvwa url" 
      exit 1
    fi
  displayName: Running ${{variables.modifiedZap2dockerStableImageName}} container
- bash: |
    docker exec ${{variables.zapContainerName}} zap-baseline.py -t ${{variables.dvwaContainerUrl}} -g gen.conf -r testreport.html -n /zap/wrk/Default_Context.context -U admin
    if [ $? -eq 0 ]; 
    then 
      echo "Baseline scan passed" 
    else 
      echo "Baseline scan failed" 
      exit 1
    fi
  displayName: Running baseline scan
- bash: |
    docker exec ${{variables.zapContainerName}} mkdir /zap/wrk/toBePublished
    docker exec ${{variables.zapContainerName}} cp /home/zap/.ZAP/zap.log /zap/wrk/toBePublished
    docker exec ${{variables.zapContainerName}} cp /zap/wrk/testreport.html /zap/wrk/toBePublished
    ls -al $(Build.Repository.LocalPath)/toBePublished
  displayName: Copying files that are to be published
  condition: always() # this step will always run, even if the pipeline is canceled

- publish: $(Build.Repository.LocalPath)/toBePublished
  displayName: Publishing files
  condition: always() # this step will always run, even if the pipeline is canceled

- bash: |
    docker container ls --all
    # making the script executable
    chmod u+x stop_remove_container.sh
    ./stop_remove_container.sh ${{variables.dvwaContainerName}}
    ./stop_remove_container.sh ${{variables.zapContainerName}}
  displayName: Stopping DVWA and ZAP Containers
  condition: always() # this step will always run, even if the pipeline is canceled

init_dvwa.sh

This file will initialize the DVWA web application after it has started up. It will get the php session id and user token and try to initialize the database.

#!/bin/bash

# this script is to initialize the DVWA web application

# getting the port number from 1st argument
dvwaContainerPortNumber=$1

startAndInitializeDVWAContainer () {
  curl --dump-header headers.txt GET "http://localhost:${dvwaContainerPortNumber}/setup.php" > htmlresponse.txt

  # extracting out the php session id
  shellOutput=$(cat headers.txt | grep "PHPSESSID")
  echo "shellOutput for PHPSESSID lines:${shellOutput}"
  
  # getting the first line of the shell output
  firstLine=`echo "${shellOutput}" | head -n1`
  echo "Extracting first line: ${firstLine}"
  
  # removing the left hand side part of the line
  temp=${firstLine#*PHPSESSID=}
  # removing the right hand side of the line, leaving only the php session id
  phpSessionId=${temp%;*}
  echo "##[debug] php session id: ${phpSessionId}"
  
  # extracting out the user token
  shellOutput=$(cat htmlresponse.txt | grep "user_token")
  echo "shellOutput:${shellOutput}"
  temp=${shellOutput#*value=\'}
  userToken=${temp%\'*}
  echo "##[debug] user token: ${userToken}"
  
  # initializing the database of the dvwa application
  shellOutput=$(curl --location --request POST "http://localhost:${dvwaContainerPortNumber}/setup.php" --header "Cookie: PHPSESSID=${phpSessionId}; security=low" --form "user_token=${userToken}" --form "create_db=Create+%2F+Reset+Database")
  
  # checking if the database has been initialized
  if [ $(echo "${shellOutput}" | grep -c "Database has been created") -ge 1 ]
  then
    # returning true if the database has been initialized
    return 0
  else
    # returning false if the database has NOT been initialized
    return 1
  fi  
}

# looping for a few times to retry initializing the dvwa application
for i in {0..2}
do
  echo "Sleeping for 5 seconds" 
  sleep 5
  echo "Awake now" 
  shellOutput=$(curl -v -L "http://localhost:${dvwaContainerPortNumber}")
  if [ $(echo "${shellOutput}" | grep -c "<title>Login :: Damn Vulnerable Web Application (DVWA) v1.10 \*Development\*</title>") -e 1 ]; then  
    echo "DVWA application still not started" 
  elif startAndInitializeDVWAContainer; then
	echo "DVWA application initialized"
    exit 0
  else
	echo "DVWA application NOT initialized"
  fi
done

# when DVWA is still not initialized after a number of tries, we return an error exit code to cause the job to fail
exit 1

stop_remove_container.sh

A generic script will check for the existing of a  container by name, stop it if found and then remove it.

#!/bin/bash	

containerName=$1
 
# to check if the container exists. xargs is used to trim spaces
containerId=$(docker ps -aqf "name=${containerName}" | xargs)
echo "container id for ${containerName}: ${containerId}"
 
if [[ ${containerId} != "" ]]
then
    echo "Stopping container"
	docker container stop ${containerId}
    echo "Removing container"
	docker container rm ${containerId}
else 
	echo "no container called ${containerName}"
fi

Leave a Comment

Your email address will not be published. Required fields are marked *