GitLab and Sitespeed.io

Within this tutorial I will explain shortly the combination of GitLab and Sitespeed.io. Normally GitLab provides this feature (Browser Performance Testing) but only with Premium or Silver editions. I imagine for this example that you know already the basics about GitLab pipelines, Docker in Docker and Sitespeed.io.

Preparation

As a first step you create a simple project directory with some files and one folder.

# create project directory
$ mkdir -p ~/Projects/SitespeedGitLab/ci

# change directory
cd ~/Projects/SitespeedGitLab

# create login file
$ vim ci/login.js

# create urls file
$ vim ci/urls.txt

# create pipeline file
$ vim .gitlab-ci.yml

# show structure (optional)
$ tree .
.
|__.gitlab-ci.yml
|__ci
  |__login.js
  |__urls.txt

Content of files

For the login we use the simplest version of a script which serves as pre script. You can expand it later as needed. If you do not need a login, you do not have to create this file (leave this out later in the command --preScript login.js).

module.exports = async function(context, commands) {

  // navigate to login page
  await commands.navigate('your domain');

  // add values into input fields
  await commands.addText.byId('username', 'input by id');
  await commands.addText.byId('password', 'input by id');

  // find the submit button and click it
  await commands.click.byClassName('Button');

  // wait to verify that you are logged in (incl. max time)
  return commands.wait.byId('specific tag with id',8000);
};

Let’s get to the next file. Here you simply enter all URLs to be checked. Each line represents a URL to be checked which (in our case) always precedes the login. There is also the possibility to login once for all sub-pages as well as various actions on the pages. In this case you would have to script everything (similar to pre/post scripts).

your url one
your url two
your url three

The files urls.txt and login.js have to be mounted inside the container. Therefore we choose the folder “ci”. After the successfully execution this folder will also provide the sitespeed reports.

Pipeline

The last step is the definition of GitLab pipeline. Here now a very simple example for .gitlab-ci.yml, with only one pipeline stage (test) and job (perf_tests). You can also expand this file later as you like (It’s already possible to build Docker images inside).

stages:
  - test

variables:
  DOCKER_HOST: tcp://localhost:2375/
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: ""

default:
  image: docker:stable
  services:
    - name: docker:dind

perf_tests:
  stage: test
  variables:
    MOUNT_PATH: "$CI_BUILDS_DIR/ci/"
    DOCKER_IMAGE: 'sitespeedio/sitespeed.io'
    BROWSER: 'firefox'
    ITERATION: '1'
  artifacts:
    name: "performance-reports"
    paths:
      - "$CI_BUILDS_DIR/ci/sitespeed-result/your domain"
  script:
    - docker run --shm-size=1g --rm -v $MOUNT_PATH:/sitespeed.io $DOCKER_IMAGE -b $BROWSER -n $ITERATION --preScript login.js urls.txt

Okay … done. You can commit/push everything into GitLab. After successfully pipeline run you can access the reports as GitLab pipeline artifacts (but not via repository folder -> because if the job is done the container and reports are gone).

ZAP API Basics

In this tutorial, I’d like to share a few ZAP API basics. This should make it possible for anyone to integrate ZAP into various pipelines.

Requirements

  • ZAP installed
  • jq installed

Minimum configuration of ZAP

Start ZAP now, if you get asked for select the persistent session – just select option “No, I don’t want…” and press button “Start”.

Select persist ZAP Session

Now open “Preferences” and ensure that ZAP API is enabled.

Enable ZAP API

Our last action for configuration is to enable ZAP Proxy.

ZAP Proxy

Start ZAP via command line

# show help (macOS)
$ /Applications/OWASP\ ZAP.app/Contents/MacOS/OWASP\ ZAP.sh -h

# show default directory (macOS)
$ ls -la ~/Library/Application\ Support/ZAP/

# start ZAP in daemon mode with specific port and apikey (macOS)
$ /Applications/OWASP\ ZAP.app/Contents/MacOS/OWASP\ ZAP.sh -daemon -port 8090 -config api.key=12345

# open ZAP API in browser
$ open http://localhost:8090/UI

Add URL (Site)

# add URL
$ curl -s "http://localhost:8090/JSON/core/action/accessUrl/?apikey=12345&url=https://www.webscantest.com&followRedirects=false" | jq .

Show ZAP Sites and Hosts

# list all sites
$ curl -s "http://localhost:8090/JSON/core/view/sites/?apikey=12345" | jq .

# list all hosts
$ curl -s "http://localhost:8090/JSON/core/view/hosts/?apikey=12345" | jq .

ZAP HTTP Sessions

# list all httpSession sites
$ curl -s "http://localhost:8090/JSON/httpSessions/view/sites/?apikey=12345" | jq .

# create new httpSession
$ curl -s "http://localhost:8090/JSON/httpSessions/action/createEmptySession/?apikey=12345&site=www.webscantest.com:443&session=session1" | jq .

# show active httpSession
$ curl -s "http://localhost:8090/JSON/httpSessions/view/activeSession/?apikey=12345&site=www.webscantest.com:443" | jq .

ZAP Spider scan

# start spider scan
$ curl -s "http://localhost:8090/JSON/spider/action/scan/?apikey=12345&zapapiformat=JSON&formMethod=GET&url=https://www.webscantest.com"

# show spider scan status
$ curl -s "http://localhost:8090/JSON/spider/view/status/?apikey=12345" | jq .

ZAP Context

# list all context
$ curl -s "http://localhost:8090/JSON/context/view/contextList/?apikey=12345" | jq .

# create context
$ curl -s "http://localhost:8090/JSON/context/action/newContext/?apikey=12345&contextName=Default+Context" | jq .

# show specific context
$ curl -s "http://localhost:8090/JSON/context/view/context/?apikey=12345&contextName=Default+Context" | jq .

# add regex into includeInContext
$ curl -s "http://localhost:8090/JSON/context/action/includeInContext/?apikey=12345&contextName=Default+Context&ex=https://www.webscantest.com.*" | jq .

# list all includeRegexs
$ curl -s "http://localhost:8090/JSON/context/view/includeRegexs/?apikey=12345&contextName=Default+Context" | jq .

ZAP Active scan

# start active scan
$ curl -s "http://localhost:8090/JSON/ascan/action/scan/?apikey=12345&zapapiformat=JSON&formMethod=GET&url=https://www.webscantest.com&recurse=&inScopeOnly=false&scanPolicyName=&method=&postData=&contextId="

# show active scan status
$ curl -s "http://localhost:8090/JSON/ascan/view/status/?apikey=12345" | jq .

ZAP alerts and reports

# list alert counts by url
$ curl -s "http://localhost:8090/JSON/alert/view/alertCountsByRisk/?apikey=12345&url=https://www.webscantest.com&recurse=True" | jq .

# list alerts by risk
curl -s "http://localhost:8090/JSON/alert/view/alertsByRisk/?apikey=12345&url=https://www.webscantest.com&recurse=True" | jq .

# show json report
$ curl -s "http://localhost:8090/OTHER/core/other/jsonreport/?apikey=12345" | jq .

# list all alerts
$ curl -s "http://localhost:8090/JSON/core/view/alerts/?apikey=12345" | jq .

ZAP shutdown

# shutdown
$ curl -s "http://localhost:8090/JSON/core/action/shutdown/?apikey=12345"