To develop an application, you usually perform multiple iterations of the following activities:
- Commit the source code to implement a new or changed feature or a bug fix
- Build the solution
- Deploy a test environment containing the solution
- Run QA tests against the test environment to confirm whether the tests pass
- Notify the development team of test failures or of a successful test run
- Destroy the test environment once the cycle is complete
- Wait for the next code commit to start the cycle again
If you automate these activities, the development lifecycle becomes much more efficient. With well-built automated tests, it also becomes much more effective because you reduce manual intervention as much as possible.
Summary of the steps
This example shows you how to leverage the integration of Acunetix with GitHub – it runs a full test cycle every time there is a pull request. In this cycle, you perform the following steps:
- Connect to your Docker host:
- Create a folder to hold files for building a staging container with the web application
- Copy relevant files to build a staging container on the Docker host
- Build an image of the staging container and run an instance of it
- Check that the web application in the staging instance is responding to requests with 200 OK
- Run a script to get Acunetix to:
- Create the staging target
- Trigger a scan of the staging target
- Wait for the scan to complete
- Check the number of vulnerabilities found; if at least 1 is found, the process will be considered as failed
- Connect to the Docker host:
- Stop and remove the staging container and its underlying image
- Remove the folder created for the staging container files
Create a GitHub repository for your web application
- Sign in to your GitHub account
- Click on the New button to create a new repository
- Enter the information for your new repository:
- Set your repository name to test-php-site
- Set your repository description to My Test PHP Site
- Enable the Add a README file checkbox to initialize your source code repository
- Click on the Create repository button
Create a repository folder with your web application and its associated Docker file
- On your repository page, click on the Add file button and select the Create new file option from the drop-down menu
- Create the product/index.php file as follows:
-
- Set the file name to product/index.php
- Set the contents to:
<html> <head> <title>PHP Test</title> </head> <body> <?php echo '<p>Hello World</p>'; ?> </body> </html>
-
- Click on the Commit new file button
- On your repository page, click on the Add file button and select the Create new file option from the drop-down menu
- Create the product/Dockerfile file as follows:
-
- Set the file name to product/Dockerfile
- Set the contents to:
FROM php:7.3.28-apache #setup the web pages COPY --chown=www-data:www-data index.php /var/www/html
-
- Click on the Commit new file button
Create a repository folder to contain your Acunetix scan script
- On your repository page, click on the Add file button and select the Create new file option from the drop-down menu
- Create the scripts/axscript.sh file as follows:
-
- Set the file name to scripts/axscript.sh
- Set the contents to:
#!/bin/bash # Declare functions cleanup(){ # Delete the scan Dummy=`curl -sS -k -X DELETE "$MyAXURL/scans/{$MyScanID}" -H "Accept: application/json" -H "X-Auth: $MyAPIKEY"` # Delete the target Dummy=`curl -sS -k -X DELETE "$MyAXURL/targets/{$MyTargetID}" -H "Accept: application/json" -H "X-Auth: $MyAPIKEY"` } # Create our intended target MyTargetID=`curl -sS -k -X POST $MyAXURL/targets -H "Content-Type: application/json" -H "X-Auth: $MyAPIKEY" --data "{\"address\":\"$MyTargetURL\",\"description\":\"$MyTargetDESC\",\"type\":\"default\",\"criticality\":10}" | grep -Po '"target_id": *\K"[^"]*"' | tr -d '"'` # Trigger a scan of the target MyScanID=`curl -i -sS -k -X POST $MyAXURL/scans -H "Content-Type: application/json" -H "X-Auth: $MyAPIKEY" --data "{\"profile_id\":\"$ScanProfileID\",\"incremental\":false,\"schedule\":{\"disable\":false,\"start_date\":null,\"time_sensitive\":false},\"user_authorized_to_scan\":\"yes\",\"target_id\":\"$MyTargetID\"}" | grep "Location: " | sed "s/Location: \/api\/v1\/scans\///" | sed "s/\r//g" | sed -z "s/\n//g"` while true; do MyScanStatus=`curl -sS -k -X GET "$MyAXURL/scans/{$MyScanID}" -H "Accept: application/json" -H "X-Auth: $MyAPIKEY"` if [[ "$MyScanStatus" == *"\"status\": \"processing\""* ]]; then echo "Scan Status: Processing - waiting 30 seconds" elif [[ "$MyScanStatus" == *"\"status\": \"scheduled\""* ]]; then echo "Scan Status: Scheduled - waiting 30 seconds" elif [[ "$MyScanStatus" == *"\"status\": \"completed\""* ]]; then echo "Scan Status: Completed" # Break out of loop break else echo "Invalid Scan Status: Aborting" # Clean Up and Exit script cleanup exit 1 fi sleep 30 done # Obtain the scan session ID MyScanSessionID=`echo "$MyScanStatus" | grep -Po '"scan_session_id": *\K"[^"]*"' | tr -d '"'` # Obtain the scan result ID MyScanResultID=`curl -sS -k -X GET "$MyAXURL/scans/{$MyScanID}/results" -H "Accept: application/json" -H "X-Auth: $MyAPIKEY" | grep -Po '"result_id": *\K"[^"]*"' | tr -d '"'` # Obtain scan vulnerabilities MyScanVulnerabilities=`curl -sS -k -X GET "$MyAXURL/scans/{$MyScanID}/results/{$MyScanResultID}/vulnerabilities" -H "Accept: application/json" -H "X-Auth: $MyAPIKEY"` # Count vulnerabilities MyVulnerabilityCount=$(echo $MyScanVulnerabilities | jq '.vulnerabilities | length') # Exit with error if we find vulnerabilities; exit WITHOUT error if vulnerabilities count is 0 if [ $MyVulnerabilityCount -gt 0 ] ; then exit 1 ; else exit 0 ; fi
-
- Click on the Commit new file button
Create secrets for your repository
- On your repository page, click on the Settings icon
- Click on the Secrets option in the sidebar
- On the Action secrets page, click on the New repository secret button
- Create 4 secrets as follows:
- DOCKER_HOST – this must contain the public IP Address of your Docker host
- DOCKER_USER – this must contain the user name used to connect to the Docker host via SSH
- DOCKER_KEY – this must contain the SSH key used to connect (securely but without using a password) to the Docker host; this would typically begin with —–BEGIN OPENSSH PRIVATE KEY—– and end with —–END OPENSSH PRIVATE KEY—–
- MYAPIKEY – this must contain the API key for your Acunetix account, which you can retrieve from the Acunetix profile page:
Create your workflow file
-
- On your repository page, click on the Add file button and select the Create new file option from the drop-down menu
- Create the .github/workflows/main.yml file as follows:
-
- Set the file name to .github/workflows/main.yml
- Set the contents to:
# main.yml name: Vulnerability testing on: push: branches: - main env: MyAXURL: 'https://online.acunetix.com/api/v1' MyTargetURL: 'http://acunetixexample.com:8080' MyTargetDESC: 'Test PHP Site - created via GitHub-to-Acunetix CI/CD integration' ScanProfileID: '11111111-1111-1111-1111-111111111111' jobs: deploy: name: Deploy product to the Docker host runs-on: ubuntu-latest steps: - name: checkout_code uses: actions/checkout@v2 - name: create_docker_folder uses: garygrossgarten/github-action-ssh@release with: host: ${{ secrets.DOCKER_HOST }} username: ${{ secrets.DOCKER_USER }} privateKey: ${{ secrets.DOCKER_KEY}} command: mkdir -p ~/test-php-site - name: copy_files_to_docker_folder uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.DOCKER_HOST }} username: ${{ secrets.DOCKER_USER }} privateKey: ${{ secrets.DOCKER_KEY}} local: ./product remote: test-php-site/ - name: build_and_run_docker_container uses: garygrossgarten/github-action-ssh@release with: host: ${{ secrets.DOCKER_HOST }} username: ${{ secrets.DOCKER_USER }} privateKey: ${{ secrets.DOCKER_KEY}} command: | cd ~/test-php-site docker build -t test-php-site . docker run -d -p 8080:80 --name mytest test-php-site check: if: always() needs: deploy name: Check if the deployed product is functional runs-on: ubuntu-latest steps: - name: check_deployment run: wget -qO- -T 5 $MyTargetURL >/dev/null ; if [ $? != 0 ] ; then exit 1 ; fi scan: if: always() needs: check name: Scan product with Acunetix runs-on: ubuntu-latest steps: - name: install_packages run: sudo apt-get -y install jq - name: checkout_code uses: actions/checkout@v2 - name: scan_product env: MyAPIKEY: ${{ secrets.MyAPIKEY }} run: | chmod +x scripts/axscript.sh scripts/axscript.sh destroy: if: always() needs: scan name: Destroy deployment runs-on: ubuntu-latest steps: - name: destroy_deployment uses: garygrossgarten/github-action-ssh@release with: host: ${{ secrets.DOCKER_HOST }} username: ${{ secrets.DOCKER_USER }} privateKey: ${{ secrets.DOCKER_KEY}} command: | docker stop mytest docker rm mytest docker rmi test-php-site rm -rf ~/test-php-site
-
- Adjust the lines highlighted in red to reflect your staging instance details; for example:
MyTargetURL: 'http://yourstagingdomain.com:8080' MyTargetDESC: 'Test PHP Site - created via GitHub-to-Acunetix CI/CD integration'
- Click on the Commit new file button
This will immediately trigger the first run of the workflow. Go to the Actions page and click on the currently running workflow link to look at the progress of the workflow:
When the workflow has been completed, you can see the success or failure of each of the main steps in the workflow. A run with zero vulnerabilities would show as follows:
Whereas a run that finds vulnerabilities would show as follows:
Get the latest content on web security
in your inbox each week.