├── .circleci
├── config.yml
└── config.yml.complete
├── .github
└── workflows
│ └── pixi-crs-ci.yml
├── LICENSE
├── README.md
├── RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
├── SECURITY.md
├── azure-pipelines.yml
├── buildspec.yml
├── cloudbuild.yaml
├── compose-aws.env
├── compose-gcp.env
├── compose-local.env
├── docker-compose-without-crs.yaml
├── docker-compose.yaml
├── img
├── AWS
│ ├── AWS_CodeBuild_Logs.png
│ ├── AWS_Pipeline_Output.png
│ ├── AWS_PixiCRS_BuildProject.png
│ ├── AWS_Testcafe_Output.png
│ └── AWS_Title_Image.png
├── AzureDevOps
│ ├── Azure_Pipeline_Output.png
│ ├── Azure_Testcafe_Output.png
│ ├── Azure_Title_Image.png
│ ├── azure-1-new-project.png
│ ├── azure-2-create-pipeline.png
│ ├── azure-3-github.png
│ ├── azure-4-select-repo.png
│ └── azure-5-review-pipeline.png
├── CircleCI
│ └── CircleCI_Pipeline_Output.png
├── GCP
│ ├── GCP_Testcafe_Output.png
│ └── GCP_Title_Image.png
└── GitHub_Actions
│ ├── GHA_Pipeline_Output.png
│ └── GHA_Testcafe_Output.png
├── testcafe
├── tests_container_ip
│ ├── test.js
│ ├── testcrs.js
│ └── testwaf.js
├── tests_container_ip2
│ ├── mongodb.js
│ ├── mongodbcrs.js
│ ├── test.js
│ ├── test_local.js
│ ├── testcrs-match.js
│ ├── testcrs.js
│ └── testwaf.js
└── tests_localhost
│ ├── test.js
│ ├── testcrs.js
│ └── testwaf.js
└── zap
├── zap-api.conf
└── zap-baseline.conf
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Getting started with CircleCI
2 | # https://circleci.com/docs/2.0/first-steps/
3 | #
4 | # .circleci/config.yml
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | - image: circleci/node:stretch
10 |
11 | steps:
12 | - run:
13 | name: Install dependencies
14 | command: |
15 | sudo apt-get update && sudo apt-get install -y curl
16 | mkdir -p /tmp/test-results/
17 |
18 | - run:
19 | name: Install Docker Compose
20 | command: |
21 | curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
22 | chmod +x ~/docker-compose
23 | sudo mv ~/docker-compose /usr/local/bin/docker-compose
24 |
25 | - setup_remote_docker
26 |
27 | - checkout
28 |
29 | - run:
30 | name: Start Application Pixi with docker-compose up
31 | # http://172.17.0.1:8000
32 | command: |
33 | set -x
34 | docker-compose version
35 | docker-compose -f docker-compose-without-crs.yaml up -d
36 |
37 | # OWASP ModSecurity Core Rule Set Container (Apache Reverse Proxy)
38 | # owasp/modsecurity-crs
39 | # Environment variables:
40 | # PARANOIA: paranoia_level
41 | # ANOMALYIN: inbound_anomaly_score_threshold
42 | # ANOMALYOUT: outbound_anomaly_score_threshold
43 | # ALLOWED_METHODS: A string indicating the allowed_methods (Default: GET HEAD POST OPTIONS)
44 | # See https://coreruleset.org/
45 | #
46 | # PROXYLOCATION: application backend
47 | - run:
48 | name: Start OWASP ModSecurity CRS Container in front Pixi
49 | # http://172.17.0.2:80
50 | # we set inbound and outbound anomaly score to 1, no tolerance
51 | # We have to expose the port here, port mapping is not supported in CircleCI
52 | command: |
53 | docker pull owasp/modsecurity-crs:v3.3.0-apache && \
54 | docker run -dt --name crs -e PARANOIA=2 \
55 | -e ALLOWED_METHODS='GET HEAD POST OPTIONS PUT' -e \
56 | ANOMALY_INBOUND=1 -e ANOMALY_OUTBOUND=1 -e BACKEND=http://172.17.0.1:8000 \
57 | -e ERRORLOG=/var/log/apache2/error.log \
58 | --expose 80 owasp/modsecurity-crs:v3.3.0-apache
59 |
60 | # ModSecurity Tuning:\
61 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/
62 | # We use rule exclusion example files:
63 | # REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf OR
64 | # RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
65 | # We use cp command here because volumes are not supported in CircleCI
66 | - run:
67 | name: ModSecurity Tuning - Load rule exclusions
68 | command: |
69 | docker cp RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf crs:/etc/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf;
70 | docker exec crs /usr/local/apache2/bin/apachectl -f /usr/local/apache2/conf/httpd.conf -k graceful
71 |
72 | # Debugging possibilities:
73 | # - run:
74 | # name: curl test
75 | # command: |
76 | # (
77 | # docker pull hiromasaono/curl && \
78 | # docker run -dt --name curl hiromasaono/curl && \
79 | # docker exec -ti curl curl http://172.17.0.1:8000/register
80 | # docker exec -ti curl curl http://172.17.0.2:8001/register
81 | # docker exec -ti apachecrs cat /var/log/apache2/error.log
82 | # docker exec -ti apachecrs curl 172.17.0.1:8000
83 | # )
84 | #
85 | - run:
86 | name: Application Tests with Testcafe
87 | command: |
88 | # https://circleci.com/docs/2.0/building-docker-images/#mounting-folders
89 | # creating dummy container which will hold a volume with config
90 | docker create -v /tests --name configs alpine:latest /bin/true
91 | # copying config file into this volume
92 | docker cp /home/circleci/project/testcafe/tests_container_ip2/test.js configs:/tests
93 | #docker cp /home/circleci/project/testcafe/tests/mongodb.js configs:/tests
94 | # starting application container using this volume
95 | docker pull testcafe/testcafe
96 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
97 | docker run --volumes-from configs:rw --name testcafe -it testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox' /tests/test.js #/tests/mongodb.js
98 | # docker cp testcafe:/tmp/res.xml /tmp/test-results/
99 | # docker run --volumes-from configs:rw --name testcafe -it testcafe/testcafe --reporter xunit:/tmp/res.xml --skip-js-errors 'chromium:headless --no-sandbox' /tests/test.js /tests/mongodb.js
100 |
101 | - run:
102 | name: Application Tests with CRS with Testcafe
103 | command: |
104 | docker cp /home/circleci/project/testcafe/tests_container_ip2/testcrs.js configs:/tests
105 | # Intentionally trigger an error with a Testcafe Test that calls $select in ARGS_GET:
106 | # docker cp /home/circleci/project/testcafe/tests/testcrs-match.js configs:/tests
107 | # Some evil Mongo DB statements:
108 | # docker cp /home/circleci/project/testcafe/tests/mongodbcrs.js configs:/tests
109 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
110 | docker run --volumes-from configs:rw --name testcafecrs -it testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox' /tests/testcrs.js #/tests/testcrs-match.js /tests/mongodbcrs.js
111 | # docker cp testcafe:/tmp/res.xml /tmp/test-results/
112 | # docker run -v /home/circleci/project/testcafe/tests:/tests -it testcafe/testcafe chrome /tests/testcrs.js --skip-js-errors --reporter xunit:/tmp/test-results/res.xml
113 |
114 | - run:
115 | name: WAF Tests with malicous request to test WAF itself
116 | command: |
117 | docker cp /home/circleci/project/testcafe/tests_container_ip2/testwaf.js configs:/tests
118 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
119 | docker run --volumes-from configs:rw --name testcafewaf -it testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox' /tests/testwaf.js
120 |
121 | - run:
122 | # Fail if ModSecurity log is not empty
123 | # If not empty -> Repair your application OR
124 | # -> ModSecurity Tuning:
125 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
126 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
127 | name: Show ModSecurity logs of Testcafe Tests
128 | command: |
129 | docker exec crs cat /var/log/apache2/error.log \
130 | | grep ModSecurity | grep -vi "My evil WAF Test" | grep -v " Score: 30" | grep msg && exit 1 || exit 0
131 |
132 |
133 | # we always want to see this step:
134 | when: always
135 |
136 | - run:
137 | # Fail if ModSecurity log does not contain WAF Test String "My evil WAF Test"
138 | # ''
139 | # If empty -> WAF Test String
140 | # did not trigger a CRS rule.
141 | # That means CRS is not working properly or test was aborted.
142 | name: Search for WAF Test String "My evil WAF Test" in ModSecurity logs
143 | command: |
144 | docker exec crs cat /var/log/apache2/error.log
145 |
146 | # we always want to see this step:
147 | when: always
148 |
149 | - store_test_results:
150 | path: /tmp/test-results
151 |
--------------------------------------------------------------------------------
/.circleci/config.yml.complete:
--------------------------------------------------------------------------------
1 | # Getting started with CircleCI
2 | # https://circleci.com/docs/2.0/first-steps/
3 | #
4 | # .circleci/config.yml
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | - image: circleci/node:9.5.0-stretch
10 |
11 | steps:
12 | - run:
13 | name: Install dependencies
14 | command: |
15 | sudo apt-get update && sudo apt-get install -y curl
16 | mkdir -p /tmp/test-results/
17 |
18 | - run:
19 | name: Install Docker Compose
20 | command: |
21 | curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
22 | chmod +x ~/docker-compose
23 | sudo mv ~/docker-compose /usr/local/bin/docker-compose
24 |
25 | - setup_remote_docker
26 |
27 | - checkout
28 |
29 | - run:
30 | name: Start App Container
31 | #http://172.17.0.1:8000
32 | command: |
33 | set -x
34 | docker-compose version
35 | docker-compose up -d
36 |
37 | # OWASP ModSecurity Core Rule Set Container (Apache Reverse Proxy)
38 | # franbuehler/modsecurity-crs-rp
39 | # Environment variables:
40 | # PARANOIA: paranoia_level
41 | # ANOMALYIN: inbound_anomaly_score_threshold
42 | # ANOMALYOUT: outbound_anomaly_score_threshold
43 | # See https://coreruleset.org/
44 | #
45 | # BACKEND: application backend
46 | # PORT: listening port of apache, this port must be exposed: --expose
47 | - run:
48 | name: Start OWASP ModSecurity CRS Container in front of application for application tests
49 | #http://172.17.0.2:8001
50 | #we set inbound and outbound anomaly score to 1, no tolerance
51 | command: |
52 | docker login -u ${DOCKER_USER} -p ${DOCKER_PASS};
53 | docker pull franbuehler/modsecurity-crs-rp && \
54 | docker run -dt --name apachecrstc -e PARANOIA=2 -e \
55 | ANOMALYIN=1 -e ANOMALYOUT=1 -e BACKEND=http://172.17.0.1:8000 \
56 | -e PORT=8001 --expose 8001 franbuehler/modsecurity-crs-rp
57 |
58 | #docker run -dt --name apachecrs franbuehler/modsecurity-crs-rp
59 |
60 | # - run:
61 | # name: Start OWASP ModSecurity CRS Container in front of application for mean application tests
62 | # #http://172.17.0.3:8002
63 | # #we set inbound and outbound anomaly score to 1, no tolerance
64 | # command: |
65 | # docker login -u ${DOCKER_USER} -p ${DOCKER_PASS};
66 | # docker pull franbuehler/modsecurity-crs-rp && \
67 | # docker run -dt --name apachecrsmeantc -e PARANOIA=2 -e \
68 | # ANOMALYIN=100 -e ANOMALYOUT=100 -e BACKEND=http://172.17.0.1:8000 \
69 | # -e PORT=8002 --expose 8002 franbuehler/modsecurity-crs-rp
70 |
71 | # - run:
72 | # name: Start OWASP ModSecurity CRS Container in front of API for ZAP Scan
73 | # #http://172.17.0.4:8003
74 | # command: |
75 | # docker run -dt --name apachecrsapi -e PARANOIA=1 -e \
76 | # ANOMALYIN=1 -e ANOMALYOUT=1 -e BACKEND=http://172.17.0.1:8090 \
77 | # -e PORT=8003 --expose 8003 franbuehler/modsecurity-crs-rp
78 |
79 | # - run:
80 | # name: Start OWASP ModSecurity CRS Container in front of application for ZAP Active Scan
81 | # #http://172.17.0.4:8003
82 | # #we set inbound and outbound anomaly score to 100, we want to see all errors
83 | # command: |
84 | # docker run -dt --name apachecrsactive -e PARANOIA=4 -e \
85 | # ANOMALYIN=1 -e ANOMALYOUT=1 -e BACKEND=http://172.17.0.1:8000 \
86 | # -e PORT=8003 --expose 8003 franbuehler/modsecurity-crs-rp
87 |
88 | # ModSecurity Tuning:
89 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/
90 | # We use rule exclusion example files:
91 | # REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf OR
92 | # RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
93 | - run:
94 | name: ModSecurity Tuning - Load rule exclusions
95 | command: |
96 | # rule 920350 (msg: Host header is a numeric IP address) triggers,
97 | # because we use IP addresses instead of hostnames.
98 | # This rule must not be excluded in production!
99 | printf "\nSecRuleRemoveById 920350\n" > tmp.conf
100 | # CRS container for application tests:
101 | docker cp tmp.conf apachecrstc:/etc/httpd/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf;
102 | docker exec apachecrstc /usr/sbin/httpd -k graceful
103 | # CRS container for application tests:
104 | # docker cp tmp.conf apachecrsmeantc:/etc/httpd/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf;
105 | # docker exec apachecrsmeantc /usr/sbin/httpd -k graceful
106 | # CRS container for ZAP API Scan:
107 | # docker cp tmp.conf apachecrsapi:/etc/httpd/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf;
108 | # docker exec apachecrsapi /usr/sbin/httpd -k graceful
109 | # CRS container for ZAP Scan:
110 | # docker cp tmp.conf apachecrsactive:/etc/httpd/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf;
111 | # docker exec apachecrsactive /usr/sbin/httpd -k graceful
112 | #
113 | # Examples:
114 | # printf "\nSecRuleUpdateTargetById 942420 \"!REQUEST_COOKIES:session\"\n" > tmp.conf
115 | # printf "\nSecRuleUpdateTargetById 942440 \"!REQUEST_COOKIES:session\"\n" > tmp.conf
116 | #
117 | # - run:
118 | # name: ZAP Baseline Scan of application (without CRS)
119 | # command: |
120 | # (
121 | # docker pull owasp/zap2docker-weekly && \
122 | # docker run -t owasp/zap2docker-weekly zap-baseline.py \
123 | # -t http://172.17.0.1:8000/ -l WARN || \
124 | # if [ $? -ne 1 ]; then exit 0; else exit 1; fi;
125 | # )
126 |
127 | # # https://hub.docker.com/r/ictu/zap2docker-weekly/
128 | # # This Active Scan has to be developed: a custom config_file and context file
129 | # # This Active Scan takes about 3,5 minutes to run
130 | # - run:
131 | # name: ZAP Active Scan of application (without CRS)
132 | # command: |
133 | # (
134 | # docker pull ictu/zap2docker-weekly && \
135 | # docker run -t ictu/zap2docker-weekly zap-baseline-custom.py \
136 | # -t http://172.17.0.1:8000/ -l WARN -m 5 \
137 | # --active_scan \
138 | # --auth_loginurl http://172.17.0.1:8000/login \
139 | # --auth_usernamefield user --auth_passwordfield pass \
140 | # --auth_auto --auth_username pixiadmin --auth_password adminpixi || \
141 | # # we know this is an insecure application:
142 | # exit 0
143 | # #if [ $? -ne 1 ]; then exit 0; else exit 1; fi;
144 | # )
145 | #
146 | # # https://hub.docker.com/r/ictu/zap2docker-weekly/
147 | # # This Active Scan has to be developed: a custom config_file and context file
148 | # # This Active Scan takes about 3,5 minutes to run
149 | # - run:
150 | # name: ZAP active scan of application with CRS
151 | # # Only fail on error code 1, which indicates at least one FAIL was found.
152 | # # error codes 2 & 3 indicate WARN or other, and should not break the run
153 | # # -u https://raw.githubusercontent.com/${DOCKER_USER}/${CIRCLE_PROJECT_REPONAME}/master/zap-baseline.conf \
154 | # command: |
155 | # (
156 | # docker pull ictu/zap2docker-weekly && \
157 | # docker run -t ictu/zap2docker-weekly zap-baseline-custom.py \
158 | # -t http://172.17.0.4:8003/ -l WARN -m 5 \
159 | # --active_scan \
160 | # --auth_loginurl http://172.17.0.4:8003/login \
161 | # --auth_usernamefield user --auth_passwordfield pass \
162 | # --auth_auto --auth_username pixiadmin --auth_password adminpixi || \
163 | # # we know this is an insecure application:
164 | # exit 0
165 | # )
166 |
167 | # Debugging possibilities:
168 | # - run:
169 | # name: curl test
170 | # command: |
171 | # (
172 | # docker pull hiromasaono/curl && \
173 | # docker run -dt --name curl hiromasaono/curl && \
174 | # docker exec -ti curl curl http://172.17.0.1:8000/register
175 | # docker exec -ti curl curl http://172.17.0.2:8001/register
176 | # docker exec -ti apachecrs cat /etc/httpd/logs/error.log
177 | # docker exec -ti apachecrs curl 172.17.0.1:8000
178 | # )
179 | #
180 | # A zap-api.conf could be developed
181 | # - run:
182 | # name: ZAP API scan of application (without CRS)
183 | # command: |
184 | # (
185 | # docker pull owasp/zap2docker-weekly && \
186 | # docker run -t owasp/zap2docker-weekly zap-api-scan.py \
187 | # -u https://raw.githubusercontent.com/franbuehler/pixi-crs/master/zap-api.conf?token=AQ6RVdBQnWmIHjOULcbaM-1untbAtqkkks5aoZmqwA \
188 | # -f openapi -l WARN \
189 | # -t http://172.17.0.1:8090/swagger.json \
190 | # -z "config formhandler.fields.field\(0\).fieldId=user" \
191 | # -z "config formhandler.fields.field\(0\).value=test@pixi.owasp" \
192 | # -z "config formhandler.fields.field\(0\).enabled=true" \
193 | # -z "config formhandler.fields.field\(1\).fieldId=pass" \
194 | # -z "config formhandler.fields.field\(1\).value=testpass" \
195 | # -z "config formhandler.fields.field\(1\).enabled=true" || \
196 | # # we know this is an insecure application:
197 | # exit 0
198 | # )
199 | #
200 | # A zap-api.conf could be developed
201 | # - run:
202 | # name: ZAP API scan of application with CRS
203 | # command: |
204 | # (
205 | # docker pull owasp/zap2docker-weekly && \
206 | # docker run -t owasp/zap2docker-weekly zap-api-scan.py \
207 | # -u https://raw.githubusercontent.com/franbuehler/pixi-crs/master/zap-api.conf?token=AQ6RVdBQnWmIHjOULcbaM-1untbAtqkkks5aoZmqwA \
208 | # -f openapi -l WARN \
209 | # -t http://172.17.0.4:8003/swagger.json \
210 | # -z "config formhandler.fields.field\(0\).fieldId=user" \
211 | # -z "config formhandler.fields.field\(0\).value=test@pixi.owasp" \
212 | # -z "config formhandler.fields.field\(0\).enabled=true" \
213 | # -z "config formhandler.fields.field\(1\).fieldId=pass" \
214 | # -z "config formhandler.fields.field\(1\).value=testpass" \
215 | # -z "config formhandler.fields.field\(1\).enabled=true" || \
216 | # if [ $? -ne 1 ]; then exit 0; else exit 1; fi;
217 | # )
218 |
219 | - run:
220 | name: Application Tests with Testcafe
221 | command: |
222 | # https://circleci.com/docs/2.0/building-docker-images/#mounting-folders
223 | # creating dummy container which will hold a volume with config
224 | docker create -v /tests --name configs alpine:latest /bin/true
225 | # copying config file into this volume
226 | docker cp /home/circleci/project/testcafe/tests/test.js configs:/tests
227 | #docker cp /home/circleci/project/testcafe/tests/mongodb.js configs:/tests
228 | # starting application container using this volume
229 | docker pull testcafe/testcafe
230 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
231 | docker run --volumes-from configs:rw --name testcafe -it testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox' /tests/test.js #/tests/mongodb.js
232 | # docker cp testcafe:/tmp/res.xml /tmp/test-results/
233 | # docker run --volumes-from configs:rw --name testcafe -it testcafe/testcafe --reporter xunit:/tmp/res.xml --skip-js-errors 'chromium:headless --no-sandbox' /tests/test.js /tests/mongodb.js
234 |
235 | - run:
236 | name: Application Tests with CRS with Testcafe
237 | command: |
238 | docker cp /home/circleci/project/testcafe/tests/testcrs.js configs:/tests
239 | #docker cp /home/circleci/project/testcafe/tests/mongodbcrs.js configs:/tests
240 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
241 | docker run --volumes-from configs:rw --name testcafecrs -it testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox' /tests/testcrs.js #/tests/mongodbcrs.js
242 | # docker cp testcafe:/tmp/res.xml /tmp/test-results/
243 | # docker run -v /home/circleci/project/testcafe/tests:/tests -it testcafe/testcafe chrome /tests/testcrs.js --skip-js-errors --reporter xunit:/tmp/test-results/res.xml
244 |
245 | - run:
246 | # Fail if ModSecurity log is not empty
247 | name: Show ModSecurity logs of Testcafe Tests
248 | command: |
249 | docker exec apachecrstc cat /etc/httpd/logs/error.log
250 | #| grep ModSecurity | grep msg | tee /tmp/test-results/ModSecurity-Testcafe.log || \
251 | # if [ $? -eq 1 ]; then echo "No ModSecurity rule matched, that is ok!"; exit 0; fi;
252 | # If not empty -> Repair your application OR
253 | # -> ModSecurity Tuning:
254 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
255 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
256 | # we always want to see this step:
257 | when: always
258 |
259 | # - run:
260 | # name: Show ModSecurity logs of mean Testcafe Tests
261 | # command: |
262 | # docker exec apachecrsmeantc cat /etc/httpd/logs/error.log
263 | # #| grep ModSecurity | grep msg | tee /tmp/test-results/ModSecurity-Testcafe.log || \
264 | # # if [ $? -eq 1 ]; then echo "No ModSecurity rule matched, that is ok!"; exit 0; fi;
265 | # # we always want to see this step:
266 | # when: always
267 |
268 | # - run:
269 | # name: Show ModSecurity logs of ZAP API Scan
270 | # command: |
271 | # docker exec apachecrsapi cat /etc/httpd/logs/error.log | grep -o -E " (at|against) .*\[file.*\[id \"[0-9]+.*\[msg \"[^\"]+" | tr -d \" | sed -e "s/ at the end of input at/ at/" -e "s/ required. /. /" -e "s/\[rev .*\[msg/[msg/" -e "s/\. / /" -e "s/(Total .*/(Total ...) .../" | tr -d \] | cut -d\ -f3,9,11- | sed -e "s/^\([^ ]*\) \([^ ]*\)/\2 \1/" | awk "{ printf \"%+6s %-35s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n\", \$1, \$2, \$3, \$4, \$5, \$6, \$7, \$8, \$9, \$10, \$11, \$12, \$13, \$14, \$15, \$16, \$17, \$18, \$19, \$20 }" | sed -e "s/\ *$//" | sort | uniq -c | sort -n | tee /tmp/test-results/ModSecurity-ZAP-api-scan.log || \
272 | # if [ $? -eq 1 ]; then echo "Empty or not existing ModSecurity log from ZAP API scan"; exit 1; else exit 0; fi;
273 | # # we always want to see this step:
274 | # when: always
275 |
276 | # - run:
277 | # name: Show ModSecurity logs of ZAP Active Scan
278 | # command: |
279 | # docker exec apachecrsactive cat /etc/httpd/logs/error.log | grep -o -E " (at|against) .*\[file.*\[id \"[0-9]+.*\[msg \"[^\"]+" | tr -d \" | sed -e "s/ at the end of input at/ at/" -e "s/ required. /. /" -e "s/\[rev .*\[msg/[msg/" -e "s/\. / /" -e "s/(Total .*/(Total ...) .../" | tr -d \] | cut -d\ -f3,9,11- | sed -e "s/^\([^ ]*\) \([^ ]*\)/\2 \1/" | awk "{ printf \"%+6s %-35s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n\", \$1, \$2, \$3, \$4, \$5, \$6, \$7, \$8, \$9, \$10, \$11, \$12, \$13, \$14, \$15, \$16, \$17, \$18, \$19, \$20 }" | sed -e "s/\ *$//" | sort | uniq -c | sort -n | tee /tmp/test-results/ModSecurity-ZAP-active-scan.log || \
280 | # if [ $? -eq 1 ]; then echo "Empty or not existing ModSecurity log from ZAP Active scan"; exit 1; else exit 0; fi;
281 | # # we always want to see this step:
282 | # when: always
283 |
284 | - store_test_results:
285 | path: /tmp/test-results
286 |
--------------------------------------------------------------------------------
/.github/workflows/pixi-crs-ci.yml:
--------------------------------------------------------------------------------
1 | name: Pixi-CRS CI Pipeline
2 |
3 | on: [push, pull_request]
4 | # Trigger the workflow on push,
5 | # but only for the master branch
6 |
7 | jobs:
8 | build:
9 | name: Start Pixi and the CRS
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@master
14 |
15 | # - name: Debugging
16 | # run: pwd
17 | # - name: Debugging
18 | # run: ls
19 |
20 | - name: Starting Pixi and CRS with docker-compose up
21 | run: docker-compose -f docker-compose.yaml --env-file compose-gcp.env up -d
22 |
23 | # Application Tests with Testcafe
24 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
25 | - name: Run Testcafe Tests Pixi without and with CRS
26 | run: docker run --volume /home/runner/work/pixi-crs/pixi-crs/testcafe/tests_container_ip:/tests --rm testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox'
27 |
28 | # Show Full error.log
29 | - name: Show ModSecurity logs
30 | run: docker exec crs cat /var/log/apache2/error.log
31 |
32 | # ModSecurity Log Analysis:
33 | # Fail if ModSecurity log is not empty
34 | # Show ModSecurity logs of Testcafe Tests
35 | # If not empty -> Repair your application OR
36 | # -> ModSecurity Tuning:
37 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
38 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
39 | - name: Fail if ModSecurity logs are not empty
40 | run: if docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep -vi MyEvilWAFTest | grep -v 949110 | grep -v 980130 | grep msg; then echo "False Positive Found! Aborting!" && exit 1 ; else echo "ModSecurity Logs empty. This is good!"; fi
41 |
42 | # Fail if ModSecurity log does not contain WAF Test String "MyEvilWAFTest"
43 | # That means CRS is not working properly or test was aborted.
44 | - name: Fail if WAF Test String is missing in ModSecurity logs
45 | run: if docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep MyEvilWAFTest; then echo "WAF Test String Found. This is good!"; else echo "WAF Test String not Found! Aborting!" && exit 1; fi
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DevSlop pixi-crs - Integration of OWASP ModSecurity CRS into a CI Pipeline
2 |
3 | This repository is one of DevSlop's modules as described in [devslop.github.io](https://devslop.github.io).
4 |
5 | This repository integrates the WAF ModSecurity with the OWASP ModSecurity Core Rule Set (CRS) and its testing into different CI Pipelines.
6 | Currently the following Pipelines are implemented:
7 |
8 | * CircleCI
9 | * AWS
10 | * Google Cloud Provider
11 | * Azure Pipeline
12 | * GitHub Actions
13 |
14 | The CI Pipelines test (with TestCafe) DevSlop's vulnerable web application Pixi without and with the CRS.
15 |
16 | By adding and testing the WAF in the Continuous Integration (CI) pipeline, we provide the application developer early feedback. The application developers get feedback about how their application will react when behind a WAF. We assure that Pixi’s legitimate traffic is not blocked by the WAF, and that illegitimate traffic is.
17 |
18 | ## Building Blocks of the pixi-crs Pipelines and how they are implemented
19 |
20 | | | CircleCI | AWS | GCP | Azure | GitHub Actions |
21 | | ------------- | -------------------- | --------------------- | ------------- | ------------------------ | --------------------- |
22 | | Code File | .circleci/config.yml | buildspec.yml | cloudbuild.yaml | azure-pipelines.yml | .github/workflows/pixi-crs-ci.yml |
23 | | Start Pixi | docker-compose up | docker-compose up | docker-compose up | docker-compose up | docker-compose up |
24 | | Start CRS | docker run | same docker-compose | same docker-compose | same docker-compose | same docker-compose |
25 | | ModSec Tuning | docker cp | Volume docker-compose | Volume docker-compose | Volume docker-compose | Volume docker-compose |
26 | | Start Testcafe| Testcafe Docker | npm install testcafe | Testcafe Docker | Testcafe Docker | Testcafe Docker |
27 | | Log Analysis | docker exec cat logfile | docker exec cat logfile | docker exec cat logfile | docker exec cat logfile | docker exec cat logfile|
28 |
29 | ## Local Startup of Pixi and CRS
30 |
31 | If you want to start Pixi and the CRS locally you can run:
32 |
33 | `docker-compose --env-file compose-local.env up -d`
34 |
35 | Then you can reach Pixi directly: http://localhost:8000/.
36 | And the WAF with Pixi as the backend: http://localhost:8080/.
37 |
38 | ## Further Reading
39 |
40 | ### Description of the CI Pipeline
41 | * [Pixi-CRS goes to the Cloud: 6 part blog posts series](https://dev.to/franbuehler/series/5552)
42 | * [DevSlop Blog Post on dev.to describing the CircleCI pixi-crs Pipeline](https://dev.to/devslop/devslop-s-pixi-crs-pipeline-4bie)
43 | * [DevSlop Blog Post on dev.to describing how the CRS protects Pixi](https://dev.to/devslop/how-the-owasp-modsecurity-core-rule-set-protects-the-vulnerable-web-application-pixi-by-owasp-devslop-n4d)
44 |
45 | ### Blog Post about Pixi's vulnerabilities and the CRS
46 | * [CRS protects Pixi on coreruleset.org](https://coreruleset.org/20190909/how-the-crs-protects-the-vulnerable-web-application-pixi-by-owasp-devslop/)
47 | * [CRS as Part of DevOps on coreruleset.org](https://coreruleset.org/20180619/the-core-rule-set-as-part-of-devops-ci-pipeline/)
48 |
49 | Also see Testcafe tests of known vulnerabilities in Pixi [in this branch](https://github.com/DevSlop/pixi-crs/tree/test-pixi-vulnerabilities).
50 |
51 | ### Description of first CRS RP Docker Container
52 | * [Description of the first CRS RP (now the changes are integrated into the official OWASP CRS Container)](https://coreruleset.org/20181212/core-rule-set-docker-image/)
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf:
--------------------------------------------------------------------------------
1 | # Rule 920350 (msg: Host header is a numeric IP address) triggers
2 | # because we use IP addresses instead of hostnames in testcafe tests.
3 | # This rule must not be excluded in production!
4 | SecRuleRemoveById 920350
5 |
6 | # Rule 942100 (msg: SQL Injection Attack Detected via libinjection) trigger,
7 | # Rule 942440 (msg: SQL Comment Sequence Detected) and
8 | # Rule 942450 (msg: SQL Hex Encoding Identified) and
9 | # because of random characters in the session cookie.
10 | SecRuleUpdateTargetById 942100 !REQUEST_COOKIES:session
11 | SecRuleUpdateTargetById 942440 !REQUEST_COOKIES:session
12 | SecRuleUpdateTargetById 942450 !REQUEST_COOKIES:session
13 |
14 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | Please report (suspected) security vulnerabilities to owasp.devslop@gmail.com.
6 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # Azure DevOps azure-pipelines.yml
2 |
3 | trigger:
4 | # Branch to trigger
5 | - master
6 |
7 | stages:
8 |
9 | # Start the OWASP ModSecurity Core Rule Set and Pixi with its DB with docker-compose
10 | # OWASP ModSecurity Core Rule Set Container (Apache Reverse Proxy)
11 | # owasp/modsecurity-crs
12 | # See https://coreruleset.org/
13 | # ModSecurity Tuning:
14 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/
15 | - stage: StartContainersAndTests
16 | jobs:
17 | - job: BuildJob
18 | steps:
19 | # Debugging
20 | # - script: pwd
21 | # - script: ls
22 | - task: DockerCompose@0
23 | displayName: Start Pixi and CRS
24 | inputs:
25 | containerregistrytype: 'Container Registry'
26 | dockerComposeFile: '**/docker-compose.yaml'
27 | action: 'Run a Docker Compose command'
28 | dockerComposeFileArgs: |
29 | CRSPORTHTTP=8080
30 | BACKEND=http://app:8000
31 | ERRORLOG=/var/log/apache2/error.log
32 | dockerComposeCommand: 'up -d'
33 |
34 | # Application Tests with Testcafe
35 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
36 | # Another way: https://devexpress.github.io/testcafe/documentation/continuous-integration/azure-devops.html
37 | # Debugging
38 | #- script: docker ps
39 | - script: docker run --volume /home/vsts/work/1/s/testcafe/tests_container_ip/:/tests testcafe/testcafe --skip-js-errors 'chromium:headless --no-sandbox'
40 | displayName: Run Testcafe Tests without and with CRS
41 |
42 | # Show Full error.log
43 | - script: docker exec crs cat /var/log/apache2/error.log
44 | displayName: Show ModSecurity logs
45 |
46 | # ModSecurity Log Analysis:
47 | # Fail if ModSecurity log is not empty
48 | # Show ModSecurity logs of Testcafe Tests
49 | # If not empty -> Repair your application OR
50 | # -> ModSecurity Tuning:
51 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
52 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
53 | - script: if docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep -vi MyEvilWAFTest | grep -v 949110 | grep -v 980130 | grep msg; then echo "False Positive Found! Aborting!" && exit 1 ; else echo "ModSecurity Logs empty. This is good!"; fi
54 | displayName: Fail if ModSecurity logs are not empty
55 |
56 | # Fail if ModSecurity log does not contain WAF Test String "MyEvilWAFTest"
57 | # That means CRS is not working properly or test was aborted.
58 | - script: if docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep MyEvilWAFTest; then echo "WAF Test String Found. This is good!"; else echo "WAF Test String not Found! Aborting!" && exit 1; fi
59 | displayName: Fail if WAF Test String is missing in ModSecurity logs
60 |
61 |
--------------------------------------------------------------------------------
/buildspec.yml:
--------------------------------------------------------------------------------
1 | # AWS buildspec.yml
2 | #
3 | version: 0.2
4 | phases:
5 | install:
6 | runtime-versions:
7 | docker: 19
8 |
9 | build:
10 | commands:
11 | # We install testcafe. We don't run it in Docker, because volumes can not be mounted!
12 | # They probably could be mounted in a docker-compose, but let's run testcafe another way...
13 | - npm install -g testcafe
14 | # Start the OWASP ModSecurity Core Rule Set and Pixi with its DB with docker-compose
15 | # OWASP ModSecurity Core Rule Set Container (Apache Reverse Proxy)
16 | # owasp/modsecurity-crs
17 | # See https://coreruleset.org/
18 | # ModSecurity Tuning:
19 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/
20 |
21 | # We have to install a higher version of docker-compose manually
22 | # so that --env-file is available
23 | # Unfortunately -e does not work, even with a higer docker-compose version
24 | - curl -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
25 | - chmod +x ~/docker-compose
26 | - mv ~/docker-compose /usr/local/bin/docker-compose
27 | - docker-compose -v
28 | - cat compose-aws.env
29 | - docker-compose --env-file compose-aws.env up -d
30 |
31 | # Application Tests with Testcafe
32 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
33 | - testcafe "chrome:headless" testcafe/tests_localhost/test.js --skip-js-errors
34 | # Application Tests with CRS with Testcafe
35 | - testcafe "chrome:headless" testcafe/tests_localhost/testcrs.js --skip-js-errors
36 | # WAF Tests with malicous request to test WAF itself
37 | - testcafe "chrome:headless" testcafe/tests_localhost/testwaf.js --skip-js-errors
38 |
39 | post_build:
40 | commands:
41 | # Fail if ModSecurity log is not empty
42 | # Show ModSecurity logs of Testcafe Tests
43 | - docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep error | grep -vi "MyEvilWAFTest" | grep -v "949110" | grep -vi "980130" && echo "False Positive Found. Check Logs. Aborting!" && exit 1 || exit 0
44 |
45 | # If not empty -> Repair your application OR
46 | # -> ModSecurity Tuning:
47 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
48 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
49 |
50 | # Fail if ModSecurity log does not contain WAF Test String "MyEvilWAFTest"
51 | # That means CRS is not working properly or test was aborted.
52 | - docker exec crs cat /var/log/apache2/error.log | grep -q MyEvilWAFTest
53 |
54 | # Show ModSecurity Full Logs:
55 | - docker exec crs cat /var/log/apache2/error.log | grep ModSecurity | grep msg
56 |
57 |
58 |
--------------------------------------------------------------------------------
/cloudbuild.yaml:
--------------------------------------------------------------------------------
1 | # Google Cloud cloudbuild.yaml
2 | #
3 | steps:
4 | # Start the OWASP ModSecurity Core Rule Set and Pixi with its DB with docker-compose
5 | # OWASP ModSecurity Core Rule Set Container (Apache Reverse Proxy)
6 | # owasp/modsecurity-crs
7 | # See https://coreruleset.org/
8 | # ModSecurity Tuning:
9 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/
10 | - id: 'Starting Pixi and CRS with docker-compose up'
11 | name: 'docker/compose:1.29.2'
12 | args: ['--env-file', '/workspace/compose-gcp.env', 'up', '-d']
13 |
14 | # Debugging possibilities
15 | #- name: 'ubuntu'
16 | # args: [ "touch", "foo" ]
17 | #- name: 'ubuntu'
18 | # args: [ "ls", "-l", "/workspace/testcafe/tests_container_ip" ]
19 | #- name: 'ubuntu'
20 | # args: [ "pwd" ]
21 | # Debugging with curl
22 | #- name: 'curlimages/curl:7.69.0'
23 | # args: [ "-v", "http://172.17.0.1:8000/register"]
24 | #- name: 'curlimages/curl:7.69.0'
25 | # args: [ "-v", "http://172.17.0.1:8080/register"]
26 |
27 | # Application Tests with Testcafe
28 | # skip-js-errors because of: Uncaught Error: Bootstrap tooltips require Tether
29 | - id: 'Run Testcafe Tests: Pixi without and with CRS'
30 | name: 'gcr.io/cloud-builders/docker'
31 | args: [ "run", "--volume", "/workspace/testcafe/tests_container_ip:/tests", "--rm", "testcafe/testcafe", "chromium:headless --no-sandbox", "--skip-js-errors" ]
32 |
33 | # Copy ModSecurity Logs:
34 | - id: 'Copy ModSecurity logs'
35 | name: 'gcr.io/cloud-builders/docker'
36 | # args: [ "exec", "crs", "cat /var/log/apache2/error.log | grep ModSecurity" ]
37 | args: [ "cp", "crs:/var/log/apache2/error.log", "/workspace/error.log" ]
38 |
39 | # Show ModSecurity Logs
40 | - id: 'Show ModSecurity logs'
41 | name: 'gcr.io/cloud-builders/gcloud'
42 | entrypoint: "bash"
43 | args:
44 | - "-c"
45 | - |
46 | cat /workspace/error.log
47 |
48 |
49 | # ModSecurity Log Analysis:
50 | # Fail if ModSecurity log does not contain WAF Test String "MyEvilWAFTest"
51 | # That means CRS is not working properly or test was aborted.
52 |
53 | - id: 'Fail if ModSecurity log does not contain WAF Test String'
54 | name: 'gcr.io/cloud-builders/gcloud'
55 | entrypoint: "bash"
56 | args:
57 | - "-c"
58 | - |
59 | cat /workspace/error.log | grep -q MyEvilWAFTest
60 |
61 | # Fail if ModSecurity log is not empty
62 | # Show ModSecurity logs of Testcafe Tests
63 | # If not empty -> Repair your application OR
64 | # -> ModSecurity Tuning:
65 | # See https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/ OR
66 | # -> GitHub issue: https://github.com/SpiderLabs/owasp-modsecurity-crs
67 | - id: 'Fail if ModSecurity log is not empty'
68 | name: 'gcr.io/cloud-builders/gcloud'
69 | entrypoint: "bash"
70 | args:
71 | - "-c"
72 | - |
73 | cat /workspace/error.log | grep ModSecurity | grep error | grep -vi MyEvilWAFTest | grep -v 949110 | grep -v 980130 && exit 1 || exit 0
74 |
75 | #- id: 'Fail if ModSecurity log is not empty'
76 | # name: 'gcr.io/cloud-builders/docker'
77 | # args: [ 'exec', 'crs', 'cat /var/log/apache2/error.log | grep ModSecurity | grep error | grep -vi "MyEvilWAFTest" | grep -v "949110" | grep -vi "980130" && exit 1 || exit 0' ]
78 |
79 |
80 | # Debugging docker
81 | #- id: 'Docker'
82 | # name: 'gcr.io/cloud-builders/docker'
83 | # args: [ "ps" ]
84 |
--------------------------------------------------------------------------------
/compose-aws.env:
--------------------------------------------------------------------------------
1 | CRSPORTHTTP=80
2 | BACKEND=http://app:8000
3 | ERRORLOG=/var/log/apache2/error.log
4 |
--------------------------------------------------------------------------------
/compose-gcp.env:
--------------------------------------------------------------------------------
1 | CRSPORTHTTP=8080
2 | BACKEND=http://app:8000
3 | ERRORLOG=/var/log/apache2/error.log
4 |
--------------------------------------------------------------------------------
/compose-local.env:
--------------------------------------------------------------------------------
1 | CRSPORTHTTP=8080
2 | BACKEND=http://app:8000
3 | ERRORLOG=/dev/stdout
4 |
--------------------------------------------------------------------------------
/docker-compose-without-crs.yaml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 |
5 | # !!! We can not start crs in docker-compose because volumes are not supported in CircleCI!!!
6 |
7 | # Comment in crs part if you want to add a WAF in front of Pixi:
8 | # crs:
9 | # image: owasp/modsecurity-crs:3.3-apache
10 | # container_name: crs
11 | # ports:
12 | # - "8080:80"
13 | # - "8443:443"
14 | #
15 | # environment:
16 | # # Application Backend listens on port 8000 (IP: REPLACEME, but do not use localhost!)
17 | # - BACKEND=http://app:8000
18 | # # Paranoia Level
19 | # - PARANOIA=2
20 | # # Inbound and Outbound Anomaly Score Threshold
21 | # - ANOMALYIN=1
22 | # - ANOMALYOUT=1
23 | # # Executing Paranoia Level
24 | # # - EXECUTING_PARANOIA=2
25 | #
26 | # # Various CRS Variables with Default Values
27 | # #- ENFORCE_BODYPROC_URLENCODED=1
28 | # - ALLOWED_METHODS=GET HEAD POST OPTIONS PUT
29 | # #- ALLOWED_REQUEST_CONTENT_TYPE=application/x-www-form-urlencoded|multipart/form-data|text/xml|application/xml|application/soap+xml|application/x-amf|application/json|application/octet-stream|text/plain
30 | # #- ALLOWED_REQUEST_CONTENT_TYPE_CHARSET=utf-8|iso-8859-1|iso-8859-15|windows-1252
31 | # #- ALLOWED_HTTP_VERSIONS=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
32 | # #- RESTRICTED_EXTENSIONS=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/
33 | # #- RESTRICTED_HEADERS=/proxy/ /lock-token/ /content-range/ /translate/ /if/
34 | # #- STATIC_EXTENSIONS=/.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/
35 | #
36 | # # CRS Variables with Default Value unlimited:
37 | # #- MAX_NUM_ARGS=255
38 | # #- ARG_NAME_LENGTH=100
39 | # #- ARG_LENGTH=400
40 | # #- TOTAL_ARG_LENGTH=64000
41 | # #- MAX_FILE_SIZE=1048576
42 | # #- COMBINED_FILE_SIZES=1048576
43 | #
44 | # # Volumes for ModSecurity Tuning when done with volumes:
45 | # volumes:
46 | # #- ./REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:/etc/modsecurity.d/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
47 | # - ./RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf:/etc/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
48 | #
49 |
50 | db:
51 | image: deadrobots/pixi:datastore
52 | container_name: pixidb
53 | ports:
54 | - "27017:27017"
55 | - "28017:28017"
56 |
57 | app:
58 | image: deadrobots/pixi:app
59 | container_name: pixi
60 | ports:
61 | - "8000:8000"
62 | - "8090:8090"
63 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 |
5 | # Comment in crs part if you want to add a WAF in front of Pixi:
6 | crs:
7 | image: owasp/modsecurity-crs:3.3-apache
8 | container_name: crs
9 | ports:
10 | # 443 is already in use
11 | - "${CRSPORTHTTP}:80"
12 | #- "8443:443" NOT USED FOR TESTS
13 |
14 | environment:
15 | # Application Backend listens on port 8000 (IP: REPLACEME, but do not use localhost!)
16 | - BACKEND=${BACKEND}
17 | # Paranoia Level
18 | - PARANOIA=2
19 | # Inbound and Outbound Anomaly Score Threshold
20 | # - ANOMALY_INBOUND=5
21 | # - ANOMALY_OUTBOUND=4
22 | # Executing Paranoia Level
23 | # - EXECUTING_PARANOIA=2
24 | # Write errorlog to filesystem instead of stdout for later analysis
25 | - ERRORLOG=${ERRORLOG}
26 |
27 | # Various CRS Variables with Default Values
28 | #- ENFORCE_BODYPROC_URLENCODED=1
29 | - ALLOWED_METHODS=GET HEAD POST OPTIONS PUT
30 | #- ALLOWED_REQUEST_CONTENT_TYPE=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain|
31 | #- ALLOWED_REQUEST_CONTENT_TYPE_CHARSET=utf-8|iso-8859-1|iso-8859-15|windows-1252
32 | #- ALLOWED_HTTP_VERSIONS=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
33 | #- RESTRICTED_EXTENSIONS=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/
34 | #- RESTRICTED_HEADERS=/proxy/ /lock-token/ /content-range/ /translate/ /if/
35 | #- STATIC_EXTENSIONS=/.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/
36 |
37 | # CRS Variables with Default Value unlimited:
38 | #- MAX_NUM_ARGS=255
39 | #- ARG_NAME_LENGTH=100
40 | #- ARG_LENGTH=400
41 | #- TOTAL_ARG_LENGTH=64000
42 | #- MAX_FILE_SIZE=1048576
43 | #- COMBINED_FILE_SIZES=1048576
44 |
45 | # Volumes for ModSecurity Tuning when done with volumes:
46 | volumes:
47 | #- ./REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:/etc/modsecurity.d/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
48 | - ./RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf:/etc/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
49 |
50 |
51 | db:
52 | image: deadrobots/pixi:datastore
53 | container_name: pixidb
54 | ports:
55 | - "27017:27017"
56 | - "28017:28017"
57 |
58 | app:
59 | image: deadrobots/pixi:app
60 | container_name: pixi
61 | ports:
62 | - "8000:8000"
63 | - "8090:8090"
64 |
65 |
--------------------------------------------------------------------------------
/img/AWS/AWS_CodeBuild_Logs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AWS/AWS_CodeBuild_Logs.png
--------------------------------------------------------------------------------
/img/AWS/AWS_Pipeline_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AWS/AWS_Pipeline_Output.png
--------------------------------------------------------------------------------
/img/AWS/AWS_PixiCRS_BuildProject.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AWS/AWS_PixiCRS_BuildProject.png
--------------------------------------------------------------------------------
/img/AWS/AWS_Testcafe_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AWS/AWS_Testcafe_Output.png
--------------------------------------------------------------------------------
/img/AWS/AWS_Title_Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AWS/AWS_Title_Image.png
--------------------------------------------------------------------------------
/img/AzureDevOps/Azure_Pipeline_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/Azure_Pipeline_Output.png
--------------------------------------------------------------------------------
/img/AzureDevOps/Azure_Testcafe_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/Azure_Testcafe_Output.png
--------------------------------------------------------------------------------
/img/AzureDevOps/Azure_Title_Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/Azure_Title_Image.png
--------------------------------------------------------------------------------
/img/AzureDevOps/azure-1-new-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/azure-1-new-project.png
--------------------------------------------------------------------------------
/img/AzureDevOps/azure-2-create-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/azure-2-create-pipeline.png
--------------------------------------------------------------------------------
/img/AzureDevOps/azure-3-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/azure-3-github.png
--------------------------------------------------------------------------------
/img/AzureDevOps/azure-4-select-repo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/azure-4-select-repo.png
--------------------------------------------------------------------------------
/img/AzureDevOps/azure-5-review-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/AzureDevOps/azure-5-review-pipeline.png
--------------------------------------------------------------------------------
/img/CircleCI/CircleCI_Pipeline_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/CircleCI/CircleCI_Pipeline_Output.png
--------------------------------------------------------------------------------
/img/GCP/GCP_Testcafe_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/GCP/GCP_Testcafe_Output.png
--------------------------------------------------------------------------------
/img/GCP/GCP_Title_Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/GCP/GCP_Title_Image.png
--------------------------------------------------------------------------------
/img/GitHub_Actions/GHA_Pipeline_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/GitHub_Actions/GHA_Pipeline_Output.png
--------------------------------------------------------------------------------
/img/GitHub_Actions/GHA_Testcafe_Output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevSlop/pixi-crs/4ed481c93e4d1066487db0af1f43a05b20d504e7/img/GitHub_Actions/GHA_Testcafe_Output.png
--------------------------------------------------------------------------------
/testcafe/tests_container_ip/test.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against Pixi directly for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.1:8000/register`;
6 |
7 | test('Register User', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
14 | test('Login User', async t => {
15 | await t
16 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
17 | .typeText('input#user', 'testuser@pixi.owasp')
18 | .typeText('input#pass', 'testpw')
19 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
20 | });
21 |
22 | test('Search String in Search Box', async t => {
23 | await t
24 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
25 | .typeText('input#user', 'testuser@pixi.owasp')
26 | .typeText('input#pass', 'testpw')
27 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
28 | //After login we search for Lunch
29 | .typeText('#search_query', 'Lunch')
30 | .pressKey('enter')
31 | });
32 |
33 | test('Click About', async t => {
34 | await t
35 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
36 | .typeText('input#user', 'testuser@pixi.owasp')
37 | .typeText('input#pass', 'testpw')
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
39 | //After login we click About
40 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
41 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
42 | });
43 |
44 | test('Click My Profile and change Name', async t => {
45 | await t
46 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
47 | .typeText('input#user', 'testuser@pixi.owasp')
48 | .typeText('input#pass', 'testpw')
49 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
50 | //After login we click My Profile
51 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
52 | //After clicking My Profile we change our name
53 | .typeText('input#password', 'testpw')
54 | .typeText('input#name', 'testuser')
55 | .pressKey('enter')
56 | });
57 |
58 | test('Logout User', async t => {
59 | await t
60 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
61 | .typeText('input#user', 'testuser@pixi.owasp')
62 | .typeText('input#pass', 'testpw')
63 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
64 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
65 | });
66 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip/testcrs.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against CRS for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.1:8080/register`;
6 |
7 | test('Register User WITH CRS', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
14 | test('Login User WITH CRS', async t => {
15 | await t
16 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
17 | .typeText('input#user', 'testuser@pixi.owasp')
18 | .typeText('input#pass', 'testpw')
19 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
20 | });
21 |
22 | test('Search String in Search Box WITH CRS', async t => {
23 | await t
24 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
25 | .typeText('input#user', 'testuser@pixi.owasp')
26 | .typeText('input#pass', 'testpw')
27 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
28 | //Evil demo, let step fail
29 | //.typeText('#search_query', '')
30 | .pressKey('enter')
31 | });
32 |
33 | test('Click About WITH CRS', async t => {
34 | await t
35 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
36 | .typeText('input#user', 'testuser@pixi.owasp')
37 | .typeText('input#pass', 'testpw')
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
39 | //After login we click About
40 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
41 | });
42 |
43 | test('Click My Profile and change Name WITH CRS', async t => {
44 | await t
45 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
46 | .typeText('input#user', 'testuser@pixi.owasp')
47 | .typeText('input#pass', 'testpw')
48 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
49 | //After login we click My Profile
50 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
51 | //After clicking My Profile we change our name
52 | .typeText('input#password', 'testpw')
53 | .typeText('input#name', 'testuser')
54 | .pressKey('enter')
55 | });
56 |
57 | test('Logout User WITH CRS', async t => {
58 | await t
59 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
60 | .typeText('input#user', 'testuser@pixi.owasp')
61 | .typeText('input#pass', 'testpw')
62 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
63 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
64 | });
65 |
66 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip/testwaf.js:
--------------------------------------------------------------------------------
1 | // One malicious Pixi Application Test against CRS for CI Pipeline to test the WAF itself
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.1:8080/login`;
6 |
7 | test('WAF Test with malicious string', async t => {
8 | await t
9 | .typeText('input#user', 'testuser@pixi.owasp')
10 | .typeText('input#pass', '')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/mongodb.js:
--------------------------------------------------------------------------------
1 | // Evil MongoDB requests against Pixi directly for CI Pipeline (Demo)
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.1:8000/register`;
6 |
7 | test('Register User', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | //After registration the search pixi field should be there
13 | //.expect('#search_query')
14 | });
15 |
16 | test('Login and Search', async t => {
17 | await t
18 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
19 | .typeText('input#user', 'testuser@pixi.owasp')
20 | .typeText('input#pass', 'testpw')
21 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
22 | //After login we search for Lunch
23 | .typeText('#search_query', 'true, $where: \'1 == 1\'')
24 | .pressKey('enter')
25 | .typeText('#search_query', ', $where: \'1 == 1\'')
26 | .pressKey('enter')
27 | .typeText('#search_query', '$where: \'1 == 1\'')
28 | .pressKey('enter')
29 | .typeText('#search_query', '\', $where: \'1 == 1\'')
30 | .pressKey('enter')
31 | .typeText('#search_query', '1, $where: \'1 == 1\'')
32 | .pressKey('enter')
33 | .typeText('#search_query', '{ $ne: 1 }')
34 | .pressKey('enter')
35 | .typeText('#search_query', '\', $or: [ {}, { \'a\':\'a')
36 | .pressKey('enter')
37 | .typeText('#search_query', '\' } ], $comment:\'successful MongoDB injection\'')
38 | .pressKey('enter')
39 | .typeText('#search_query', 'db.injection.insert({success:1});')
40 | .pressKey('enter')
41 | .typeText('#search_query', 'db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1')
42 | .pressKey('enter')
43 | .typeText('#search_query', '|| 1==1')
44 | .pressKey('enter')
45 | .typeText('#search_query', '\' && this.password.match(/.*/)//+%00')
46 | .pressKey('enter')
47 | .typeText('#search_query', '\' && this.passwordzz.match(/.*/)//+%00')
48 | .pressKey('enter')
49 | .typeText('#search_query', '\'%20%26%26%20this.password.match(/.*/)//+%00')
50 | .pressKey('enter')
51 | .typeText('#search_query', '\'%20%26%26%20this.passwordzz.match(/.*/)//+%00')
52 | .pressKey('enter')
53 | .typeText('#search_query', '{$gt: \'\'}')
54 | .pressKey('enter')
55 | .typeText('#search_query', '[$ne]=1')
56 | .pressKey('enter')
57 | .typeText('#search_query', '\';sleep(5000);')
58 | .pressKey('enter')
59 | .typeText('#search_query', '\';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);')
60 | .pressKey('enter')
61 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
62 | });
63 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/mongodbcrs.js:
--------------------------------------------------------------------------------
1 | // Evil MongoDB requests against CRS for CI Pipeline (Demo)
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.2/register`;
6 |
7 | test('Register User WITH CRS', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | //After registration the search pixi field should be there
13 | //.expect('#search_query')
14 | });
15 |
16 | test('Login and Search', async t => {
17 | await t
18 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
19 | .typeText('input#user', 'testuser@pixi.owasp')
20 | .typeText('input#pass', 'testpw')
21 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
22 | //After login we search for Lunch
23 | .typeText('#search_query', '[$exists]')
24 | .pressKey('enter')
25 | .typeText('#search_query', 'true, $where: \'1 == 1\'')
26 | .pressKey('enter')
27 | .typeText('#search_query', ', $where: \'1 == 1\'')
28 | .pressKey('enter')
29 | .typeText('#search_query', '$where: \'1 == 1\'')
30 | .pressKey('enter')
31 | .typeText('#search_query', '\', $where: \'1 == 1\'')
32 | .pressKey('enter')
33 | .typeText('#search_query', '1, $where: \'1 == 1\'')
34 | .pressKey('enter')
35 | .typeText('#search_query', '{ $ne: 1 }')
36 | .pressKey('enter')
37 | .typeText('#search_query', '\', $or: [ {}, { \'a\':\'a')
38 | .pressKey('enter')
39 | .typeText('#search_query', '\' } ], $comment:\'successful MongoDB injection\'')
40 | .pressKey('enter')
41 | .typeText('#search_query', 'db.injection.insert({success:1});')
42 | .pressKey('enter')
43 | .typeText('#search_query', 'db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1')
44 | .pressKey('enter')
45 | .typeText('#search_query', '|| 1==1')
46 | .pressKey('enter')
47 | .typeText('#search_query', '\' && this.password.match(/.*/)//+%00')
48 | .pressKey('enter')
49 | .typeText('#search_query', '\' && this.passwordzz.match(/.*/)//+%00')
50 | .pressKey('enter')
51 | .typeText('#search_query', '\'%20%26%26%20this.password.match(/.*/)//+%00')
52 | .pressKey('enter')
53 | .typeText('#search_query', '\'%20%26%26%20this.passwordzz.match(/.*/)//+%00')
54 | .pressKey('enter')
55 | .typeText('#search_query', '{$gt: \'\'}')
56 | .pressKey('enter')
57 | .typeText('#search_query', '[$ne]=1')
58 | .pressKey('enter')
59 | .typeText('#search_query', '\';sleep(5000);')
60 | .pressKey('enter')
61 | .typeText('#search_query', '\';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);')
62 | .pressKey('enter')
63 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
64 | });
65 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/test.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against Pixi directly for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.1:8000/register`;
6 |
7 | test('Register User', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | //After registration the search pixi field should be there
13 | //.expect('#search_query')
14 | });
15 |
16 | test('Login User', async t => {
17 | await t
18 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
19 | .typeText('input#user', 'testuser@pixi.owasp')
20 | .typeText('input#pass', 'testpw')
21 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
22 | });
23 |
24 | test('Search String in Search Box', async t => {
25 | await t
26 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
27 | .typeText('input#user', 'testuser@pixi.owasp')
28 | .typeText('input#pass', 'testpw')
29 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
30 | //After login we search for Lunch
31 | .typeText('#search_query', 'Lunch')
32 | .pressKey('enter')
33 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
34 | });
35 |
36 | test('Click About', async t => {
37 | await t
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
39 | .typeText('input#user', 'testuser@pixi.owasp')
40 | .typeText('input#pass', 'testpw')
41 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
42 | //After login we click About
43 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
44 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
45 | });
46 |
47 | test('Click My Profile and change Name', async t => {
48 | await t
49 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
50 | .typeText('input#user', 'testuser@pixi.owasp')
51 | .typeText('input#pass', 'testpw')
52 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
53 | //After login we click My Profile
54 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
55 | //After clicking My Profile we change our name
56 | .typeText('input#password', 'testpw')
57 | .typeText('input#name', 'testuser')
58 | .pressKey('enter')
59 | });
60 |
61 | test('Logout User', async t => {
62 | await t
63 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
64 | .typeText('input#user', 'testuser@pixi.owasp')
65 | .typeText('input#pass', 'testpw')
66 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
67 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
68 | });
69 |
70 | test('Repeat Login for Video', async t => {
71 | await t
72 | .typeText('input#email', 'testuser@pixi.owasp')
73 | .typeText('input#password', 'testpw')
74 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
75 | //After registration the search pixi field should be there
76 | //.expect('#search_query')
77 | });
78 |
79 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/test_local.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against Pixi directly for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://192.168.1.104:8000/register`;
6 |
7 | test('Register User', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | //After registration the search pixi field should be there
13 | //.expect('#search_query')
14 | });
15 |
16 | test('Login User', async t => {
17 | await t
18 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
19 | .typeText('input#user', 'testuser@pixi.owasp')
20 | .typeText('input#pass', 'testpw')
21 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
22 | });
23 |
24 | test('Search String in Search Box', async t => {
25 | await t
26 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
27 | .typeText('input#user', 'testuser@pixi.owasp')
28 | .typeText('input#pass', 'testpw')
29 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
30 | //After login we search for Lunch
31 | .typeText('#search_query', 'Lunch')
32 | .pressKey('enter')
33 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
34 | });
35 |
36 | test('Click About', async t => {
37 | await t
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
39 | .typeText('input#user', 'testuser@pixi.owasp')
40 | .typeText('input#pass', 'testpw')
41 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
42 | //After login we click About
43 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
44 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
45 | });
46 |
47 | test('Click My Profile and change Name', async t => {
48 | await t
49 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
50 | .typeText('input#user', 'testuser@pixi.owasp')
51 | .typeText('input#pass', 'testpw')
52 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
53 | //After login we click My Profile
54 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
55 | //After clicking My Profile we change our name
56 | .typeText('input#password', 'testpw')
57 | .typeText('input#name', 'testuser')
58 | .pressKey('enter')
59 | });
60 |
61 | test('Logout User', async t => {
62 | await t
63 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
64 | .typeText('input#user', 'testuser@pixi.owasp')
65 | .typeText('input#pass', 'testpw')
66 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
67 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
68 | });
69 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/testcrs-match.js:
--------------------------------------------------------------------------------
1 | import { Selector } from 'testcafe';
2 |
3 | fixture`Getting Started`
4 | .page `http://172.17.0.2/register`;
5 |
6 | test('Register User WITH CRS', async t => {
7 | await t
8 | .typeText('input#email', 'testuser@pixi.owasp')
9 | .typeText('input#password', 'testpw')
10 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
11 | //After registration the search pixi field should be there
12 | //.expect('#search_query')
13 | });
14 |
15 | test('Login User WITH CRS', async t => {
16 | await t
17 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
18 | .typeText('input#user', 'testuser@pixi.owasp')
19 | .typeText('input#pass', 'testpw')
20 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
21 | .navigateTo('http://172.17.0.2/register?$select=userid')
22 | });
23 |
24 | test('Search String WITH CRS', async t => {
25 | await t
26 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
27 | .typeText('input#user', 'testuser@pixi.owasp')
28 | .typeText('input#pass', 'testpw')
29 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
30 | //After login we search for Lunch
31 | .typeText('#search_query', 'Lunch')
32 | .pressKey('enter')
33 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
34 | });
35 |
36 | test('Logout User WITH CRS', async t => {
37 | await t
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
39 | .typeText('input#user', 'testuser@pixi.owasp')
40 | .typeText('input#pass', 'testpw')
41 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
42 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
43 | });
44 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/testcrs.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against CRS for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | // Use 172.17.0.2 instead of 172.17.0.1 if crs is not started via docker-compose:
6 | .page `http://172.17.0.2/register`;
7 |
8 | test('Register User WITH CRS', async t => {
9 | await t
10 | .typeText('input#email', 'testuser@pixi.owasp')
11 | .typeText('input#password', 'testpw')
12 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
13 | //After registration the search pixi field should be there
14 | //.expect('#search_query')
15 | });
16 |
17 | test('Login User WITH CRS', async t => {
18 | await t
19 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
20 | .typeText('input#user', 'testuser@pixi.owasp')
21 | .typeText('input#pass', 'testpw')
22 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
23 | });
24 |
25 | test('Search String in Search Box WITH CRS', async t => {
26 | await t
27 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
28 | .typeText('input#user', 'testuser@pixi.owasp')
29 | .typeText('input#pass', 'testpw')
30 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
31 | //Evil demo, let step fail
32 | //.typeText('#search_query', '')
33 | //.pressKey('enter')
34 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
35 | });
36 |
37 | test('Click About WITH CRS', async t => {
38 | await t
39 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
40 | .typeText('input#user', 'testuser@pixi.owasp')
41 | .typeText('input#pass', 'testpw')
42 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
43 | //After login we click About
44 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
45 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
46 | });
47 |
48 | test('Click My Profile and change Name WITH CRS', async t => {
49 | await t
50 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
51 | .typeText('input#user', 'testuser@pixi.owasp')
52 | .typeText('input#pass', 'testpw')
53 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
54 | //After login we click My Profile
55 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
56 | //After clicking My Profile we change our name
57 | .typeText('input#password', 'testpw')
58 | .typeText('input#name', 'testuser')
59 | .pressKey('enter')
60 | });
61 |
62 | test('Logout User WITH CRS', async t => {
63 | await t
64 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
65 | .typeText('input#user', 'testuser@pixi.owasp')
66 | .typeText('input#pass', 'testpw')
67 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
68 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
69 | });
70 |
71 |
--------------------------------------------------------------------------------
/testcafe/tests_container_ip2/testwaf.js:
--------------------------------------------------------------------------------
1 | // One malicious Pixi Application Test against CRS for CI Pipeline to test the WAF itself
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://172.17.0.2/register`;
6 |
7 | test('Register User WITH CRS', async t => {
8 | await t
9 | .typeText('input#email', 'test@test>')
10 | .typeText('input#password', '')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | //After registration the search pixi field should be there
13 | //.expect('#search_query')
14 | });
15 |
--------------------------------------------------------------------------------
/testcafe/tests_localhost/test.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against Pixi directly for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://localhost:8000/register`;
6 |
7 | test('Register User', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
14 | test('Login User', async t => {
15 | await t
16 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
17 | .typeText('input#user', 'testuser@pixi.owasp')
18 | .typeText('input#pass', 'testpw')
19 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
20 | });
21 |
22 | test('Search String in Search Box', async t => {
23 | await t
24 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
25 | .typeText('input#user', 'testuser@pixi.owasp')
26 | .typeText('input#pass', 'testpw')
27 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
28 | //After login we search for Lunch
29 | .typeText('#search_query', 'Lunch')
30 | .pressKey('enter')
31 | });
32 |
33 | test('Click About', async t => {
34 | await t
35 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
36 | .typeText('input#user', 'testuser@pixi.owasp')
37 | .typeText('input#pass', 'testpw')
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
39 | //After login we click About
40 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
41 | //.expect('body > div > div:nth-child(12) > div > div.card-deck')
42 | });
43 |
44 | test('Click My Profile and change Name', async t => {
45 | await t
46 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
47 | .typeText('input#user', 'testuser@pixi.owasp')
48 | .typeText('input#pass', 'testpw')
49 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
50 | //After login we click My Profile
51 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
52 | //After clicking My Profile we change our name
53 | .typeText('input#password', 'testpw')
54 | .typeText('input#name', 'testuser')
55 | .pressKey('enter')
56 | });
57 |
58 | test('Logout User', async t => {
59 | await t
60 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
61 | .typeText('input#user', 'testuser@pixi.owasp')
62 | .typeText('input#pass', 'testpw')
63 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
64 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
65 | });
66 |
--------------------------------------------------------------------------------
/testcafe/tests_localhost/testcrs.js:
--------------------------------------------------------------------------------
1 | // Normal Pixi Application Tests against CRS for CI Pipeline
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://localhost/register`;
6 |
7 | test('Register User WITH CRS', async t => {
8 | await t
9 | .typeText('input#email', 'testuser@pixi.owasp')
10 | .typeText('input#password', 'testpw')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
14 | test('Login User WITH CRS', async t => {
15 | await t
16 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
17 | .typeText('input#user', 'testuser@pixi.owasp')
18 | .typeText('input#pass', 'testpw')
19 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
20 | });
21 |
22 | test('Search String in Search Box WITH CRS', async t => {
23 | await t
24 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
25 | .typeText('input#user', 'testuser@pixi.owasp')
26 | .typeText('input#pass', 'testpw')
27 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
28 | //Evil demo, let step fail
29 | //.typeText('#search_query', '')
30 | .pressKey('enter')
31 | });
32 |
33 | test('Click About WITH CRS', async t => {
34 | await t
35 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
36 | .typeText('input#user', 'testuser@pixi.owasp')
37 | .typeText('input#pass', 'testpw')
38 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
39 | //After login we click About
40 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(1) > a')
41 | });
42 |
43 | test('Click My Profile and change Name WITH CRS', async t => {
44 | await t
45 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
46 | .typeText('input#user', 'testuser@pixi.owasp')
47 | .typeText('input#pass', 'testpw')
48 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
49 | //After login we click My Profile
50 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(3) > a')
51 | //After clicking My Profile we change our name
52 | .typeText('input#password', 'testpw')
53 | .typeText('input#name', 'testuser')
54 | .pressKey('enter')
55 | });
56 |
57 | test('Logout User WITH CRS', async t => {
58 | await t
59 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-4 > a:nth-child(3) > button')
60 | .typeText('input#user', 'testuser@pixi.owasp')
61 | .typeText('input#pass', 'testpw')
62 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
63 | .click('body > div > div:nth-child(1) > div.col-md-7 > ul > li:nth-child(4) > a')
64 | });
65 |
66 |
--------------------------------------------------------------------------------
/testcafe/tests_localhost/testwaf.js:
--------------------------------------------------------------------------------
1 | // One malicious Pixi Application Test against CRS for CI Pipeline to test the WAF itself
2 | import { Selector } from 'testcafe';
3 |
4 | fixture`Getting Started`
5 | .page `http://localhost/login`;
6 |
7 | test('WAF Test with malicious string', async t => {
8 | await t
9 | .typeText('input#user', 'testuser@pixi.owasp')
10 | .typeText('input#pass', '')
11 | .click('body > div.jumbotron.jumbotron-fluid > div > div:nth-child(5) > div.col-8 > form > button')
12 | });
13 |
--------------------------------------------------------------------------------
/zap/zap-api.conf:
--------------------------------------------------------------------------------
1 | # zap-api-scan rule configuration file
2 | # Change WARN to IGNORE to ignore rule or FAIL to fail if rule matches
3 | # Active scan rules set to IGNORE will not be run which will speed up the scan
4 | # Only the rule identifiers are used - the names are just for info
5 | # You can add your own messages to each rule by appending them after a tab on each line.
6 | 0 WARN (Directory Browsing - Active/release)
7 | 10010 WARN (Cookie No HttpOnly Flag - Passive/release)
8 | 10011 WARN (Cookie Without Secure Flag - Passive/release)
9 | 10012 WARN (Password Autocomplete in Browser - Passive/release)
10 | 10015 WARN (Incomplete or No Cache-control and Pragma HTTP Header Set - Passive/release)
11 | 10016 WARN (Web Browser XSS Protection Not Enabled - Passive/release)
12 | 10017 WARN (Cross-Domain JavaScript Source File Inclusion - Passive/release)
13 | 10019 WARN (Content-Type Header Missing - Passive/release)
14 | 10020 WARN (X-Frame-Options Header Scanner - Passive/release)
15 | 10021 WARN (X-Content-Type-Options Header Missing - Passive/release)
16 | 10023 WARN (Information Disclosure - Debug Error Messages - Passive/beta)
17 | 10024 WARN (Information Disclosure - Sensitive Informations in URL - Passive/beta)
18 | 10025 WARN (Information Disclosure - Sensitive Information in HTTP Referrer Header - Passive/beta)
19 | 10026 WARN (HTTP Parameter Override - Passive/beta)
20 | 10027 WARN (Information Disclosure - Suspicious Comments - Passive/beta)
21 | 10032 WARN (Viewstate Scanner - Passive/beta)
22 | 10040 WARN (Secure Pages Include Mixed Content - Passive/release)
23 | 10045 WARN (Source Code Disclosure - /WEB-INF folder - Active/beta)
24 | 10048 WARN (Remote Code Execution - Shell Shock - Active/beta)
25 | 10095 WARN (Backup File Disclosure - Active/beta)
26 | 10105 WARN (Weak Authentication Method - Passive/beta)
27 | 10202 WARN (Absence of Anti-CSRF Tokens - Passive/beta)
28 | 2 WARN (Private IP Disclosure - Passive/release)
29 | 20012 WARN (Anti CSRF Tokens Scanner - Active/beta)
30 | 20014 WARN (HTTP Parameter Pollution scanner - Active/beta)
31 | 20015 WARN (Heartbleed OpenSSL Vulnerability - Active/beta)
32 | 20016 WARN (Cross-Domain Misconfiguration - Active/beta)
33 | 20017 WARN (Source Code Disclosure - CVE-2012-1823 - Active/beta)
34 | 20018 WARN (Remote Code Execution - CVE-2012-1823 - Active/beta)
35 | 20019 WARN (External Redirect - Active/release)
36 | 3 WARN (Session ID in URL Rewrite - Passive/release)
37 | 30001 WARN (Buffer Overflow - Active/release)
38 | 30002 WARN (Format String Error - Active/release)
39 | 30003 WARN (Integer Overflow Error - Active/beta)
40 | 40003 WARN (CRLF Injection - Active/release)
41 | 40008 WARN (Parameter Tampering - Active/release)
42 | 40009 WARN (Server Side Include - Active/release)
43 | 40012 WARN (Cross Site Scripting (Reflected) - Active/release)
44 | 40013 WARN (Session Fixation - Active/beta)
45 | 40014 WARN (Cross Site Scripting (Persistent) - Active/release)
46 | 40016 WARN (Cross Site Scripting (Persistent) - Prime - Active/release)
47 | 40017 WARN (Cross Site Scripting (Persistent) - Spider - Active/release)
48 | 40018 WARN (SQL Injection - Active/release)
49 | 40019 WARN (SQL Injection - MySQL - Active/beta)
50 | 40020 WARN (SQL Injection - Hypersonic SQL - Active/beta)
51 | 40021 WARN (SQL Injection - Oracle - Active/beta)
52 | 40022 WARN (SQL Injection - PostgreSQL - Active/beta)
53 | 40023 WARN (Possible Username Enumeration - Active/beta)
54 | 42 WARN (Source Code Disclosure - SVN - Active/beta)
55 | 50000 WARN (Script Active Scan Rules - Active/release)
56 | 50001 WARN (Script Passive Scan Rules - Passive/release)
57 | 6 WARN (Path Traversal - Active/release)
58 | 7 WARN (Remote File Inclusion - Active/release)
59 | 90001 WARN (Insecure JSF ViewState - Passive/beta)
60 | 90011 WARN (Charset Mismatch - Passive/beta)
61 | 90019 WARN (Server Side Code Injection - Active/release)
62 | 90020 WARN (Remote OS Command Injection - Active/release)
63 | 90021 WARN (XPath Injection - Active/beta)
64 | 90022 WARN (Application Error Disclosure - Passive/release)
65 | 90023 WARN (XML External Entity Attack - Active/beta)
66 | 90024 WARN (Generic Padding Oracle - Active/beta)
67 | 90025 WARN (Expression Language Injection - Active/beta)
68 | 90026 WARN (SOAP Action Spoofing - Active/alpha)
69 | 90028 WARN (Insecure HTTP Method - Active/beta)
70 | 90029 WARN (SOAP XML Injection - Active/alpha)
71 | 90030 WARN (WSDL File Passive Scanner - Passive/alpha)
72 | 90033 WARN (Loosely Scoped Cookie - Passive/beta)
73 |
--------------------------------------------------------------------------------
/zap/zap-baseline.conf:
--------------------------------------------------------------------------------
1 | # zap-baseline rule configuration file
2 | # change FAIL to IGNORE to ignore rule or FAIL to fail if rule matches
3 | # only the rule identifiers are used - the names are just for info
4 | 10010 FAIL (Cookie No HttpOnly Flag)
5 | 10011 FAIL (Cookie Without Secure Flag)
6 | 10012 IGNORE (Password Autocomplete in browser)
7 | 10016 FAIL (Web Browser XSS Protection Not Enabled)
8 | 10017 FAIL (Cross-Domain JavaScript Source File Inclusion)
9 | 10019 FAIL (Content-Type Header Missing)
10 | 10020 FAIL (X-Frame-Options Header Not Set)
11 | 10021 FAIL (X-Content-Type-Options Header Missing)
12 | 10034 FAIL (Heartbleed OpenSSL Vulnerability (Indicative))
13 | 10035 FAIL (Strict-Transport-Security Header Not Set)
14 | 10038 FAIL (Content Security Policy (CSP) Header Not Set)
15 | 10040 FAIL (Secure Pages Include Mixed Content)
16 | 10052 FAIL (X-ChromeLogger-Data (XCOLD) Header Information Leak)
17 | 10098 FAIL (Cross-Domain Misconfiguration)
18 | 40014 FAIL (Absence of Anti-CSRF Tokens)
19 |
--------------------------------------------------------------------------------