diff --git a/.circleci/config.yml b/.circleci/config.yml index 4bc7b6d8ca..3ee46ad8c1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -241,13 +241,7 @@ jobs: at: . - run: name: Start up local server - command: | # production style build (single BE server with static FE) - docker-compose -f docker-compose.dss.yml run --rm server yarn install --production=false - docker-compose -f docker-compose.dss.yml run --rm server yarn --cwd frontend install --production=false - docker-compose -f docker-compose.dss.yml run --rm server yarn build - docker-compose -f docker-compose.dss.yml run --rm server yarn --cwd frontend run build - docker-compose -f docker-compose.dss.yml up -d - docker-compose -f docker-compose.dss.yml exec server yarn db:migrate:ci + command: ./bin/prod-style-server - run: name: Pull OWASP ZAP docker image command: docker pull owasp/zap2docker-weekly @@ -256,16 +250,7 @@ jobs: command: chmod g+w reports - run: name: Run OWASP ZAP - command: | - docker run \ - -v $(pwd)/zap.conf:/zap/wrk/zap.conf:ro \ - -v $(pwd)/reports:/zap/wrk:rw \ - --rm \ - --user zap:$(id -g) \ - --network="project_smarthub" \ - -t owasp/zap2docker-weekly zap-baseline.py \ - -t http://server:8080 \ - -c zap.conf -I -i -r owasp_report.html + command: ./bin/run-owasp-scan - store_artifacts: path: reports/owasp_report.html accessibility_scan: diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..4a074fa087 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +# EditorConfig is awesome: https://EditorConfig.org + +[zap.conf] +indent_size = 4 +indent_style = tab diff --git a/bin/prod-style-server b/bin/prod-style-server new file mode 100755 index 0000000000..101b60da5d --- /dev/null +++ b/bin/prod-style-server @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This script sets up a server as run-owasp-scan expects it + +# remove old build directory +rm -r build/ + +# run a production style build (single BE server with static FE) +docker-compose -f docker-compose.dss.yml run --rm server yarn install --production=false +docker-compose -f docker-compose.dss.yml run --rm server yarn --cwd frontend install --production=false +docker-compose -f docker-compose.dss.yml run --rm server yarn build +docker-compose -f docker-compose.dss.yml run --rm server yarn --cwd frontend run build +docker-compose -f docker-compose.dss.yml up -d +docker-compose -f docker-compose.dss.yml run --rm server yarn db:migrate:ci diff --git a/bin/run-owasp-scan b/bin/run-owasp-scan new file mode 100755 index 0000000000..9441f487a7 --- /dev/null +++ b/bin/run-owasp-scan @@ -0,0 +1,29 @@ +#!/bin/bash +########################### +# +# Allow running OWASP Zap scans on demand. This script expects +# a server running at http://server:8080. This can be setup with +# +# ./bin/prod-style-server +# +# The only argument for this script is the network name docker will +# find server on. For a default git clone, this should be +# +# ./bin/run-owasp-scan head-start-ttadp_smarthub +# +########################### + +declare network="project_smarthub" +if [ -n "$1" ]; then + network=$1 +fi + +docker run \ + -v $(pwd)/zap.conf:/zap/wrk/zap.conf:ro \ + -v $(pwd)/reports:/zap/wrk:rw \ + --rm \ + --user zap:$(id -g) \ + --network=$network \ + -t owasp/zap2docker-weekly zap-baseline.py \ + -t http://server:8080 \ + -c zap.conf -I -i -r owasp_report.html diff --git a/src/app.js b/src/app.js index 8f3ca1f7c5..cd00462a85 100644 --- a/src/app.js +++ b/src/app.js @@ -5,6 +5,7 @@ import axios from 'axios'; import cookieSession from 'cookie-session'; import path from 'path'; import join from 'url-join'; +import { omit } from 'lodash'; import { INTERNAL_SERVER_ERROR } from 'http-codes'; import { CronJob } from 'cron'; import { hsesAuth } from './middleware/authMiddleware'; @@ -21,7 +22,14 @@ const oauth2CallbackPath = '/oauth2-client/login/oauth2/code/'; app.use(requestLogger); app.use(express.json()); app.use(express.urlencoded({ extended: true })); -app.use(helmet()); +app.use(helmet({ + contentSecurityPolicy: { + directives: { + ...omit(helmet.contentSecurityPolicy.getDefaultDirectives(), 'upgrade-insecure-requests', 'block-all-mixed-content'), + 'form-action': ["'self'"], + }, + }, +})); app.use(cookieSession({ name: 'session', diff --git a/zap.conf b/zap.conf index 84df7891ef..75839435ed 100644 --- a/zap.conf +++ b/zap.conf @@ -19,6 +19,7 @@ 10040 FAIL (Secure Pages Include Mixed Content) 10105 FAIL (Weak Authentication Method) 10202 FAIL (Absence of Anti-CSRF Tokens) +10055 WARN (CSP) 2 WARN (Private IP Disclosure) 3 FAIL (Session ID in URL Rewrite) 50001 WARN (Script Passive Scan Rules) @@ -26,4 +27,4 @@ 90011 WARN (Charset Mismatch) 90022 WARN (Application Error Disclosure) 90033 FAIL (Loosely Scoped Cookie) -* OUTOFSCOPE .*(robots.txt) +* OUTOFSCOPE .*(robots.txt|sitemap.xml)