├── .dockerignore ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── ci.yaml │ ├── close-issues.yml │ ├── nightly-coraza-check.yaml │ └── release_notes.sh ├── .gitignore ├── .golangci.yml ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── README.md ├── e2e ├── coraza │ ├── docker-compose.yml │ └── envoy-config.yaml └── http_trailer │ ├── docker-compose.yml │ └── envoy-config.yaml ├── example ├── envoy │ ├── docker-compose.yml │ ├── envoy-config.yaml │ ├── grafana │ │ ├── dashboard.json │ │ ├── dashboard.yaml │ │ ├── datasource.yaml │ │ └── grafana.ini │ └── prometheus.yaml └── istio │ └── README.md ├── ftw ├── Dockerfile ├── docker-compose.yml ├── envoy-config-nowasm.yaml ├── envoy-config.yaml ├── ftw.yml └── tests.sh ├── go.mod ├── go.sum ├── go.work ├── init_tinygo.go ├── internal ├── auditlog │ └── serial_writer.go └── operators │ ├── operators.go │ └── operators_go.go ├── lifecycle_multiphase_test.go ├── mage.go ├── magefiles ├── e2etest.go ├── go.mod ├── go.sum ├── loadtest.go └── magefile.go ├── main.go ├── main_test.go ├── rule_option.go ├── rule_option_multiphase.go └── wasmplugin ├── config.go ├── config_test.go ├── fs.go ├── logger.go ├── memstats.go ├── memstats_on.go ├── metrics.go ├── plugin.go ├── plugin_test.go ├── rules ├── coraza-demo.conf ├── coraza.conf-recommended.conf ├── crs-setup.conf.example ├── crs │ ├── REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example │ ├── REQUEST-901-INITIALIZATION.conf │ ├── REQUEST-905-COMMON-EXCEPTIONS.conf │ ├── REQUEST-911-METHOD-ENFORCEMENT.conf │ ├── REQUEST-913-SCANNER-DETECTION.conf │ ├── REQUEST-920-PROTOCOL-ENFORCEMENT.conf │ ├── REQUEST-921-PROTOCOL-ATTACK.conf │ ├── REQUEST-922-MULTIPART-ATTACK.conf │ ├── REQUEST-930-APPLICATION-ATTACK-LFI.conf │ ├── REQUEST-931-APPLICATION-ATTACK-RFI.conf │ ├── REQUEST-932-APPLICATION-ATTACK-RCE.conf │ ├── REQUEST-933-APPLICATION-ATTACK-PHP.conf │ ├── REQUEST-934-APPLICATION-ATTACK-GENERIC.conf │ ├── REQUEST-941-APPLICATION-ATTACK-XSS.conf │ ├── REQUEST-942-APPLICATION-ATTACK-SQLI.conf │ ├── REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf │ ├── REQUEST-944-APPLICATION-ATTACK-JAVA.conf │ ├── REQUEST-949-BLOCKING-EVALUATION.conf │ ├── RESPONSE-950-DATA-LEAKAGES.conf │ ├── RESPONSE-951-DATA-LEAKAGES-SQL.conf │ ├── RESPONSE-952-DATA-LEAKAGES-JAVA.conf │ ├── RESPONSE-953-DATA-LEAKAGES-PHP.conf │ ├── RESPONSE-954-DATA-LEAKAGES-IIS.conf │ ├── RESPONSE-955-WEB-SHELLS.conf │ ├── RESPONSE-959-BLOCKING-EVALUATION.conf │ ├── RESPONSE-980-CORRELATION.conf │ ├── RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example │ ├── iis-errors.data │ ├── java-classes.data │ ├── java-errors.data │ ├── lfi-os-files.data │ ├── php-config-directives.data │ ├── php-errors-pl2.data │ ├── php-errors.data │ ├── php-function-names-933150.data │ ├── php-function-names-933151.data │ ├── php-variables.data │ ├── restricted-files.data │ ├── restricted-upload.data │ ├── scanners-user-agents.data │ ├── sql-errors.data │ ├── ssrf.data │ ├── unix-shell.data │ ├── web-shells-asp.data │ ├── web-shells-php.data │ └── windows-powershell-commands.data └── ftw-config.conf ├── timing.go └── timing_on.go /.dockerignore: -------------------------------------------------------------------------------- 1 | !build -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | wasmplugin/rules/crs/* linguist-generated 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | allow: 8 | - dependency-name: "github.com/corazawaf/coraza/v3" 9 | dependency-type: "direct" 10 | target-branch: "main" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - "*" 8 | paths-ignore: 9 | - "**/*.md" 10 | - "LICENSE" 11 | pull_request: 12 | workflow_dispatch: 13 | 14 | env: 15 | GO_VERSION: '1.23' 16 | TINYGO_VERSION: 0.34.0 17 | # Run e2e tests against latest two releases and latest dev 18 | ENVOY_IMAGES: > 19 | envoyproxy/envoy:v1.33-latest 20 | envoyproxy/envoy:v1.32-latest 21 | envoyproxy/envoy-dev:latest 22 | istio/proxyv2:1.25.0 23 | istio/proxyv2:1.24.3 24 | istio/proxyv2:1.23.5 25 | 26 | jobs: 27 | build: 28 | name: "Build (multiphase evaluation: ${{ matrix.multiphase_eval }})" 29 | runs-on: ubuntu-22.04 30 | permissions: 31 | contents: write 32 | packages: write 33 | strategy: 34 | fail-fast: false # ftw runs are flaky, let's keep the two jobs running reducing re-runs 35 | matrix: 36 | multiphase_eval: ["true","false"] 37 | env: 38 | MULTIPHASE_EVAL: ${{ matrix.multiphase_eval }} 39 | steps: 40 | - name: Check out code 41 | uses: actions/checkout@v4 42 | with: # Ensure release_notes.sh can see prior commits 43 | fetch-depth: 0 44 | 45 | - name: Install Go 46 | uses: actions/setup-go@v3 47 | with: 48 | go-version: ${{ env.GO_VERSION }} 49 | cache: true 50 | 51 | - name: Install TinyGo 52 | run: | 53 | gh release download v${TINYGO_VERSION} -p '*.linux-amd64.tar.gz' -D ~ -R github.com/tinygo-org/tinygo 54 | tar -xf ~/tinygo${TINYGO_VERSION}.linux-amd64.tar.gz -C $HOME 55 | echo "$HOME/tinygo/bin" >> $GITHUB_PATH 56 | env: 57 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | 59 | - name: Cache TinyGo build 60 | uses: actions/cache@v3 61 | with: 62 | path: | 63 | ~/.cache/tinygo 64 | key: ${{ runner.os }}-tinygo-${{ hashFiles('**/go.sum') }} 65 | restore-keys: | 66 | ${{ runner.os }}-tinygo- 67 | 68 | - name: Run code checks 69 | run: go run mage.go lint 70 | 71 | - name: Build WASM filter 72 | run: go run mage.go build 73 | 74 | - name: Run unit tests 75 | run: go run mage.go coverage 76 | 77 | - name: Run e2e tests 78 | shell: bash 79 | run: > 80 | for image in $ENVOY_IMAGES; do 81 | echo "Running e2e with Envoy image $image" 82 | ENVOY_IMAGE=$image go run mage.go e2e 83 | done 84 | 85 | - name: Run regression tests (ftw) 86 | run: go run mage.go ftw 87 | 88 | - uses: actions/upload-artifact@v4 89 | if: success() || failure() 90 | with: 91 | name: ftw-envoy-logs-multiphase-${{ matrix.multiphase_eval }} 92 | path: build/ftw-envoy.log 93 | 94 | - name: Set up QEMU 95 | uses: docker/setup-qemu-action@v3 96 | 97 | - name: Set up Docker Buildx 98 | if: ${{ matrix.multiphase_eval=='true' }} 99 | uses: docker/setup-buildx-action@v3 100 | 101 | - name: Docker meta 102 | if: ${{ matrix.multiphase_eval=='true' }} 103 | id: meta 104 | uses: docker/metadata-action@v4 105 | with: 106 | images: | 107 | ghcr.io/${{ github.repository }} 108 | tags: | 109 | type=ref,event=branch 110 | type=ref,event=pr 111 | type=semver,pattern={{version}} 112 | type=semver,pattern={{major}} 113 | 114 | - name: Docker meta busybox 115 | if: ${{ matrix.multiphase_eval=='true' }} 116 | id: meta-busybox 117 | uses: docker/metadata-action@v4 118 | with: 119 | images: | 120 | ghcr.io/${{ github.repository }} 121 | tags: | 122 | type=ref,event=branch 123 | type=ref,event=pr 124 | type=semver,pattern={{version}} 125 | type=semver,pattern={{major}} 126 | flavor: | 127 | suffix=-busybox 128 | 129 | - name: Login to GHCR 130 | if: ${{ matrix.multiphase_eval=='true' }} 131 | uses: docker/login-action@v2 132 | with: 133 | registry: ghcr.io 134 | username: ${{ github.actor }} 135 | password: ${{ secrets.GITHUB_TOKEN }} 136 | 137 | - name: Build and push busybox based image 138 | if: ${{ matrix.multiphase_eval=='true' }} 139 | uses: docker/build-push-action@v5 140 | with: 141 | context: . 142 | push: ${{ github.event_name != 'pull_request' }} 143 | tags: ${{ steps.meta-busybox.outputs.tags }} 144 | platforms: linux/amd64,linux/arm64 145 | labels: ${{ steps.meta-busybox.outputs.labels }} 146 | cache-from: type=gha 147 | cache-to: type=gha,mode=max 148 | build-args: | 149 | BASE_IMAGE=busybox:1.36-uclibc 150 | 151 | - name: Build and push 152 | if: ${{ matrix.multiphase_eval=='true' }} 153 | uses: docker/build-push-action@v5 154 | with: 155 | context: . 156 | push: ${{ github.event_name != 'pull_request' }} 157 | tags: ${{ steps.meta.outputs.tags }} 158 | labels: ${{ steps.meta.outputs.labels }} 159 | cache-from: type=gha 160 | cache-to: type=gha,mode=max 161 | 162 | - name: Create draft release 163 | # Triggered only on tag creation and if release does not exist 164 | if: matrix.multiphase_eval=='true' && github.event_name == 'push' && contains(github.ref, 'refs/tags/') 165 | run: | 166 | set +e 167 | tag="${GITHUB_REF#refs/tags/}" 168 | if ! gh release view ${tag}; then 169 | echo "Release ${tag} does not exist, creating..." 170 | ./.github/workflows/release_notes.sh ${tag} > release-notes.txt 171 | gh release create ${tag} --draft --notes-file release-notes.txt --title ${GITHUB_REF#refs/tags/} 172 | fi 173 | env: 174 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 175 | 176 | 177 | - name: Push build artifact to release 178 | # Triggered only on tag creation 179 | if: matrix.multiphase_eval=='true' && github.event_name == 'push' && contains(github.ref, 'refs/tags/') 180 | run: | 181 | ls build 182 | mv build/main.wasm build/coraza-proxy-wasm.wasm 183 | tag="${GITHUB_REF#refs/tags/}" 184 | zip -j build/coraza-proxy-wasm-${tag}.zip build/coraza-proxy-wasm.wasm 185 | gh release upload ${tag} ./build/coraza-proxy-wasm-${tag}.zip --clobber 186 | env: 187 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 188 | -------------------------------------------------------------------------------- /.github/workflows/close-issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v3 14 | with: 15 | days-before-issue-stale: 30 16 | days-before-issue-close: 14 17 | stale-issue-label: "stale" 18 | stale-issue-message: "This issue has been open 30 days waiting for feedback. Remove the stale label or comment, or this will be closed in 14 days." 19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 20 | days-before-pr-stale: -1 21 | days-before-pr-close: -1 22 | only-issue-labels: "awaiting feedback" 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/nightly-coraza-check.yaml: -------------------------------------------------------------------------------- 1 | name: Nightly Coraza Check 2 | on: 3 | pull_request: 4 | paths: 5 | - ".github/workflows/nightly-coraza-check.yaml" 6 | - ".github/workflows/ci.yaml" 7 | schedule: 8 | - cron: "0 4 * * *" 9 | 10 | env: 11 | GO_VERSION: '1.23' 12 | TINYGO_VERSION: 0.34.0 13 | 14 | jobs: 15 | test: 16 | name: "Test (multiphase evaluation: ${{ matrix.multiphase_eval }})" 17 | runs-on: ubuntu-22.04 18 | strategy: 19 | matrix: 20 | multiphase_eval: ["true","false"] 21 | env: 22 | MULTIPHASE_EVAL: ${{ matrix.multiphase_eval }} 23 | steps: 24 | - name: Check out code 25 | uses: actions/checkout@v4 26 | 27 | - name: Install Go 28 | uses: actions/setup-go@v3 29 | with: 30 | go-version: ${{ env.GO_VERSION }} 31 | cache: true 32 | 33 | - name: Install TinyGo 34 | run: | 35 | gh release download v${TINYGO_VERSION} -p '*.linux-amd64.tar.gz' -D ~ -R github.com/tinygo-org/tinygo 36 | tar -xf ~/tinygo${TINYGO_VERSION}.linux-amd64.tar.gz -C $HOME 37 | echo "$HOME/tinygo/bin" >> $GITHUB_PATH 38 | env: 39 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | 41 | - name: Cache TinyGo build 42 | uses: actions/cache@v3 43 | with: 44 | path: | 45 | ~/.cache/tinygo 46 | key: ${{ runner.os }}-tinygo-${{ hashFiles('**/go.sum') }} 47 | restore-keys: | 48 | ${{ runner.os }}-tinygo- 49 | 50 | - name: Get last commit of coraza 51 | id: coraza-latest-commit 52 | run: echo "value=$(gh api repos/corazawaf/coraza/commits/main -q .sha)" >> $GITHUB_OUTPUT 53 | env: 54 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | - name: Fetch last coraza version 57 | run: go get -u github.com/corazawaf/coraza/v3@${{ steps.coraza-latest-commit.outputs.value }} && go mod tidy 58 | 59 | - name: Build WASM filter 60 | run: go run mage.go build 61 | 62 | - name: Run unit tests 63 | run: go run mage.go coverage 64 | 65 | - name: Run e2e tests 66 | run: go run mage.go e2e 67 | -------------------------------------------------------------------------------- /.github/workflows/release_notes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ue 2 | # Copyright The OWASP Coraza contributors 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # 6 | # Highly inspired by https://github.com/tetratelabs/wazero/blob/main/.github/workflows/release_notes.sh 7 | # 8 | # This script generates the release notes for a specific release tag. 9 | # .github/workflows/release_notes.sh v1.3.0 10 | 11 | tag=$1 12 | prior_tag=$(git tag -l 'v*'|sed "/${tag}/,+10d"|tail -1) 13 | if [ -n "${prior_tag}" ]; then 14 | range="${prior_tag}..${tag}" 15 | else 16 | range=${tag} 17 | fi 18 | 19 | git config log.mailmap true 20 | changelog=$(git log --format='%h %s %aN, %(trailers:key=co-authored-by)' "${range}") 21 | 22 | # strip the v off the tag name more shell portable than ${tag:1} 23 | version=$(echo "${tag}" | cut -c2-100) || exit 1 24 | cat <alert(0)' -IL 112 | HTTP/2 403 113 | vary: Accept-Encoding 114 | date: Tue, 10 Oct 2023 13:45:47 GMT 115 | server: istio-envoy 116 | ``` 117 | 118 | Depending on your configuration a log in the istio-proxy's log will look like this: 119 | 120 | ```text 121 | envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 122 | wasm log istio-ingress.coraza-ingressgateway: [client "my-client"] 123 | Coraza: Warning. Javascript method detected [file "@owasp_crs/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] 124 | [line "7982"] [id "941390"] [rev ""] [msg "Javascript method detected"] 125 | [data "Matched Data: alert( found within ARGS_GET:arg: "] 126 | [severity "critical"] [ver "OWASP_CRS/4.0.0-rc1"] [maturity "0"] [accuracy "0"] 127 | [tag "application-multi"] [tag "language-multi"] [tag "attack-xss"] [tag "paranoia-level/1"] 128 | [tag "OWASP_CRS"] [tag "capec/1000/152/242"] [hostname "my-hostname"] [uri "/anything/?arg="] 129 | [unique_id "wTueIQloYpvpWNLzVfy"] thread=27 130 | ``` 131 | -------------------------------------------------------------------------------- /ftw/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2022 The OWASP Coraza contributors 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | FROM ghcr.io/coreruleset/go-ftw:1.3.0 5 | 6 | RUN apk update && apk add curl 7 | 8 | WORKDIR /workspace 9 | 10 | # Keep this CRS version aligned with the one embedded in wasmplugin/rules 11 | ARG CRS_VERSION=v4.14.0 12 | 13 | ADD https://github.com/coreruleset/coreruleset/archive/refs/tags/${CRS_VERSION}.tar.gz /workspace/coreruleset/ 14 | RUN cd coreruleset && tar -xf ${CRS_VERSION}.tar.gz --strip-components 1 15 | 16 | COPY ftw.yml /workspace/ftw.yml 17 | COPY tests.sh /workspace/tests.sh 18 | 19 | ENTRYPOINT ["sh"] 20 | CMD ["-c", "/workspace/tests.sh"] 21 | 22 | -------------------------------------------------------------------------------- /ftw/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | albedo: 3 | image: ghcr.io/coreruleset/albedo:0.2.0 4 | chown: 5 | image: alpine:3.16 6 | command: 7 | - /bin/sh 8 | - -c 9 | # Early creates the log file so wasm-logs does not fail even if envoy is not yet healthy 10 | - touch /home/envoy/logs/envoy.log && chown -R 101:101 /home/envoy/logs 11 | volumes: 12 | - logs:/home/envoy/logs:rw 13 | envoy: 14 | depends_on: 15 | - chown 16 | - albedo 17 | image: ${ENVOY_IMAGE:-envoyproxy/envoy:v1.31-latest} 18 | command: 19 | - -c 20 | - ${ENVOY_CONFIG:-/conf/envoy-config.yaml} 21 | - --log-level 22 | - info 23 | - --component-log-level 24 | - wasm:debug 25 | - --log-format [%Y-%m-%d %T.%f][%t][%l][%n] [%g:%#] %v 26 | - --log-path 27 | - /home/envoy/logs/envoy.log 28 | volumes: 29 | - ../build:/build 30 | - .:/conf 31 | - logs:/home/envoy/logs:rw 32 | ports: 33 | - 8080:80 34 | wasm-logs: 35 | depends_on: 36 | - envoy 37 | image: debian:11-slim 38 | entrypoint: bash 39 | command: 40 | - -c 41 | - tail -c +0 -f /home/envoy/logs/envoy.log | grep --line-buffered "[critical][wasm]" > /home/envoy/logs/ftw.log 42 | volumes: 43 | - logs:/home/envoy/logs:rw 44 | envoy-logs: 45 | depends_on: 46 | - envoy 47 | image: debian:11-slim 48 | entrypoint: bash 49 | command: 50 | - -c 51 | - tail -c +0 -f /home/envoy/logs/envoy.log > /build/ftw-envoy.log 52 | volumes: 53 | - logs:/home/envoy/logs:ro 54 | - ../build:/build 55 | ftw: 56 | depends_on: 57 | - envoy-logs 58 | - wasm-logs 59 | build: . 60 | environment: 61 | - FTW_CLOUDMODE 62 | - FTW_INCLUDE 63 | volumes: 64 | - logs:/home/envoy/logs:ro 65 | - ../build:/build 66 | ftw-memstats: 67 | depends_on: 68 | - ftw 69 | image: debian:11-slim 70 | entrypoint: bash 71 | command: 72 | - -c 73 | - "tail -c +0 -f /home/envoy/logs/envoy.log | grep 'Sys: '" 74 | volumes: 75 | - logs:/home/envoy/logs:ro 76 | volumes: 77 | logs: 78 | -------------------------------------------------------------------------------- /ftw/envoy-config-nowasm.yaml: -------------------------------------------------------------------------------- 1 | static_resources: 2 | listeners: 3 | - address: 4 | socket_address: 5 | address: 0.0.0.0 6 | port_value: 80 7 | filter_chains: 8 | - filters: 9 | - name: envoy.filters.network.http_connection_manager 10 | typed_config: 11 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 12 | stat_prefix: ingress_http 13 | codec_type: auto 14 | http_protocol_options: 15 | accept_http_10: true 16 | route_config: 17 | virtual_hosts: 18 | - name: local_route 19 | domains: 20 | - "*" 21 | routes: 22 | - match: 23 | prefix: "/" 24 | route: 25 | cluster: local_server 26 | http_filters: 27 | - name: envoy.filters.http.router 28 | typed_config: 29 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 30 | 31 | clusters: 32 | - name: local_server 33 | connect_timeout: 6000s 34 | type: strict_dns 35 | lb_policy: round_robin 36 | load_assignment: 37 | cluster_name: local_server 38 | endpoints: 39 | - lb_endpoints: 40 | - endpoint: 41 | address: 42 | socket_address: 43 | address: albedo 44 | port_value: 8080 45 | -------------------------------------------------------------------------------- /ftw/envoy-config.yaml: -------------------------------------------------------------------------------- 1 | static_resources: 2 | listeners: 3 | - address: 4 | socket_address: 5 | address: 0.0.0.0 6 | port_value: 80 7 | filter_chains: 8 | - filters: 9 | - name: envoy.filters.network.http_connection_manager 10 | typed_config: 11 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 12 | stat_prefix: ingress_http 13 | codec_type: auto 14 | http_protocol_options: 15 | # accept_http_10 handles pre-HTTP/1.1 traffics for CRS regression tests against legacy HTTP versions 16 | accept_http_10: true 17 | route_config: 18 | virtual_hosts: 19 | - name: local_route 20 | domains: 21 | - "*" 22 | routes: 23 | - match: 24 | prefix: "/" 25 | route: 26 | cluster: local_server 27 | http_filters: 28 | - name: envoy.filters.http.wasm 29 | typed_config: 30 | "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm 31 | config: 32 | name: "coraza-filter" 33 | root_id: "" 34 | configuration: 35 | "@type": "type.googleapis.com/google.protobuf.StringValue" 36 | # NB: inline rules order matter. Some ftw-config rules override the coraza-recommended default ones. 37 | value: | 38 | { 39 | "directives_map": { 40 | "default": [ 41 | "Include @recommended-conf", 42 | "Include @ftw-conf", 43 | "Include @crs-setup-conf", 44 | "Include @owasp_crs/*.conf" 45 | ] 46 | }, 47 | "default_directives": "default", 48 | "metric_labels": {}, 49 | "per_authority_directives": {} 50 | } 51 | vm_config: 52 | runtime: "envoy.wasm.runtime.v8" 53 | vm_id: "my_vm_id" 54 | code: 55 | local: 56 | filename: "build/main.wasm" 57 | - name: envoy.filters.http.router 58 | typed_config: 59 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 60 | 61 | clusters: 62 | - name: local_server 63 | connect_timeout: 6000s 64 | type: STRICT_DNS 65 | lb_policy: ROUND_ROBIN 66 | load_assignment: 67 | cluster_name: local_server 68 | endpoints: 69 | - lb_endpoints: 70 | - endpoint: 71 | address: 72 | socket_address: 73 | address: albedo 74 | port_value: 8080 75 | -------------------------------------------------------------------------------- /ftw/ftw.yml: -------------------------------------------------------------------------------- 1 | --- 2 | logfile: '/home/envoy/logs/ftw.log' 3 | maxmarkerretries: 10 4 | testoverride: 5 | input: 6 | dest_addr: envoy 7 | ignore: 8 | # Envoy not compatible tests 9 | '911100-5': 'Invalid HTTP method. Rejected by Envoy with Error 400' 10 | '911100-7': 'Invalid HTTP method. Rejected by Envoy with Error 400' 11 | '920100-4': 'Accepted by Envoy. Valid request. It is only disabled by default from Apache and Nginx' 12 | '920100-10': 'Invalid HTTP method. Rejected by Envoy with Error 400' 13 | '920100-14': 'Invalid HTTP method. Rejected by Envoy with Error 400' 14 | '920100-16': 'Invalid HTTP request line. Rejected by Envoy with Error 400' 15 | '920181-1': 'Content-Length with Transfer-Encoding chunked is rejected by Envoy with Error 400' 16 | '920210-2': 'Connection header is stripped out by Envoy' 17 | '920210-3': 'Connection header is stripped out by Envoy' 18 | '920210-4': 'Connection header is stripped out by Envoy' 19 | '920210-6': 'Connection header is stripped out by Envoy' 20 | '920210-7': 'Connection header is stripped out by Envoy' 21 | '920274-2': 'PL4 - False positive. Envoy Populates :path header, therefore invalid character are detected' 22 | '920274-3': 'PL4 - False positive. Envoy Populates :path header, therefore invalid character are detected' 23 | '920274-5': 'PL4 - False positive. Envoy Populates :path header, therefore invalid character are detected' 24 | '932161-7': 'Referer header is sanitized by Envoy and removed from the request' 25 | '932161-9': 'Referer header is sanitized by Envoy and removed from the request' 26 | '932161-10': 'Referer header is sanitized by Envoy and removed from the request' 27 | '932161-11': 'Referer header is sanitized by Envoy and removed from the request' 28 | '932161-12': 'Referer header is sanitized by Envoy and removed from the request' 29 | '932237-8': 'Referer header is sanitized by Envoy and removed from the request' 30 | '932237-18': 'Referer header is sanitized by Envoy and removed from the request' 31 | '932239-6': 'Referer header is sanitized by Envoy and removed from the request' 32 | '932239-7': 'Referer header is sanitized by Envoy and removed from the request' 33 | '932239-19': 'Referer header is sanitized by Envoy and removed from the request' 34 | '932239-27': 'Referer header is sanitized by Envoy and removed from the request' 35 | '932239-29': 'Referer header is sanitized by Envoy and removed from the request' 36 | '941101-1': 'Referer header is sanitized by Envoy and removed from the request' 37 | '941110-4': 'Referer header is sanitized by Envoy and removed from the request' 38 | '949110-4': 'Related to 920100. Invalid HTTP method. Rejected by Envoy with Error 400' 39 | 40 | # coraza-proxy-wasm not compatible tests 41 | '920280-1': 'Rule 920280 matches missing Host. coraza-proxy-wasm crafts it from :authority' 42 | '920280-3': 'Rule 920280 matches missing Host. coraza-proxy-wasm crafts it from :authority' 43 | '920290-1': 'Rule 920290 matches empty Host. coraza-proxy-wasm crafts it from :authority' 44 | 45 | # Rules working, tests excluded for different expected output 46 | '920270-4': 'Log contains 920270. Test has log_contains disabled.' 47 | 48 | # Coraza related issues 49 | '920171-2': 'Rule 920171 not detected. GET/HEAD with body. Coraza side' 50 | '920171-3': 'Rule 920171 not detected. GET/HEAD with body. Coraza side' 51 | '920430-3': 'Rule 920430 not detected. Proto version. Coraza side' 52 | '920430-8': 'Rule 920430 not detected. Proto version. Coraza side' 53 | '920430-9': 'Rule 920430 not detected. Proto version. Coraza side' 54 | '934120-23': 'Rule 934120 partially detected. With HTTP/1.1 Envoy return 400. With HTTP/2 Enclosed alphanumerics not detected. Coraza Side' 55 | '934120-24': 'Rule 934120 partially detected. With HTTP/1.1 Envoy return 400. With HTTP/2 Enclosed alphanumerics not detected. Coraza Side' 56 | '934120-25': 'Rule 934120 partially detected. With HTTP/1.1 Envoy return 400. With HTTP/2 Enclosed alphanumerics not detected. Coraza Side' 57 | '934120-26': 'Rule 934120 partially detected. With HTTP/1.1 Envoy return 400. With HTTP/2 Enclosed alphanumerics not detected. Coraza Side' 58 | '934120-39': 'Rule 934120 partially detected. With HTTP/1.1 Envoy return 400. With HTTP/2 Enclosed alphanumerics not detected. Coraza Side' 59 | '932200-13': 'Unfortunate match inside logs against a different rule log. wip' 60 | 61 | '920274-1': 'Host validation. Apache expects status 400, investigate Coraza-proxy-wasm behavior' 62 | '920430-5': 'To be investigated Coraza side' 63 | '932300-10': 'To be investigated Coraza side, failing only with multiphase evaluation' 64 | '933120-2': 'To be investigated Coraza side, failing only with multiphase evaluation' 65 | 66 | '921140-1': 'Expected 400. To be investigated' 67 | '921250-1': 'Expected to match $Version in cookies, To be investigated Coraza side' 68 | '921250-2': 'Expected to match $Version in cookies, To be investigated Coraza side' 69 | '922130-1': 'match_regex, likely different error message. To be investigated' 70 | '922130-2': 'match_regex, likely different error message. To be investigated' 71 | '922130-7': 'match_regex, likely different error message. To be investigated' 72 | -------------------------------------------------------------------------------- /ftw/tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2022 The OWASP Coraza contributors 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | cd /workspace 6 | 7 | # Copied from https://github.com/jcchavezs/modsecurity-wasm-filter-e2e/blob/master/tests.sh 8 | 9 | step=1 10 | total_steps=1 11 | max_retries=15 #seconds for the server reachability timeout 12 | host=${1:-envoy} 13 | health_url="http://${host}:80" 14 | 15 | # Testing if the server is up 16 | echo "[$step/$total_steps] Testing application reachability" 17 | status_code="000" 18 | while [[ "$status_code" -eq "000" ]]; do 19 | status_code=$(curl --write-out "%{http_code}" --silent --output /dev/null $health_url) 20 | sleep 1 21 | echo -ne "[Wait] Waiting for response from $health_url. Timeout: ${max_retries}s \r" 22 | let "max_retries--" 23 | if [[ "$max_retries" -eq 0 ]] ; then 24 | echo "[Fail] Timeout waiting for response from $health_url, make sure the server is running." 25 | echo "Envoy Logs:" && cat /home/envoy/logs/envoy.log 26 | exit 1 27 | fi 28 | done 29 | if [[ "$status_code" -ne "200" ]]; then 30 | echo -e "\n[Fail] Unexpected status code $status_code, expected 200. Exiting." 31 | exit 1 32 | fi 33 | echo -e "\n[Ok] Got status code $status_code, expected 200. Ready to start." 34 | 35 | 36 | FTW_CLOUDMODE=${FTW_CLOUDMODE:-false} 37 | 38 | FTW_INCLUDE=$([ "${FTW_INCLUDE}" == "" ] && echo "" || echo "-i ${FTW_INCLUDE}") 39 | 40 | /ftw run -d coreruleset/tests/regression/tests --config ftw.yml --read-timeout=10s --cloud=$FTW_CLOUDMODE $FTW_INCLUDE || exit 1 41 | 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/corazawaf/coraza-proxy-wasm 2 | 3 | go 1.23.8 4 | 5 | require ( 6 | github.com/corazawaf/coraza-wasilibs v0.2.0 7 | github.com/corazawaf/coraza/v3 v3.3.3 8 | github.com/stretchr/testify v1.10.0 9 | github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0 10 | github.com/tidwall/gjson v1.18.0 11 | github.com/wasilibs/nottinygc v0.7.1 12 | ) 13 | 14 | require ( 15 | github.com/corazawaf/libinjection-go v0.2.2 // indirect 16 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 17 | github.com/kr/pretty v0.1.0 // indirect 18 | github.com/magefile/mage v1.15.1-0.20241126214340-bdc92f694516 // indirect 19 | github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 // indirect 20 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 21 | github.com/tetratelabs/wazero v1.7.3 // indirect 22 | github.com/tidwall/match v1.1.1 // indirect 23 | github.com/tidwall/pretty v1.2.1 // indirect 24 | github.com/valllabh/ocsf-schema-golang v1.0.3 // indirect 25 | github.com/wasilibs/go-aho-corasick v0.6.0 // indirect 26 | github.com/wasilibs/go-libinjection v0.5.0 // indirect 27 | github.com/wasilibs/go-re2 v1.6.0 // indirect 28 | golang.org/x/net v0.37.0 // indirect 29 | golang.org/x/sync v0.12.0 // indirect 30 | golang.org/x/sys v0.31.0 // indirect 31 | google.golang.org/protobuf v1.35.1 // indirect 32 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | rsc.io/binaryregexp v0.2.0 // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc h1:OlJhrgI3I+FLUCTI3JJW8MoqyM78WbqJjecqMnqG+wc= 2 | github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc/go.mod h1:7rsocqNDkTCira5T0M7buoKR2ehh7YZiPkzxRuAgvVU= 3 | github.com/corazawaf/coraza-wasilibs v0.2.0 h1:BT8x2pks6Xk7Oi1cUS9BPO+hi3QWQyQAtBkC3IR3Mt8= 4 | github.com/corazawaf/coraza-wasilibs v0.2.0/go.mod h1:jmUPQdndtPfMzKPn0a8BqdikXjuT3wY+6zDx5NvKshI= 5 | github.com/corazawaf/coraza/v3 v3.3.3 h1:kqjStHAgWqwP5dh7n0vhTOF0a3t+VikNS/EaMiG0Fhk= 6 | github.com/corazawaf/coraza/v3 v3.3.3/go.mod h1:xSaXWOhFMSbrV8qOOfBKAyw3aOqfwaSaOy5BgSF8XlA= 7 | github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g= 8 | github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw= 9 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 10 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= 12 | github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= 13 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 14 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 15 | github.com/jcchavezs/mergefs v0.1.0 h1:7oteO7Ocl/fnfFMkoVLJxTveCjrsd//UB0j89xmnpec= 16 | github.com/jcchavezs/mergefs v0.1.0/go.mod h1:eRLTrsA+vFwQZ48hj8p8gki/5v9C2bFtHH5Mnn4bcGk= 17 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 18 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 19 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 20 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 21 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 22 | github.com/magefile/mage v1.15.1-0.20241126214340-bdc92f694516 h1:aAO0L0ulox6m/CLRYvJff+jWXYYCKGpEm3os7dM/Z+M= 23 | github.com/magefile/mage v1.15.1-0.20241126214340-bdc92f694516/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 24 | github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= 25 | github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= 26 | github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 h1:1Kw2vDBXmjop+LclnzCb/fFy+sgb3gYARwfmoUcQe6o= 27 | github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw= 28 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 29 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 31 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 32 | github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0 h1:Xuwzknb4+OHBSYFXif0aBdV0F4MShL8L5YYFda9uUIs= 33 | github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0/go.mod h1:niJQcnEDtftzrVC0/qqlSs2Kzr1dwb7VxpIPHBO2XXk= 34 | github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw= 35 | github.com/tetratelabs/wazero v1.7.3/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y= 36 | github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= 37 | github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 38 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 39 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 40 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 41 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 42 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 43 | github.com/valllabh/ocsf-schema-golang v1.0.3 h1:eR8k/3jP/OOqB8LRCtdJ4U+vlgd/gk5y3KMXoodrsrw= 44 | github.com/valllabh/ocsf-schema-golang v1.0.3/go.mod h1:sZ3as9xqm1SSK5feFWIR2CuGeGRhsM7TR1MbpBctzPk= 45 | github.com/wasilibs/go-aho-corasick v0.6.0 h1:/usYKOljcc+264yq7jzq1tsgyuS1F/cUzLE75TJIgsQ= 46 | github.com/wasilibs/go-aho-corasick v0.6.0/go.mod h1:juBo/POXN3BapT9Bw93oxRQyyPha7AEu5SPUicPfLn4= 47 | github.com/wasilibs/go-libinjection v0.5.0 h1:ef+xIy0vLkZgvsyZevGDOFH3sq1aIrQI/8ImaCGDqDU= 48 | github.com/wasilibs/go-libinjection v0.5.0/go.mod h1:i1Va/vm/PFDYZS1JHUd3Ab2bNk7Qol3W70bEWz7I3b4= 49 | github.com/wasilibs/go-re2 v1.6.0 h1:CLlhDebt38wtl/zz4ww+hkXBMcxjrKFvTDXzFW2VOz8= 50 | github.com/wasilibs/go-re2 v1.6.0/go.mod h1:prArCyErsypRBI/jFAFJEbzyHzjABKqkzlidF0SNA04= 51 | github.com/wasilibs/nottinygc v0.7.1 h1:rKu19+SFniRNuSo5NX7/wxpSpXmMUmkcyt/YiWLJg8w= 52 | github.com/wasilibs/nottinygc v0.7.1/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo= 53 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= 54 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 55 | golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= 56 | golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 57 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 58 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 59 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 60 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 61 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= 62 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 63 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 64 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 65 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 66 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 67 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 68 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 69 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 70 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 71 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 72 | rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= 73 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 74 | -------------------------------------------------------------------------------- /go.work: -------------------------------------------------------------------------------- 1 | go 1.23.8 2 | 3 | use ( 4 | . 5 | ./magefiles 6 | ) 7 | -------------------------------------------------------------------------------- /init_tinygo.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //go:build tinygo 5 | 6 | package main 7 | 8 | import ( 9 | "unsafe" 10 | 11 | _ "github.com/wasilibs/nottinygc" 12 | ) 13 | 14 | // Some host functions that are not implemented by Envoy end up getting imported anyways 15 | // by code that gets compiled but not executed at runtime. Because we know they are not 16 | // executed, we can stub them out to allow functioning on Envoy. Note, these match the 17 | // names and signatures of wasi-libc, used by TinyGo, not WASI ABI. Review these exports when either 18 | // the minimum supported version of Envoy changes or the maximum version of TinyGo. 19 | 20 | // fdopendir is re-exported to avoid TinyGo 0.28's import of wasi_snapshot_preview1.fd_readdir. 21 | // 22 | //export fdopendir 23 | func fdopendir(fd int32) unsafe.Pointer { 24 | return nil 25 | } 26 | 27 | // readdir is re-exported to avoid TinyGo 0.28's import of wasi_snapshot_preview1.fd_readdir. 28 | // 29 | //export readdir 30 | func readdir(unsafe.Pointer) unsafe.Pointer { 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /internal/auditlog/serial_writer.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package auditlog 5 | 6 | import ( 7 | "io" 8 | 9 | "github.com/corazawaf/coraza/v3/experimental/plugins" 10 | "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" 11 | "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" 12 | ) 13 | 14 | // RegisterProxyWasmSerialWriter overrides the default "Serial" audit log writer (see https://github.com/corazawaf/coraza/blob/main/internal/auditlog/init_tinygo.go) 15 | // in order to print audit logs to the proxy-wasm log as info messages with a prefix to differentiate them from other logs. 16 | func RegisterProxyWasmSerialWriter() { 17 | plugins.RegisterAuditLogWriter("serial", func() plugintypes.AuditLogWriter { 18 | return &wasmSerial{} 19 | }) 20 | } 21 | 22 | type wasmSerial struct { 23 | io.Closer 24 | formatter plugintypes.AuditLogFormatter 25 | } 26 | 27 | func (s *wasmSerial) Init(cfg plugintypes.AuditLogConfig) error { 28 | s.formatter = cfg.Formatter 29 | return nil 30 | } 31 | 32 | func (s *wasmSerial) Write(al plugintypes.AuditLog) error { 33 | if s.formatter == nil { 34 | return nil 35 | } 36 | 37 | bts, err := s.formatter.Format(al) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | if len(bts) == 0 { 43 | return nil 44 | } 45 | // Print the audit log to the proxy-wasm log as an info message adding an "AuditLog:" prefix. 46 | proxywasm.LogInfo("AuditLog:" + string(bts)) 47 | return nil 48 | } 49 | 50 | func (s *wasmSerial) Close() error { return nil } 51 | -------------------------------------------------------------------------------- /internal/operators/operators.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //go:build tinygo 5 | 6 | package operators 7 | 8 | import ( 9 | wasilibs "github.com/corazawaf/coraza-wasilibs" 10 | ) 11 | 12 | func Register() { 13 | wasilibs.RegisterRX() 14 | wasilibs.RegisterPM() 15 | wasilibs.RegisterSQLi() 16 | wasilibs.RegisterXSS() 17 | } 18 | -------------------------------------------------------------------------------- /internal/operators/operators_go.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //go:build !tinygo 5 | 6 | package operators 7 | 8 | func Register() { 9 | } 10 | -------------------------------------------------------------------------------- /mage.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | //go:build ignore 5 | // +build ignore 6 | 7 | // Entrypoint to mage for running without needing to install the command. 8 | // https://magefile.org/zeroinstall/ 9 | package main 10 | 11 | import ( 12 | "os" 13 | 14 | "github.com/magefile/mage/mage" 15 | ) 16 | 17 | func main() { 18 | os.Exit(mage.Main()) 19 | } 20 | -------------------------------------------------------------------------------- /magefiles/e2etest.go: -------------------------------------------------------------------------------- 1 | // Copyright The OWASP Coraza contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "cmp" 8 | "crypto/tls" 9 | "fmt" 10 | "net/http" 11 | "os" 12 | "strings" 13 | "time" 14 | 15 | "github.com/magefile/mage/sh" 16 | "golang.org/x/net/http2" 17 | ) 18 | 19 | // E2e runs e2e tests. Requires docker. 20 | func E2e() error { 21 | envoyHost := cmp.Or(os.Getenv("ENVOY_HOST"), "localhost:8080") 22 | httpbinHost := cmp.Or(os.Getenv("HTTPBIN_HOST"), "localhost:8081") 23 | 24 | if err := runCorazaE2e(envoyHost, httpbinHost); err != nil { 25 | return err 26 | } 27 | 28 | if err := runHttpTrailerE2e(envoyHost); err != nil { 29 | return err 30 | } 31 | return nil 32 | } 33 | 34 | // runCorazaE2e runs Coraza e2e tests with a built plugin against the example deployment 35 | func runCorazaE2e(envoyHost, httpbinHost string) error { 36 | dockerComposeFilePath := "e2e/coraza/docker-compose.yml" 37 | var err error 38 | if err = sh.RunV("docker", "compose", "--file", dockerComposeFilePath, "up", "-d", "envoy"); err != nil { 39 | sh.RunV("docker", "compose", "-f", dockerComposeFilePath, "logs", "envoy") 40 | return err 41 | } 42 | defer func() { 43 | _ = sh.RunV("docker", "compose", "--file", dockerComposeFilePath, "down", "-v") 44 | }() 45 | 46 | // --nulled-body is needed because coraza-proxy-wasm returns a 200 OK with a nulled body when if the interruption happens after phase 3 47 | if err = sh.RunV("go", "run", "github.com/corazawaf/coraza/v3/http/e2e/cmd/httpe2e@main", "--proxy-hostport", 48 | "http://"+envoyHost, "--httpbin-hostport", "http://"+httpbinHost, "--nulled-body"); err != nil { 49 | sh.RunV("docker", "compose", "-f", dockerComposeFilePath, "logs", "envoy") 50 | } 51 | return err 52 | } 53 | 54 | // runHttpTrailerE2e runs HTTP trailer E2E tests 55 | // It is meant to check that HTTP2 request payloads with trailers are scanned at phase 2 before being sent to upstream. 56 | // This might happen because the end_of_stream parameter from OnHttpRequestBody is never set to true in HTTP2 if trailers 57 | // are available. In order to mitigate this, OnHttp[Request|Response]Trailers callbacks have been implemented as an enforcement 58 | // point of the body phase rules. 59 | // The test expects Coraza to enforce the interruption (403) during phase="http_request_body" and not phase="http_response_headers", 60 | // which would mean that the payload was sent to upstream before being scanned and was blocked on the way back. 61 | func runHttpTrailerE2e(envoyHost string) error { 62 | fmt.Printf("Running HTTP trailer test\n") 63 | dockerComposeFilePath := "e2e/http_trailer/docker-compose.yml" 64 | if err := sh.RunV("go", "run", "filippo.io/mkcert@v1.4.4", "-key-file", "e2e/http_trailer/server.key", 65 | "-cert-file", "e2e/http_trailer/server.crt", "example.com"); err != nil { 66 | return err 67 | } 68 | defer func() { 69 | _ = os.Remove("e2e/http_trailer/server.key") 70 | _ = os.Remove("e2e/http_trailer/server.crt") 71 | }() 72 | if err := sh.RunV("docker", "compose", "--file", dockerComposeFilePath, "up", "-d", "envoy"); err != nil { 73 | sh.RunV("docker", "compose", "-f", dockerComposeFilePath, "logs", "envoy") 74 | return err 75 | } 76 | defer func() { 77 | _ = sh.RunV("docker", "compose", "--file", dockerComposeFilePath, "down", "-v") 78 | }() 79 | 80 | client := &http.Client{ 81 | Transport: &http2.Transport{ 82 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 83 | }, 84 | } 85 | 86 | // Wait for envoy to be ready 87 | ready := false 88 | for range 20 { 89 | resp, err := client.Get("https://" + envoyHost) 90 | if err == nil && resp.StatusCode == http.StatusOK { 91 | resp.Body.Close() 92 | ready = true 93 | break 94 | } 95 | time.Sleep(500 * time.Millisecond) 96 | fmt.Println("Waiting for Envoy to be ready...") 97 | } 98 | if !ready { 99 | sh.RunV("docker", "compose", "-f", dockerComposeFilePath, "logs", "envoy") 100 | return fmt.Errorf("timeout waiting for Envoy") 101 | } 102 | 103 | // Run the actual test. 104 | req, err := http.NewRequest("POST", "https://"+envoyHost, strings.NewReader("{\"foo\": \" 63 | # ex0 shell web shell 64 | color=DeepSkyBlue size=6> ## ex0 shell 65 | # FaTaLSheLL web shell 66 |

FaTaLSheLL v 67 | # G-Security Webshell 68 | G-Security Webshell 69 | # Gecko web shell 70 | Gecko [ 71 | # h4ntu shell web shell 72 | <title>h4ntu shell [powered by tsoi] 73 | # IDBTEAM SHELLS file manager 74 |

-=[+] IDBTEAM SHELLS 75 | # IndoXploit web shell 76 | IndoXploit 77 | # KA_uShell web shell 78 | | 79 | ## Laudanum PHP Web Shells (http://sourceforge.net/projects/laudanum) 80 | # Laudanum dns.php 81 | Laudanum PHP DNS Access 82 | # Laudanum file.php 83 | Laudanum PHP File Browser 84 | # Laudanum host.php 85 | Laudanum PHP Hostname by IP Lookup 86 | # Laudanum proxy.php 87 | Laudanum PHP Proxy 88 | # Laudanum shell.php 89 | Laudanum PHP Shell Access 90 | ## Laudanum WordPress Plugin settings.php 91 |

Laudanum Tools

92 | # Lifka Shell web shell 93 | >LIFKA SHELL 94 | # Loader'z web shell 95 | Loader'z WEB shell 96 | # Locus7Shell web shell 97 | b>--[ x2300 Locus7Shell v. 98 | # Lolipop web shell 99 | Lolipop.php - Edited By KingDefacer - 100 | # MARIJUANA web shell (https://0x5a455553.github.io/MARIJUANA/) 101 | <link rel="icon" href="//0x5a455553.github.io/MARIJUANA/icon.png" /> 102 | # Matamu Mat web shell 103 | <title> Matamu Mat 104 | # MyShell web shell 105 | MyShell ©2001 Digitart Producciones 106 | # NCC Shell web shell 107 |

.:NCC:. Shell v 108 | # PHPShell by Macker web shell 109 | PHPShell by Macker - Version 110 | # PHPShell by MAX666 web shell 111 | PHPShell by MAX666, Private Exploit, For Server Hacking 112 | # qsd web shell 113 |
Execute Shell Command (safe mode is off):
114 | # Rootshell web shell 115 |

Rootshell v 116 | # rusuh web shell 117 | ./rusuh 118 | # Safe0ver web shell 119 | ##Safe0ver## 120 | # Shany's web shell 121 |

Watch Your system Shany was here.

Linux Shells



122 | # Simple PHP backdoor web shell 123 |