From 8b082fb20b28209522edf7f54b2468610c5dcea3 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:59:38 +0200 Subject: [PATCH 01/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 27020ae6ab..e5149fd46b 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -4,6 +4,9 @@ on: push: pull_request: +env: + LUA_VERSION: "5.5" + jobs: build-linux: name: Linux (${{ matrix.platform.label }}, ${{ matrix.compiler.label }}, ${{ matrix.configure.label }}) @@ -47,7 +50,7 @@ jobs: libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ - liblua5.2-dev \ + liblua${{ env.LUA_VERSION }}-dev \ libmaxminddb-dev \ libpcre2-dev \ libxml2-dev \ @@ -56,7 +59,9 @@ jobs: libpcre3-dev \ bison \ flex \ - pkg-config + pkg-config \ + python3 \ + python3-venv - name: Run build preparation script @@ -109,13 +114,14 @@ jobs: libtool \ yajl \ lmdb \ - lua \ + lua@${{ env.LUA_VERSION }} \ libmaxminddb \ libxml2 \ ssdeep \ pcre \ bison \ - flex + flex \ + python3 - name: Run build preparation script run: ./build.sh @@ -206,7 +212,7 @@ jobs: - name: Install cppcheck run: | - brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep + brew install autoconf automake libtool cppcheck libmaxminddb yajl lua@${{ env.LUA_VERSION }} lmdb ssdeep python3 - name: Configure project run: | @@ -249,14 +255,16 @@ jobs: libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ - liblua5.2-dev \ + liblua${{ env.LUA_VERSION }}-dev \ libmaxminddb-dev \ libpcre2-dev \ libxml2-dev \ libfuzzy-dev \ pcre2-utils \ bison \ - flex + flex \ + python3 \ + python3-venv - name: Run build preparation script run: ./build.sh From 8214a3460f518aee7c739c87284ff66943402a09 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:03:59 +0200 Subject: [PATCH 02/34] Update ci.yml --- .github/workflows/ci.yml | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6895b825d..218fc6a6da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,16 @@ name: Quality Assurance on: push: pull_request: + +env: + LUA_VERSION: "5.5" jobs: build-linux: name: Linux (${{ matrix.platform.label }}, ${{ matrix.compiler.label }}, ${{ matrix.configure.label }}) runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-22.04] platform: @@ -40,7 +44,7 @@ jobs: sudo apt-get install -y libyajl-dev:${{ matrix.platform.arch }} \ libcurl4-openssl-dev:${{ matrix.platform.arch }} \ liblmdb-dev:${{ matrix.platform.arch }} \ - liblua5.2-dev:${{ matrix.platform.arch }} \ + liblua${{ env.LUA_VERSION }}-dev:${{ matrix.platform.arch }} \ libmaxminddb-dev:${{ matrix.platform.arch }} \ libpcre2-dev:${{ matrix.platform.arch }} \ pcre2-utils:${{ matrix.platform.arch }} \ @@ -56,9 +60,9 @@ jobs: run: | sudo apt-get install -y libgeoip-dev:${{ matrix.platform.arch }} \ libfuzzy-dev:${{ matrix.platform.arch }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - submodules: true + submodules: recursive fetch-depth: 0 - name: build.sh run: ./build.sh @@ -77,6 +81,7 @@ jobs: name: macOS (${{ matrix.configure.label }}) runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [macos-14] configure: @@ -99,16 +104,16 @@ jobs: libtool \ yajl \ lmdb \ - lua \ + lua@${{ env.LUA_VERSION }} \ libmaxminddb \ libxml2 \ ssdeep \ pcre \ bison \ flex - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - submodules: true + submodules: recursive fetch-depth: 0 - name: Build GeoIP run: | @@ -134,6 +139,7 @@ jobs: name: Windows (${{ matrix.platform.label }}, ${{ matrix.configure.label }}) runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [windows-2022] platform: @@ -147,9 +153,9 @@ jobs: - {label: "wo libxml", opt: "-DWITH_LIBXML2=OFF" } - {label: "with lmdb", opt: "-DWITH_LMDB=ON" } steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - submodules: true + submodules: recursive fetch-depth: 0 - name: Install Conan run: | @@ -197,7 +203,7 @@ jobs: cppcheck - uses: actions/checkout@v4 with: - submodules: true + submodules: recursive fetch-depth: 0 - name: configure run: | From a65df3f75ee195c5de6840d26705a3d2c0636f42 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 12:06:11 +0200 Subject: [PATCH 03/34] Update ci.yml --- .github/workflows/ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 218fc6a6da..108ac5d0f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: libmaxminddb-dev:${{ matrix.platform.arch }} \ libpcre2-dev:${{ matrix.platform.arch }} \ pcre2-utils:${{ matrix.platform.arch }} \ - bison flex + bison flex python3 python3-venv - name: Setup Dependencies (x32) if: ${{ matrix.platform.label == 'x32' }} run: | @@ -110,7 +110,8 @@ jobs: ssdeep \ pcre \ bison \ - flex + flex \ + python3 - uses: actions/checkout@v6 with: submodules: recursive @@ -200,8 +201,9 @@ jobs: brew install autoconf \ automake \ libtool \ - cppcheck - - uses: actions/checkout@v4 + cppcheck \ + python3 + - uses: actions/checkout@v6 with: submodules: recursive fetch-depth: 0 From 8b9659c326f8c2fb0630103221cc911613c4eda3 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:55:52 +0200 Subject: [PATCH 04/34] Update ci.yml --- .github/workflows/ci.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 108ac5d0f9..c96071bd09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: pull_request: env: - LUA_VERSION: "5.5" + LUA_VERSION: "5.4" jobs: build-linux: @@ -58,8 +58,8 @@ jobs: - name: Setup Dependencies (x64) if: ${{ matrix.platform.label == 'x64' }} run: | - sudo apt-get install -y libgeoip-dev:${{ matrix.platform.arch }} \ - libfuzzy-dev:${{ matrix.platform.arch }} + sudo apt-get install -y libfuzzy-dev:${{ matrix.platform.arch }} + - uses: actions/checkout@v6 with: submodules: recursive @@ -116,16 +116,6 @@ jobs: with: submodules: recursive fetch-depth: 0 - - name: Build GeoIP - run: | - git clone --depth 1 --no-checkout https://github.com/maxmind/geoip-api-c.git - cd geoip-api-c - git fetch --tags - # Check out the last release, v1.6.12 - git checkout 4b526e7331ca1d692b74a0509ddcc725622ed31a - autoreconf --install - ./configure --disable-dependency-tracking --disable-silent-rules --prefix=/opt/homebrew - make install - name: build.sh run: ./build.sh - name: configure From 9d9986e74f99aae7370d1b4f107d7e9a8dd028b5 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:12:21 +0200 Subject: [PATCH 05/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index e5149fd46b..e7ba0b0282 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -50,7 +50,7 @@ jobs: libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ - liblua${{ env.LUA_VERSION }}-dev \ + liblua5.4-dev \ libmaxminddb-dev \ libpcre2-dev \ libxml2-dev \ From 814e88fc37bc0f89ec912b13d4281e479a23728d Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:07:08 +0200 Subject: [PATCH 06/34] Update ci.yml lua dynamisch --- .github/workflows/ci.yml | 46 +++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c96071bd09..45d4b1ea20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,6 @@ on: push: pull_request: -env: - LUA_VERSION: "5.4" - jobs: build-linux: name: Linux (${{ matrix.platform.label }}, ${{ matrix.compiler.label }}, ${{ matrix.configure.label }}) @@ -37,6 +34,45 @@ jobs: - platform: {label: "x32"} configure: {label: "wo ssdeep"} steps: + - name: Detect latest Lua dev package + id: detect_lua + shell: bash + run: | + set -euo pipefail + + sudo dpkg --add-architecture ${{ matrix.platform.arch }} + sudo apt-get update -y -qq + + CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + + if [ -z "$CANDIDATES" ]; then + echo "No libluaX.Y-dev package found" + exit 1 + fi + + VALID_PACKAGES="" + for pkg in $CANDIDATES; do + if apt-cache show "${pkg}:${{ matrix.platform.arch }}" >/dev/null 2>&1; then + VALID_PACKAGES="${VALID_PACKAGES}${pkg}"$'\n' + fi + done + + if [ -z "$VALID_PACKAGES" ]; then + echo "No Lua package available for architecture ${{ matrix.platform.arch }}" + exit 1 + fi + + BEST_PKG="$( + printf '%s\n' "$VALID_PACKAGES" \ + | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1 &/' \ + | sort -V \ + | tail -n1 \ + | awk '{print $2}' + )" + + echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" + echo "Using $BEST_PKG for ${{ matrix.platform.arch }}" + - name: Setup Dependencies (common) run: | sudo dpkg --add-architecture ${{ matrix.platform.arch }} @@ -44,7 +80,7 @@ jobs: sudo apt-get install -y libyajl-dev:${{ matrix.platform.arch }} \ libcurl4-openssl-dev:${{ matrix.platform.arch }} \ liblmdb-dev:${{ matrix.platform.arch }} \ - liblua${{ env.LUA_VERSION }}-dev:${{ matrix.platform.arch }} \ + ${{ steps.detect_lua.outputs.lua_pkg }}:${{ matrix.platform.arch }} \ libmaxminddb-dev:${{ matrix.platform.arch }} \ libpcre2-dev:${{ matrix.platform.arch }} \ pcre2-utils:${{ matrix.platform.arch }} \ @@ -104,7 +140,7 @@ jobs: libtool \ yajl \ lmdb \ - lua@${{ env.LUA_VERSION }} \ + lua \ libmaxminddb \ libxml2 \ ssdeep \ From e68f6b1a9b5ab81f7f916b5b9874749dd8920415 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:12:04 +0200 Subject: [PATCH 07/34] Enhance CI to detect latest Lua dev package Updated CI workflow to dynamically detect and install the latest Lua development package instead of using a fixed version. --- .github/workflows/ci_new.yml | 67 ++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index e7ba0b0282..560ffcab87 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -4,9 +4,6 @@ on: push: pull_request: -env: - LUA_VERSION: "5.5" - jobs: build-linux: name: Linux (${{ matrix.platform.label }}, ${{ matrix.compiler.label }}, ${{ matrix.configure.label }}) @@ -43,14 +40,39 @@ jobs: fetch-depth: 0 submodules: recursive - - name: Install dependencies + - name: Detect latest Lua dev package + id: detect_lua + shell: bash run: | + set -euo pipefail + sudo apt-get update -y -qq + + CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + + if [ -z "$CANDIDATES" ]; then + echo "No libluaX.Y-dev package found" + exit 1 + fi + + BEST_PKG="$( + printf '%s\n' "$CANDIDATES" \ + | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1 &/' \ + | sort -V \ + | tail -n1 \ + | awk '{print $2}' + )" + + echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" + echo "Using $BEST_PKG" + + - name: Install dependencies + run: | sudo apt-get install -y \ libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ - liblua5.4-dev \ + ${{ steps.detect_lua.outputs.lua_pkg }} \ libmaxminddb-dev \ libpcre2-dev \ libxml2-dev \ @@ -63,7 +85,6 @@ jobs: python3 \ python3-venv - - name: Run build preparation script run: ./build.sh @@ -114,7 +135,7 @@ jobs: libtool \ yajl \ lmdb \ - lua@${{ env.LUA_VERSION }} \ + lua \ libmaxminddb \ libxml2 \ ssdeep \ @@ -212,7 +233,7 @@ jobs: - name: Install cppcheck run: | - brew install autoconf automake libtool cppcheck libmaxminddb yajl lua@${{ env.LUA_VERSION }} lmdb ssdeep python3 + brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep python3 - name: Configure project run: | @@ -240,11 +261,35 @@ jobs: with: fetch-depth: 0 submodules: recursive + + - name: Detect latest Lua dev package + id: detect_lua + shell: bash + run: | + set -euo pipefail + + apt-get update + CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + + if [ -z "$CANDIDATES" ]; then + echo "No libluaX.Y-dev package found" + exit 1 + fi + + BEST_PKG="$( + printf '%s\n' "$CANDIDATES" \ + | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1 &/' \ + | sort -V \ + | tail -n1 \ + | awk '{print $2}' + )" + + echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" + echo "Using $BEST_PKG" - name: Install dependencies (v2 style) run: | - apt-get update apt-get install -y \ autoconf \ automake \ @@ -255,7 +300,7 @@ jobs: libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ - liblua${{ env.LUA_VERSION }}-dev \ + ${{ steps.detect_lua.outputs.lua_pkg }} \ libmaxminddb-dev \ libpcre2-dev \ libxml2-dev \ @@ -265,7 +310,7 @@ jobs: flex \ python3 \ python3-venv - + - name: Run build preparation script run: ./build.sh From b50a48d175c4bf20876d71d4b13dba2fa6a573ca Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:15:29 +0200 Subject: [PATCH 08/34] Update ci.yml --- .github/workflows/ci.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45d4b1ea20..4dd0223208 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,38 +40,30 @@ jobs: run: | set -euo pipefail - sudo dpkg --add-architecture ${{ matrix.platform.arch }} sudo apt-get update -y -qq - CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + CANDIDATES="$(apt-cache pkgnames | grep -E '^liblua[0-9]+\.[0-9]+-dev$' || true)" if [ -z "$CANDIDATES" ]; then echo "No libluaX.Y-dev package found" exit 1 fi - VALID_PACKAGES="" - for pkg in $CANDIDATES; do - if apt-cache show "${pkg}:${{ matrix.platform.arch }}" >/dev/null 2>&1; then - VALID_PACKAGES="${VALID_PACKAGES}${pkg}"$'\n' - fi - done - - if [ -z "$VALID_PACKAGES" ]; then - echo "No Lua package available for architecture ${{ matrix.platform.arch }}" - exit 1 - fi - BEST_PKG="$( - printf '%s\n' "$VALID_PACKAGES" \ + printf '%s\n' "$CANDIDATES" \ | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1 &/' \ | sort -V \ | tail -n1 \ | awk '{print $2}' )" + if [ -z "$BEST_PKG" ]; then + echo "Failed to determine Lua package" + exit 1 + fi + echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" - echo "Using $BEST_PKG for ${{ matrix.platform.arch }}" + echo "Using $BEST_PKG" - name: Setup Dependencies (common) run: | From 6708f71a084e35ded3496dd069838043cf2c9cc9 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:35:35 +0200 Subject: [PATCH 09/34] Update Lua package detection in CI workflow --- .github/workflows/ci_new.yml | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 560ffcab87..fd48381bce 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -262,7 +262,7 @@ jobs: fetch-depth: 0 submodules: recursive - - name: Detect latest Lua dev package + - name: Detect latest Lua packages id: detect_lua shell: bash run: | @@ -270,7 +270,7 @@ jobs: apt-get update - CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + CANDIDATES="$(apt-cache pkgnames | grep -E '^liblua[0-9]+\.[0-9]+-dev$' || true)" if [ -z "$CANDIDATES" ]; then echo "No libluaX.Y-dev package found" @@ -285,8 +285,20 @@ jobs: | awk '{print $2}' )" - echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" - echo "Using $BEST_PKG" + if [ -z "$BEST_PKG" ]; then + echo "Failed to determine Lua dev package" + printf '%s\n' "$CANDIDATES" + exit 1 + fi + + BEST_VER="$(printf '%s\n' "$BEST_PKG" | sed -E 's/^liblua([0-9]+\.[0-9]+)-dev$/\1/')" + LUA_PKG="lua$BEST_VER" + + echo "lua_dev_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" + echo "lua_pkg=$LUA_PKG" >> "$GITHUB_OUTPUT" + + echo "Using dev package: $BEST_PKG" + echo "Using interpreter: $LUA_PKG" - name: Install dependencies (v2 style) run: | @@ -300,6 +312,7 @@ jobs: libyajl-dev \ libcurl4-openssl-dev \ liblmdb-dev \ + ${{ steps.detect_lua.outputs.lua_dev_pkg }} \ ${{ steps.detect_lua.outputs.lua_pkg }} \ libmaxminddb-dev \ libpcre2-dev \ @@ -311,6 +324,12 @@ jobs: python3 \ python3-venv + - name: Show Lua installation + run: | + which lua || true + lua -v || true + dpkg -l | grep lua || true + - name: Run build preparation script run: ./build.sh From 9823d8e9bf587c215196e82967a31752d7e21a43 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:13:26 +0200 Subject: [PATCH 10/34] Remove python3 from CI workflow installation Removed python3 from the installation steps in the CI workflow. --- .github/workflows/ci_new.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index fd48381bce..9eeb5d8d08 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -141,8 +141,7 @@ jobs: ssdeep \ pcre \ bison \ - flex \ - python3 + flex - name: Run build preparation script run: ./build.sh @@ -233,7 +232,7 @@ jobs: - name: Install cppcheck run: | - brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep python3 + brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep - name: Configure project run: | From e68ad1c8259590bcdb9c74cd956456e72e7ed828 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:13:57 +0200 Subject: [PATCH 11/34] Update CI workflow to remove python3 installation Removed python3 from the installation list in CI workflow. --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dd0223208..9add866e42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,8 +138,8 @@ jobs: ssdeep \ pcre \ bison \ - flex \ - python3 + flex + - uses: actions/checkout@v6 with: submodules: recursive @@ -219,8 +219,8 @@ jobs: brew install autoconf \ automake \ libtool \ - cppcheck \ - python3 + cppcheck + - uses: actions/checkout@v6 with: submodules: recursive From 633f2ebcc56454ce85743335547797a90bee1022 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:21:46 +0200 Subject: [PATCH 12/34] Add capture/non-capture regression coverage for detectSQLi/XSS --- .../regression/operator-detectsqli.json | 93 +++++++++++++++++++ .../regression/operator-detectxss.json | 93 +++++++++++++++++++ 2 files changed, 186 insertions(+) diff --git a/test/test-cases/regression/operator-detectsqli.json b/test/test-cases/regression/operator-detectsqli.json index fc49b0754c..ee46f2283f 100644 --- a/test/test-cases/regression/operator-detectsqli.json +++ b/test/test-cases/regression/operator-detectsqli.json @@ -368,5 +368,98 @@ "SecRule ARGS \"@detectSQLi\" \"id:1208,phase:2,capture,pass,t:trim,setvar:tx.sqli_hit=1\"", "SecRule TX:sqli_hit \"@eq 1\" \"id:2208,phase:2,deny,status:403\"" ] + }, + { + "enabled": 1, + "version_min": 300000, + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "title": "Testing Operator :: @detectSQLi :: capture stores fingerprint in TX.0", + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "61", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=ascii(substring(version() from 1 for 1))¶m2=value2" + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule ARGS \"@detectSQLi\" \"id:1209,phase:2,capture,pass,t:trim,setvar:tx.sqli_hit=1\"", + "SecRule TX:0 \"@streq f(f(f\" \"id:2209,phase:2,deny,status:403\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "title": "Testing Operator :: @detectSQLi :: no capture keeps TX.0 unchanged", + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "61", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=ascii(substring(version() from 1 for 1))¶m2=value2" + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule ARGS \"@detectSQLi\" \"id:1210,phase:2,pass,t:trim,setvar:tx.sqli_hit=1\"", + "SecRule TX:0 \"@streq f(f(f\" \"id:2210,phase:2,deny,status:409\"", + "SecRule TX:sqli_hit \"@eq 1\" \"id:2211,phase:2,deny,status:403\"" + ] } ] diff --git a/test/test-cases/regression/operator-detectxss.json b/test/test-cases/regression/operator-detectxss.json index 8cc651f6d9..048f57ac17 100644 --- a/test/test-cases/regression/operator-detectxss.json +++ b/test/test-cases/regression/operator-detectxss.json @@ -320,5 +320,98 @@ "SecRule ARGS \"@detectXSS\" \"id:1107,phase:2,pass,t:trim,setvar:tx.xss_hit=1\"", "SecRule TX:xss_hit \"@eq 1\" \"id:2107,phase:2,deny,status:403\"" ] + }, + { + "enabled": 1, + "version_min": 300000, + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "title": "Testing Operator :: @detectXSS :: capture stores original input in TX.0", + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "46", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=¶m2=value2" + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule ARGS \"@detectXSS\" \"id:1108,phase:2,capture,pass,t:trim,setvar:tx.xss_hit=1\"", + "SecRule TX:0 \"@streq \" \"id:2108,phase:2,deny,status:403\"" + ] + }, + { + "enabled": 1, + "version_min": 300000, + "client": { + "ip": "200.249.12.31", + "port": 123 + }, + "server": { + "ip": "200.249.12.31", + "port": 80 + }, + "response": { + "headers": { + "Date": "Mon, 13 Jul 2015 20:02:41 GMT", + "Last-Modified": "Sun, 26 Oct 2014 22:33:37 GMT", + "Content-Type": "text/html", + "Content-Length": "8" + }, + "body": [ + "no need." + ] + }, + "title": "Testing Operator :: @detectXSS :: no capture keeps TX.0 unchanged", + "request": { + "headers": { + "Host": "localhost", + "User-Agent": "curl/7.38.0", + "Accept": "*/*", + "Content-Length": "46", + "Content-Type": "application/x-www-form-urlencoded" + }, + "uri": "/", + "method": "POST", + "body": [ + "param1=¶m2=value2" + ] + }, + "expected": { + "http_code": 403 + }, + "rules": [ + "SecRuleEngine On", + "SecRule ARGS \"@detectXSS\" \"id:1109,phase:2,pass,t:trim,setvar:tx.xss_hit=1\"", + "SecRule TX:0 \"@streq \" \"id:2109,phase:2,deny,status:409\"", + "SecRule TX:xss_hit \"@eq 1\" \"id:2110,phase:2,deny,status:403\"" + ] } ] From fa02a620810bde6e6a61d8a5d01c553583940a4e Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:02:20 +0200 Subject: [PATCH 13/34] Remove pcre --- .github/workflows/ci_new.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 9eeb5d8d08..51b17b0ef0 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -32,7 +32,6 @@ jobs: - { label: "without ssdeep", opt: "--without-ssdeep" } - { label: "with lmdb", opt: "--with-lmdb" } - { label: "with pcre2 (default)", opt: "" } - - { label: "with pcre", opt: "--with-pcre" } steps: - uses: actions/checkout@v6 @@ -78,7 +77,6 @@ jobs: libxml2-dev \ libfuzzy-dev \ pcre2-utils \ - libpcre3-dev \ bison \ flex \ pkg-config \ From 7b7f6d43d3889f3f447f85d3ddf88da93a38b9b8 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:24:54 +0200 Subject: [PATCH 14/34] Pcre2 set --- .github/workflows/ci_new.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 51b17b0ef0..c0b114c346 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -31,7 +31,7 @@ jobs: - { label: "without geoip", opt: "--without-geoip" } - { label: "without ssdeep", opt: "--without-ssdeep" } - { label: "with lmdb", opt: "--with-lmdb" } - - { label: "with pcre2 (default)", opt: "" } + - { label: "with pcre2 (default)", opt: "--with-pcre2" } steps: - uses: actions/checkout@v6 From f33e33911952089061a2e5b1412255ec7530915c Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:45:14 +0200 Subject: [PATCH 15/34] Update macOS runners in CI workflow --- .github/workflows/ci_new.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index c0b114c346..d85f27980c 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -102,11 +102,12 @@ jobs: build-macos: name: macOS (${{ matrix.configure.label }}) - runs-on: macos-15 + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + os: [macos-15, macos-26] configure: - { label: "with parser generation", opt: "--enable-parser-generation" } - { label: "without curl", opt: "--without-curl" } @@ -220,7 +221,7 @@ jobs: cppcheck: name: Static analysis (cppcheck) - runs-on: macos-15 + runs-on: macos-26 steps: - uses: actions/checkout@v6 From 7cd1d6718982b20abe81fb3eda220cced2763dc3 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:22:13 +0200 Subject: [PATCH 16/34] Fix Windows test include path and case-insensitive override matching --- build/win32/CMakeLists.txt | 4 +- src/Makefile.am | 1 + src/operators/detect_sqli.cc | 7 +- src/operators/detect_xss.cc | 4 +- src/operators/libinjection_adapter.cc | 46 +++++++++++++ src/operators/libinjection_adapter.h | 27 ++++++++ test/Makefile.am | 1 + .../unit/operator-libinjection-error.json | 40 ++++++++++++ test/test-suite.in | 1 + test/unit/unit.cc | 65 ++++++++++++++++++- test/unit/unit_test.cc | 8 ++- test/unit/unit_test.h | 2 + 12 files changed, 196 insertions(+), 10 deletions(-) create mode 100644 src/operators/libinjection_adapter.cc create mode 100644 src/operators/libinjection_adapter.h create mode 100644 test/test-cases/unit/operator-libinjection-error.json diff --git a/build/win32/CMakeLists.txt b/build/win32/CMakeLists.txt index fbf39f08d9..5ee49714d3 100644 --- a/build/win32/CMakeLists.txt +++ b/build/win32/CMakeLists.txt @@ -164,7 +164,7 @@ project(libModSecurityTests) function(setTestTargetProperties executable) target_compile_definitions(${executable} PRIVATE WITH_PCRE2) - target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers) + target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers ${BASE_DIR}/others) target_link_libraries(${executable} PRIVATE libModSecurity pcre2::pcre2 dirent::dirent) add_package_dependency(${executable} WITH_YAJL yajl::yajl HAVE_YAJL) endfunction() @@ -239,7 +239,7 @@ setTestTargetProperties(rules_optimization) project(libModSecurityExamples) function(setExampleTargetProperties executable) - target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers) + target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers ${BASE_DIR}/others) target_link_libraries(${executable} PRIVATE libModSecurity) endfunction() diff --git a/src/Makefile.am b/src/Makefile.am index 14c26697b5..476585eda2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -187,6 +187,7 @@ OPERATORS = \ operators/contains_word.cc \ operators/detect_sqli.cc \ operators/detect_xss.cc \ + operators/libinjection_adapter.cc \ operators/ends_with.cc \ operators/eq.cc \ operators/fuzzy_hash.cc \ diff --git a/src/operators/detect_sqli.cc b/src/operators/detect_sqli.cc index 5e3014d294..8680909377 100644 --- a/src/operators/detect_sqli.cc +++ b/src/operators/detect_sqli.cc @@ -21,7 +21,7 @@ #include "src/operators/operator.h" #include "src/operators/libinjection_utils.h" -#include "libinjection/src/libinjection.h" +#include "src/operators/libinjection_adapter.h" #include "libinjection/src/libinjection_error.h" namespace modsecurity::operators { @@ -32,7 +32,7 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, std::array fingerprint{}; const injection_result_t sqli_result = - libinjection_sqli(input.c_str(), input.length(), fingerprint.data()); + runLibinjectionSQLi(input.c_str(), input.length(), fingerprint.data()); if (t == nullptr) { return isMaliciousLibinjectionResult(sqli_result); @@ -71,6 +71,9 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, std::string("Added DetectSQLi error input TX.0: ") + input); } + + // Keep m_matched untouched for parser-error paths to avoid + // introducing synthetic fingerprints for non-TRUE results. break; case LIBINJECTION_RESULT_FALSE: diff --git a/src/operators/detect_xss.cc b/src/operators/detect_xss.cc index ff6d36782f..e9ecf00a20 100644 --- a/src/operators/detect_xss.cc +++ b/src/operators/detect_xss.cc @@ -19,7 +19,7 @@ #include "src/operators/operator.h" #include "src/operators/libinjection_utils.h" -#include "libinjection/src/libinjection.h" +#include "src/operators/libinjection_adapter.h" #include "libinjection/src/libinjection_error.h" namespace modsecurity::operators { @@ -28,7 +28,7 @@ bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, const std::string& input, RuleMessage &ruleMessage) { const injection_result_t xss_result = - libinjection_xss(input.c_str(), input.length()); + runLibinjectionXSS(input.c_str(), input.length()); if (t == nullptr) { return isMaliciousLibinjectionResult(xss_result); diff --git a/src/operators/libinjection_adapter.cc b/src/operators/libinjection_adapter.cc new file mode 100644 index 0000000000..f2a50163f6 --- /dev/null +++ b/src/operators/libinjection_adapter.cc @@ -0,0 +1,46 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + */ + +#include "src/operators/libinjection_adapter.h" + +#include "libinjection/src/libinjection.h" + +namespace modsecurity::operators { +namespace { +// Per-thread overrides avoid cross-thread interference during mtstress tests. +thread_local DetectSQLiFn g_sqli_override = nullptr; +thread_local DetectXSSFn g_xss_override = nullptr; +} + +injection_result_t runLibinjectionSQLi(const char *input, size_t len, + char *fingerprint) { + if (DetectSQLiFn fn = g_sqli_override) { + return fn(input, len, fingerprint); + } + + return libinjection_sqli(input, len, fingerprint); +} + +injection_result_t runLibinjectionXSS(const char *input, size_t len) { + if (DetectXSSFn fn = g_xss_override) { + return fn(input, len); + } + + return libinjection_xss(input, len); +} + +void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn) { + g_sqli_override = fn; +} + +void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn) { + g_xss_override = fn; +} + +void clearLibinjectionOverridesForTesting() { + g_sqli_override = nullptr; + g_xss_override = nullptr; +} + +} // namespace modsecurity::operators diff --git a/src/operators/libinjection_adapter.h b/src/operators/libinjection_adapter.h new file mode 100644 index 0000000000..e8ed04f011 --- /dev/null +++ b/src/operators/libinjection_adapter.h @@ -0,0 +1,27 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + */ + +#ifndef SRC_OPERATORS_LIBINJECTION_ADAPTER_H_ +#define SRC_OPERATORS_LIBINJECTION_ADAPTER_H_ + +#include + +#include "libinjection/src/libinjection_error.h" // matches detect_xss.cc, detect_sqli.cc, and libinjection_utils.h + +namespace modsecurity::operators { + +using DetectSQLiFn = injection_result_t (*)(const char *, size_t, char *); +using DetectXSSFn = injection_result_t (*)(const char *, size_t); + +injection_result_t runLibinjectionSQLi(const char *input, size_t len, + char *fingerprint); +injection_result_t runLibinjectionXSS(const char *input, size_t len); + +void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn); +void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn); +void clearLibinjectionOverridesForTesting(); + +} // namespace modsecurity::operators + +#endif // SRC_OPERATORS_LIBINJECTION_ADAPTER_H_ diff --git a/test/Makefile.am b/test/Makefile.am index 2e7e05d614..de8d4f4af4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -73,6 +73,7 @@ unit_tests_LDFLAGS = \ unit_tests_CPPFLAGS = \ -Icommon \ -I$(top_srcdir)/ \ + -I$(top_srcdir)/others \ -g \ -I$(top_builddir)/headers \ $(CURL_CFLAGS) \ diff --git a/test/test-cases/unit/operator-libinjection-error.json b/test/test-cases/unit/operator-libinjection-error.json new file mode 100644 index 0000000000..ef440cd4c6 --- /dev/null +++ b/test/test-cases/unit/operator-libinjection-error.json @@ -0,0 +1,40 @@ +[ + { + "type": "op", + "name": "detectXSS", + "param": "", + "input": "", + "ret": 1, + "capture": 1, + "libinjection_override": "error", + "output": "" + }, + { + "type": "op", + "name": "detectSQLi", + "param": "", + "input": "''''''", + "ret": 1, + "capture": 1, + "libinjection_override": "error", + "output": "''''''" + }, + { + "type": "op", + "name": "detectXSS", + "param": "", + "input": "", + "ret": 1, + "capture": 1, + "output": "" + }, + { + "type": "op", + "name": "detectSQLi", + "param": "", + "input": "ascii(substring(version() from 1 for 1))", + "ret": 1, + "capture": 1, + "output": "f(f(f" + } +] diff --git a/test/test-suite.in b/test/test-suite.in index 6e8754254b..c667bf677f 100644 --- a/test/test-suite.in +++ b/test/test-suite.in @@ -198,6 +198,7 @@ TESTS+=test/test-cases/secrules-language-tests/operators/contains.json TESTS+=test/test-cases/secrules-language-tests/operators/containsWord.json TESTS+=test/test-cases/secrules-language-tests/operators/detectSQLi.json TESTS+=test/test-cases/secrules-language-tests/operators/detectXSS.json +TESTS+=test/test-cases/unit/operator-libinjection-error.json TESTS+=test/test-cases/secrules-language-tests/operators/endsWith.json TESTS+=test/test-cases/secrules-language-tests/operators/eq.json TESTS+=test/test-cases/secrules-language-tests/operators/ge.json diff --git a/test/unit/unit.cc b/test/unit/unit.cc index 8bf5954d27..0cb20f2f6e 100644 --- a/test/unit/unit.cc +++ b/test/unit/unit.cc @@ -28,6 +28,8 @@ #include "src/actions/transformations/transformation.h" #include "modsecurity/transaction.h" #include "modsecurity/actions/action.h" +#include "src/actions/capture.h" +#include "src/operators/libinjection_adapter.h" #include "test/common/modsecurity_test.h" @@ -57,6 +59,34 @@ void print_help() { } +namespace { +injection_result_t sqli_force_error(const char *, size_t, char *) { + return LIBINJECTION_RESULT_ERROR; +} + +injection_result_t xss_force_error(const char *, size_t) { + return LIBINJECTION_RESULT_ERROR; +} + +void configure_libinjection_override(const UnitTest &t) { + modsecurity::operators::clearLibinjectionOverridesForTesting(); + + if (t.libinjection_override != "error") { + return; + } + + const std::string operator_name = modsecurity::utils::string::tolower(t.name); + + if (operator_name == "detectsqli") { + modsecurity::operators::setLibinjectionSQLiOverrideForTesting( + sqli_force_error); + } else if (operator_name == "detectxss") { + modsecurity::operators::setLibinjectionXSSOverrideForTesting( + xss_force_error); + } +} +} // namespace + struct OperatorTest { using ItemType = Operator; @@ -71,13 +101,42 @@ struct OperatorTest { } static UnitTestResult eval(ItemType &op, const UnitTest &t, modsecurity::Transaction &transaction) { - modsecurity::RuleWithActions rule{nullptr, nullptr, "dummy.conf", -1}; + configure_libinjection_override(t); + + std::unique_ptr actions; + if (t.capture) { + actions = std::make_unique(); + actions->push_back(new modsecurity::actions::Capture("capture")); + } + + modsecurity::RuleWithActions rule{actions.release(), nullptr, "dummy.conf", -1}; modsecurity::RuleMessage ruleMessage{rule, transaction}; - return {op.evaluate(&transaction, &rule, t.input, ruleMessage), {}}; + + const bool matched = op.evaluate(&transaction, &rule, t.input, ruleMessage); + + UnitTestResult result; + result.ret = matched; + if (t.capture) { + auto tx0 = transaction.m_collections.m_tx_collection->resolveFirst("0"); + if (tx0 != nullptr) { + result.output = *tx0; + } + } + + modsecurity::operators::clearLibinjectionOverridesForTesting(); + return result; } static bool check(const UnitTestResult &result, const UnitTest &t) { - return result.ret != t.ret; + if (result.ret != t.ret) { + return true; + } + + if (t.capture || t.output.empty() == false) { + return result.output != t.output; + } + + return false; } }; diff --git a/test/unit/unit_test.cc b/test/unit/unit_test.cc index e67c100523..36e1c5a3af 100644 --- a/test/unit/unit_test.cc +++ b/test/unit/unit_test.cc @@ -110,11 +110,13 @@ std::unique_ptr UnitTest::from_yajl_node(const yajl_val &node) { size_t num_tests = node->u.object.len; auto u = std::make_unique(); + u->skipped = false; + u->capture = 0; + for (int i = 0; i < num_tests; i++) { const char *key = node->u.object.keys[ i ]; yajl_val val = node->u.object.values[ i ]; - u->skipped = false; if (strcmp(key, "param") == 0) { u->param = YAJL_GET_STRING(val); } else if (strcmp(key, "input") == 0) { @@ -128,6 +130,10 @@ std::unique_ptr UnitTest::from_yajl_node(const yajl_val &node) { u->type = YAJL_GET_STRING(val); } else if (strcmp(key, "ret") == 0) { u->ret = YAJL_GET_INTEGER(val); + } else if (strcmp(key, "capture") == 0) { + u->capture = YAJL_GET_INTEGER(val); + } else if (strcmp(key, "libinjection_override") == 0) { + u->libinjection_override = YAJL_GET_STRING(val); } else if (strcmp(key, "output") == 0) { u->output = std::string(YAJL_GET_STRING(val)); json2bin(&u->output); diff --git a/test/unit/unit_test.h b/test/unit/unit_test.h index ffd776442b..95257d7061 100644 --- a/test/unit/unit_test.h +++ b/test/unit/unit_test.h @@ -44,7 +44,9 @@ class UnitTest { std::string type; std::string filename; std::string output; + std::string libinjection_override; int ret; + int capture; int skipped; UnitTestResult result; }; From 0af7e13776c28aa3cb294137b501e135f2e9b1a1 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:11:11 +0200 Subject: [PATCH 17/34] Update libinjection_adapter.cc --- src/operators/libinjection_adapter.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/operators/libinjection_adapter.cc b/src/operators/libinjection_adapter.cc index f2a50163f6..a0281f8c23 100644 --- a/src/operators/libinjection_adapter.cc +++ b/src/operators/libinjection_adapter.cc @@ -8,9 +8,16 @@ namespace modsecurity::operators { namespace { + // Per-thread overrides avoid cross-thread interference during mtstress tests. -thread_local DetectSQLiFn g_sqli_override = nullptr; -thread_local DetectXSSFn g_xss_override = nullptr; +// Intentional design: +// - thread_local to isolate tests across threads +// - function pointers to keep zero-overhead call path +// - mutable for test injection hooks +// NOSONAR: required for testing override mechanism (see set*OverrideForTesting) +thread_local DetectSQLiFn g_sqli_override = nullptr; // NOSONAR +thread_local DetectXSSFn g_xss_override = nullptr; // NOSONAR + } injection_result_t runLibinjectionSQLi(const char *input, size_t len, @@ -30,11 +37,15 @@ injection_result_t runLibinjectionXSS(const char *input, size_t len) { return libinjection_xss(input, len); } -void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn) { +// Test-only hook: allows injecting alternative detection functions +// NOSONAR: function pointer is intentional (no std::function overhead) +void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn) { // NOSONAR g_sqli_override = fn; } -void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn) { +// Test-only hook: allows injecting alternative detection functions +// NOSONAR: function pointer is intentional (no std::function overhead) +void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn) { // NOSONAR g_xss_override = fn; } From 98c0f87123154da2162d98de6e6a0ef639c8b2b7 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:26:15 +0200 Subject: [PATCH 18/34] Add pcre support and update dependencies in CI --- .github/workflows/ci_new.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index d85f27980c..8364cae878 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -32,6 +32,7 @@ jobs: - { label: "without ssdeep", opt: "--without-ssdeep" } - { label: "with lmdb", opt: "--with-lmdb" } - { label: "with pcre2 (default)", opt: "--with-pcre2" } + - { label: "with pcre", opt: "--with-pcre" } steps: - uses: actions/checkout@v6 @@ -77,6 +78,7 @@ jobs: libxml2-dev \ libfuzzy-dev \ pcre2-utils \ + libpcre3-dev \ bison \ flex \ pkg-config \ @@ -140,7 +142,7 @@ jobs: ssdeep \ pcre \ bison \ - flex + flex - name: Run build preparation script run: ./build.sh @@ -231,7 +233,7 @@ jobs: - name: Install cppcheck run: | - brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep + brew install autoconf automake libtool cppcheck libmaxminddb yajl lua lmdb ssdeep - name: Configure project run: | From cfa0bcdf5e3a846d721d7eb84359c5d3482d8e7c Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:32:34 +0200 Subject: [PATCH 19/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 8364cae878..b7f11ff0f1 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -31,7 +31,7 @@ jobs: - { label: "without geoip", opt: "--without-geoip" } - { label: "without ssdeep", opt: "--without-ssdeep" } - { label: "with lmdb", opt: "--with-lmdb" } - - { label: "with pcre2 (default)", opt: "--with-pcre2" } + - { label: "with pcre2 (default)", opt: "" } - { label: "with pcre", opt: "--with-pcre" } steps: From d6648d1641acbfcb97761837896033d4f443fdf7 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:39:38 +0200 Subject: [PATCH 20/34] Add libinjection_error.h to Makefile.am --- others/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/others/Makefile.am b/others/Makefile.am index b102a0330c..beba0bfc84 100644 --- a/others/Makefile.am +++ b/others/Makefile.am @@ -15,6 +15,7 @@ noinst_HEADERS = \ libinjection/src/libinjection_sqli.h \ libinjection/src/libinjection_sqli_data.h \ libinjection/src/libinjection_xss.h \ + libinjection/src/libinjection_error.h \ mbedtls/include/mbedtls/base64.h \ mbedtls/include/mbedtls/check_config.h \ mbedtls/include/mbedtls/mbedtls_config.h \ From 5c04f6b7f505b19e20df9f4a0d386b31997fb7b7 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:23:04 +0200 Subject: [PATCH 21/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index b7f11ff0f1..21470194f4 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -40,6 +40,7 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Detect latest Lua dev package id: detect_lua shell: bash @@ -48,7 +49,7 @@ jobs: sudo apt-get update -y -qq - CANDIDATES="$(apt-cache search '^liblua[0-9]+\.[0-9]+-dev$' | awk '{print $1}')" + CANDIDATES="$(apt-cache pkgnames | grep -E '^liblua[0-9]+\.[0-9]+-dev$' || true)" if [ -z "$CANDIDATES" ]; then echo "No libluaX.Y-dev package found" @@ -63,9 +64,15 @@ jobs: | awk '{print $2}' )" + if [ -z "$BEST_PKG" ]; then + echo "Failed to determine Lua package" + exit 1 + fi + echo "lua_pkg=$BEST_PKG" >> "$GITHUB_OUTPUT" echo "Using $BEST_PKG" + - name: Install dependencies run: | sudo apt-get install -y \ From 6997bb434d2cc54b3e84be43c0e343fbe716c4a0 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:33:16 +0200 Subject: [PATCH 22/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 21470194f4..8714f7a52d 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -92,6 +92,12 @@ jobs: python3 \ python3-venv + - name: Show Lua installation + run: | + which lua || true + lua -v || true + dpkg -l | grep lua || true + - name: Run build preparation script run: ./build.sh From 19ea6d0716abeed0f63607482e79a8b7ec62230a Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:36:37 +0200 Subject: [PATCH 23/34] Isolate transaction state in multithreaded unit tests --- test/unit/unit.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/unit/unit.cc b/test/unit/unit.cc index 0cb20f2f6e..5931ea81c0 100644 --- a/test/unit/unit.cc +++ b/test/unit/unit.cc @@ -173,7 +173,8 @@ UnitTestResult perform_unit_test_once(const UnitTest &t, modsecurity::Transactio template -UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, modsecurity::Transaction &transaction) { +UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, + modsecurity_test::ModSecurityTestContext &context) { constexpr auto NUM_THREADS = 50; constexpr auto ITERATIONS = 5'000; @@ -188,8 +189,9 @@ UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, modsecurity::T { auto &result = results[i]; threads[i] = std::thread( - [&item, &t, &result, &transaction]() + [&item, &t, &result, &context]() { + auto transaction = context.create_transaction(); for (auto j = 0; j != ITERATIONS; ++j) result = TestType::eval(*item.get(), t, transaction); }); @@ -212,12 +214,13 @@ UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, modsecurity::T template void perform_unit_test_helper(const ModSecurityTest &test, UnitTest &t, - ModSecurityTestResults &res, modsecurity::Transaction &transaction) { + ModSecurityTestResults &res, modsecurity::Transaction &transaction, + modsecurity_test::ModSecurityTestContext &context) { if (!test.m_test_multithreaded) t.result = perform_unit_test_once(t, transaction); else - t.result = perform_unit_test_multithreaded(t, transaction); + t.result = perform_unit_test_multithreaded(t, context); if (TestType::check(t.result, t)) { res.push_back(&t); @@ -257,9 +260,9 @@ void perform_unit_test(const ModSecurityTest &test, UnitTest &t, } if (t.type == "op") { - perform_unit_test_helper(test, t, res, transaction); + perform_unit_test_helper(test, t, res, transaction, context); } else if (t.type == "tfn") { - perform_unit_test_helper(test, t, res, transaction); + perform_unit_test_helper(test, t, res, transaction, context); } else { std::cerr << "Failed. Test type is unknown: << " << t.type; std::cerr << std::endl; From db5583233cf5571d9eb00f544a1f74ee68f01ce6 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:50:45 +0200 Subject: [PATCH 24/34] Update ci_new.yml --- .github/workflows/ci_new.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci_new.yml b/.github/workflows/ci_new.yml index 8714f7a52d..a1009e02b1 100644 --- a/.github/workflows/ci_new.yml +++ b/.github/workflows/ci_new.yml @@ -40,7 +40,6 @@ jobs: fetch-depth: 0 submodules: recursive - - name: Detect latest Lua dev package id: detect_lua shell: bash @@ -97,7 +96,7 @@ jobs: which lua || true lua -v || true dpkg -l | grep lua || true - + - name: Run build preparation script run: ./build.sh From 468f681ca33aa077d4c94291a5fbaf9c24876fcc Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:41:18 +0200 Subject: [PATCH 25/34] Update libinjection_adapter.cc --- src/operators/libinjection_adapter.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/operators/libinjection_adapter.cc b/src/operators/libinjection_adapter.cc index a0281f8c23..ca7fdaca02 100644 --- a/src/operators/libinjection_adapter.cc +++ b/src/operators/libinjection_adapter.cc @@ -1,5 +1,16 @@ /* * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * */ #include "src/operators/libinjection_adapter.h" From d19f58bd0b60b72599df423f9d17dcc34eced8f3 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:41:29 +0200 Subject: [PATCH 26/34] Update libinjection_adapter.h --- src/operators/libinjection_adapter.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/operators/libinjection_adapter.h b/src/operators/libinjection_adapter.h index e8ed04f011..f5b78371b9 100644 --- a/src/operators/libinjection_adapter.h +++ b/src/operators/libinjection_adapter.h @@ -1,5 +1,16 @@ /* * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * */ #ifndef SRC_OPERATORS_LIBINJECTION_ADAPTER_H_ From 91fbf351e072401eb0bb8ebcc0063745a853f359 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 07:58:06 +0200 Subject: [PATCH 27/34] Hide testing override functions from symbol table --- src/operators/libinjection_adapter.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/operators/libinjection_adapter.h b/src/operators/libinjection_adapter.h index f5b78371b9..94141d6e7c 100644 --- a/src/operators/libinjection_adapter.h +++ b/src/operators/libinjection_adapter.h @@ -22,6 +22,13 @@ namespace modsecurity::operators { +// Keep testing override hooks out of the shared-library dynamic symbol table. +#if defined(__GNUC__) || defined(__clang__) +#define MODSEC_HIDDEN __attribute__((visibility("hidden"))) +#else +#define MODSEC_HIDDEN +#endif + using DetectSQLiFn = injection_result_t (*)(const char *, size_t, char *); using DetectXSSFn = injection_result_t (*)(const char *, size_t); @@ -29,9 +36,11 @@ injection_result_t runLibinjectionSQLi(const char *input, size_t len, char *fingerprint); injection_result_t runLibinjectionXSS(const char *input, size_t len); -void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn); -void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn); -void clearLibinjectionOverridesForTesting(); +MODSEC_HIDDEN void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn); +MODSEC_HIDDEN void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn); +MODSEC_HIDDEN void clearLibinjectionOverridesForTesting(); + +#undef MODSEC_HIDDEN } // namespace modsecurity::operators From e10e9e011f44f6bfe0f27a3b35e863cf42a1b297 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 07:59:27 +0200 Subject: [PATCH 28/34] Log input in hex format for SQLi detection --- src/operators/detect_sqli.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/operators/detect_sqli.cc b/src/operators/detect_sqli.cc index 8680909377..a16b398e3d 100644 --- a/src/operators/detect_sqli.cc +++ b/src/operators/detect_sqli.cc @@ -22,12 +22,15 @@ #include "src/operators/operator.h" #include "src/operators/libinjection_utils.h" #include "src/operators/libinjection_adapter.h" +#include "src/utils/string.h" #include "libinjection/src/libinjection_error.h" namespace modsecurity::operators { bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, const std::string& input, RuleMessage &ruleMessage) { + const std::string loggable_input = + utils::string::limitTo(80, utils::string::toHexIfNeeded(input)); std::array fingerprint{}; @@ -44,7 +47,7 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, ms_dbg_a(t, 4, std::string("detected SQLi using libinjection with fingerprint '") - + fingerprint.data() + "' at: '" + input + "'"); + + fingerprint.data() + "' at: '" + loggable_input + "'"); if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst( @@ -61,7 +64,7 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, std::string("libinjection parser error during SQLi analysis (") + libinjectionResultToString(sqli_result) + "); treating as match (fail-safe). Input: '" - + input + "'"); + + loggable_input + "'"); if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst( @@ -79,7 +82,7 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, case LIBINJECTION_RESULT_FALSE: ms_dbg_a(t, 9, std::string("libinjection was not able to find any SQLi in: ") - + input); + + loggable_input); break; } From 29a461bee6b0baa741bf003c8af60eab8b20f0a3 Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 08:00:04 +0200 Subject: [PATCH 29/34] Add logging for input in XSS detection --- src/operators/detect_xss.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/operators/detect_xss.cc b/src/operators/detect_xss.cc index e9ecf00a20..6900ff272c 100644 --- a/src/operators/detect_xss.cc +++ b/src/operators/detect_xss.cc @@ -20,12 +20,15 @@ #include "src/operators/operator.h" #include "src/operators/libinjection_utils.h" #include "src/operators/libinjection_adapter.h" +#include "src/utils/string.h" #include "libinjection/src/libinjection_error.h" namespace modsecurity::operators { bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, const std::string& input, RuleMessage &ruleMessage) { + const std::string loggable_input = + utils::string::limitTo(80, utils::string::toHexIfNeeded(input)); const injection_result_t xss_result = runLibinjectionXSS(input.c_str(), input.length()); @@ -48,7 +51,7 @@ bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, std::string("libinjection parser error during XSS analysis (") + libinjectionResultToString(xss_result) + "); treating as match (fail-safe). Input: " - + input); + + loggable_input); if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst("0", input); ms_dbg_a(t, 7, std::string("Added DetectXSS error input TX.0: ") + input); @@ -57,7 +60,8 @@ bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, case LIBINJECTION_RESULT_FALSE: ms_dbg_a(t, 9, - std::string("libinjection was not able to find any XSS in: ") + input); + std::string("libinjection was not able to find any XSS in: ") + + loggable_input); break; } From 7c104e421fce08b4b9e6cda661b14db35941ca1b Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 08:07:05 +0200 Subject: [PATCH 30/34] Update multithreaded unit test implementation Refactor multithreaded unit test to use thread-specific context. --- test/unit/unit.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/unit/unit.cc b/test/unit/unit.cc index 5931ea81c0..5dc515d82e 100644 --- a/test/unit/unit.cc +++ b/test/unit/unit.cc @@ -175,6 +175,7 @@ UnitTestResult perform_unit_test_once(const UnitTest &t, modsecurity::Transactio template UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, modsecurity_test::ModSecurityTestContext &context) { + (void)context; constexpr auto NUM_THREADS = 50; constexpr auto ITERATIONS = 5'000; @@ -189,9 +190,11 @@ UnitTestResult perform_unit_test_multithreaded(const UnitTest &t, { auto &result = results[i]; threads[i] = std::thread( - [&item, &t, &result, &context]() + [&item, &t, &result]() { - auto transaction = context.create_transaction(); + modsecurity_test::ModSecurityTestContext thread_context( + "ModSecurity-unit mtstress-thread"); + auto transaction = thread_context.create_transaction(); for (auto j = 0; j != ITERATIONS; ++j) result = TestType::eval(*item.get(), t, transaction); }); From 0cf4f3c78d21a15b2bfca774ff819f6396b394cf Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:03:33 +0200 Subject: [PATCH 31/34] Update libinjection_adapter.h --- src/operators/libinjection_adapter.h | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/operators/libinjection_adapter.h b/src/operators/libinjection_adapter.h index 94141d6e7c..f5b78371b9 100644 --- a/src/operators/libinjection_adapter.h +++ b/src/operators/libinjection_adapter.h @@ -22,13 +22,6 @@ namespace modsecurity::operators { -// Keep testing override hooks out of the shared-library dynamic symbol table. -#if defined(__GNUC__) || defined(__clang__) -#define MODSEC_HIDDEN __attribute__((visibility("hidden"))) -#else -#define MODSEC_HIDDEN -#endif - using DetectSQLiFn = injection_result_t (*)(const char *, size_t, char *); using DetectXSSFn = injection_result_t (*)(const char *, size_t); @@ -36,11 +29,9 @@ injection_result_t runLibinjectionSQLi(const char *input, size_t len, char *fingerprint); injection_result_t runLibinjectionXSS(const char *input, size_t len); -MODSEC_HIDDEN void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn); -MODSEC_HIDDEN void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn); -MODSEC_HIDDEN void clearLibinjectionOverridesForTesting(); - -#undef MODSEC_HIDDEN +void setLibinjectionSQLiOverrideForTesting(DetectSQLiFn fn); +void setLibinjectionXSSOverrideForTesting(DetectXSSFn fn); +void clearLibinjectionOverridesForTesting(); } // namespace modsecurity::operators From 3e98c815fb0090a25dfd7d3dbc6f5af87cf2d36b Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:43:48 +0200 Subject: [PATCH 32/34] Guard log-only detect operator variables under NO_LOGS --- src/operators/detect_sqli.cc | 8 ++++++++ src/operators/detect_xss.cc | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/operators/detect_sqli.cc b/src/operators/detect_sqli.cc index a16b398e3d..5bda1a494f 100644 --- a/src/operators/detect_sqli.cc +++ b/src/operators/detect_sqli.cc @@ -29,8 +29,10 @@ namespace modsecurity::operators { bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, const std::string& input, RuleMessage &ruleMessage) { +#ifndef NO_LOGS const std::string loggable_input = utils::string::limitTo(80, utils::string::toHexIfNeeded(input)); +#endif std::array fingerprint{}; @@ -45,9 +47,11 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, case LIBINJECTION_RESULT_TRUE: t->m_matched.emplace_back(fingerprint.data()); +#ifndef NO_LOGS ms_dbg_a(t, 4, std::string("detected SQLi using libinjection with fingerprint '") + fingerprint.data() + "' at: '" + loggable_input + "'"); +#endif if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst( @@ -60,11 +64,13 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, break; case LIBINJECTION_RESULT_ERROR: +#ifndef NO_LOGS ms_dbg_a(t, 4, std::string("libinjection parser error during SQLi analysis (") + libinjectionResultToString(sqli_result) + "); treating as match (fail-safe). Input: '" + loggable_input + "'"); +#endif if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst( @@ -80,9 +86,11 @@ bool DetectSQLi::evaluate(Transaction *t, RuleWithActions *rule, break; case LIBINJECTION_RESULT_FALSE: +#ifndef NO_LOGS ms_dbg_a(t, 9, std::string("libinjection was not able to find any SQLi in: ") + loggable_input); +#endif break; } diff --git a/src/operators/detect_xss.cc b/src/operators/detect_xss.cc index 6900ff272c..7395b247a5 100644 --- a/src/operators/detect_xss.cc +++ b/src/operators/detect_xss.cc @@ -27,8 +27,10 @@ namespace modsecurity::operators { bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, const std::string& input, RuleMessage &ruleMessage) { +#ifndef NO_LOGS const std::string loggable_input = utils::string::limitTo(80, utils::string::toHexIfNeeded(input)); +#endif const injection_result_t xss_result = runLibinjectionXSS(input.c_str(), input.length()); @@ -47,11 +49,13 @@ bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, break; case LIBINJECTION_RESULT_ERROR: +#ifndef NO_LOGS ms_dbg_a(t, 4, std::string("libinjection parser error during XSS analysis (") + libinjectionResultToString(xss_result) + "); treating as match (fail-safe). Input: " + loggable_input); +#endif if (rule != nullptr && rule->hasCaptureAction()) { t->m_collections.m_tx_collection->storeOrUpdateFirst("0", input); ms_dbg_a(t, 7, std::string("Added DetectXSS error input TX.0: ") + input); @@ -59,9 +63,11 @@ bool DetectXSS::evaluate(Transaction *t, RuleWithActions *rule, break; case LIBINJECTION_RESULT_FALSE: +#ifndef NO_LOGS ms_dbg_a(t, 9, std::string("libinjection was not able to find any XSS in: ") + loggable_input); +#endif break; } From 93a50a43698b94a313976867f38a1042c79c0a6b Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:07:54 +0200 Subject: [PATCH 33/34] docs: strengthen evidence model in libinjection audit --- ...injection-v4-migration-audit-2026-04-03.md | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 doc/libinjection-v4-migration-audit-2026-04-03.md diff --git a/doc/libinjection-v4-migration-audit-2026-04-03.md b/doc/libinjection-v4-migration-audit-2026-04-03.md new file mode 100644 index 0000000000..d59841244f --- /dev/null +++ b/doc/libinjection-v4-migration-audit-2026-04-03.md @@ -0,0 +1,244 @@ +# libinjection v4.0 migration audit (beweisnahe Fassung, 2026-04-03) + +## 1) Executive summary + +- **OBSERVED:** Die direkten libinjection-Aufrufer für SQLi/XSS nutzen `injection_result_t` und behandeln `LIBINJECTION_RESULT_TRUE`, `LIBINJECTION_RESULT_FALSE`, `LIBINJECTION_RESULT_ERROR` explizit per `switch`. `ERROR` wird lokal fail-safe als Match behandelt. +- **OBSERVED:** Die Operator-Engine propagiert Ergebnisse nur als `bool` (Match/No-Match), nicht als Tri-State. +- **INFERRED:** Dadurch ist die Migration sicherheitslogisch (Enforcement) robust, aber nicht vollständig tri-state-konsistent entlang der gesamten Call-Chain/Architektur. + +**Endurteil:** `PARTIALLY MIGRATED` + +--- + +## 2) Migration scope discovered + +### 2.1 Direkte libinjection-Nutzung + +- **OBSERVED:** Adapter-Signaturen und direkte Calls: + - `runLibinjectionSQLi(...) -> libinjection_sqli(...)` + - `runLibinjectionXSS(...) -> libinjection_xss(...)` +- **OBSERVED:** Signaturen verwenden `injection_result_t` (keine int/bool-Rückgabe im Adapter). + +### 2.2 Result-/Error-Typen und Utilities + +- **OBSERVED:** `libinjection_error.h` wird eingebunden in Adapter/Operator-Utilities/Detectors. +- **OBSERVED:** `isMaliciousLibinjectionResult(...)` mappt `TRUE || ERROR` auf „malicious“ (fail-safe). + +### 2.3 Security-Entry-Points im Produkt + +- **OBSERVED:** `DetectSQLi::evaluate(...)` und `DetectXSS::evaluate(...)` sind die produktiven Operator-Entry-Points. +- **OBSERVED:** Parser/Factory binden `@detectSQLi` und `@detectXSS` auf diese Operatoren. + +### 2.4 Testrelevante Wrapper/Overrides + +- **OBSERVED:** Test-Hooks erlauben erzwungenes `LIBINJECTION_RESULT_ERROR` via thread-local Override. +- **OBSERVED:** Unit-Test-Fälle prüfen `ret=1` + Capture für erzwungene ERROR-Pfade. + +### 2.5 Nicht gefundene APIs + +- **OBSERVED:** Keine Verwendungen von `libinjection_is_xss` oder `libinjection_h5_next` im Repository-Codepfad (`src/`, `test/`). +- **UNCERTAIN:** Ob diese APIs in externen Modulen/Build-Varianten außerhalb dieses Repos genutzt werden, ist nicht beweisbar. + +--- + +## 3) Call-chain analysis + +## 3.1 SQLi-Kette (konkret) + +1. **OBSERVED (Tri-State vorhanden):** `DetectSQLi::evaluate` hält libinjection-Ergebnis als `const injection_result_t sqli_result`. +2. **OBSERVED (Tri-State verarbeitet):** `switch (sqli_result)` hat Cases für `TRUE`, `ERROR`, `FALSE`. +3. **OBSERVED (Tri-State -> bool):** Rückgabe erfolgt über `return isMaliciousLibinjectionResult(sqli_result);`. +4. **OBSERVED (nur bool propagiert):** `Operator::evaluateInternal(...)` arbeitet mit `bool res` und gibt bool zurück. +5. **OBSERVED (Enforcement bool-basiert):** `RuleWithOperator::executeOperatorAt` und `RuleWithOperator::evaluate` nutzen nur `bool ret` zur Match/No-Match-Entscheidung. + +## 3.2 XSS-Kette (konkret) + +1. **OBSERVED (Tri-State vorhanden):** `DetectXSS::evaluate` hält `const injection_result_t xss_result`. +2. **OBSERVED (Tri-State verarbeitet):** `switch (xss_result)` mit Cases `TRUE`, `ERROR`, `FALSE`. +3. **OBSERVED (Tri-State -> bool):** Rückgabe über `isMaliciousLibinjectionResult(xss_result)`. +4. **OBSERVED (nur bool propagiert):** Weiter oben identische bool-Engine. + +## 3.3 Zentrale These „Tri-State wird auf bool reduziert“ (beweisnah) + +- **Letzte Stelle mit echtem Tri-State:** + - `DetectSQLi::evaluate`: lokale Variable `sqli_result` und `switch`. + - `DetectXSS::evaluate`: lokale Variable `xss_result` und `switch`. +- **Stelle der Reduktion auf bool:** + - `return isMaliciousLibinjectionResult(...);` in beiden evaluate-Methoden. +- **Stelle, an der nur Match/No-Match propagiert wird:** + - `Operator::evaluateInternal` (`bool res`), `RuleWithOperator::executeOperatorAt` (`bool ret`), `RuleWithOperator::evaluate` (`if (ret == true) ...`). +- **Konkrete Folgen:** + - **OBSERVED:** Enforcement bleibt fail-safe, weil `ERROR` als `true` (match) in die bool-Engine läuft. + - **INFERRED:** Logging/Reasoning oberhalb der Detector-Ebene kann den Zustand „parser error vs. attack detected“ nicht als getrennten Rückgabekanal nutzen; Differenzierung passiert nur lokal über Debug-Logs in den Detectoren. + +--- + +## 4) Correctly migrated locations + +### C-01: Adapter-Interface auf v4-Resulttypen + +- **OBSERVED:** `DetectSQLiFn`, `DetectXSSFn`, `runLibinjectionSQLi`, `runLibinjectionXSS` verwenden `injection_result_t`. +- **Mechanismus:** Interface-Signatur migriert, kein implizites int/bool am Adapter-Rand. + +### C-02: Expliziter Tri-State-Switch in SQLi-Operator + +- **OBSERVED:** `switch (sqli_result)` mit separaten Cases `TRUE`, `ERROR`, `FALSE`. +- **Mechanismus:** explizite Zustandsbehandlung statt truthy/falsy. +- **OBSERVED:** `ERROR`-Case loggt parser error und behandelt den Fall als Match (fail-safe) via finalem Mapping. +- **OBSERVED:** Capture-Verhalten unterscheidet TRUE (Fingerprint) vs ERROR (Input), d.h. semantisch bewusst. + +### C-03: Expliziter Tri-State-Switch in XSS-Operator + +- **OBSERVED:** `switch (xss_result)` mit Cases `TRUE`, `ERROR`, `FALSE`. +- **Mechanismus:** explizite Zustandsbehandlung. +- **OBSERVED:** `ERROR`-Case loggt parser error und behandelt als Match (fail-safe) via finalem Mapping. +- **OBSERVED:** Capture bei TRUE und ERROR vorhanden. + +### C-04: Zentrales fail-safe Mapping klar dokumentiert + +- **OBSERVED:** `isMaliciousLibinjectionResult` mappt `TRUE || ERROR` auf `true`. +- **Mechanismus:** explizites fail-safe Mapping in Utility-Funktion. + +### C-05: Tests für ERROR-Pfad vorhanden + +- **OBSERVED:** Test-Override-Funktionen geben `LIBINJECTION_RESULT_ERROR` zurück. +- **OBSERVED:** JSON-Testfälle erwarten Match (`ret: 1`) bei Override `error` für beide Operatoren. + +--- + +## 5) Findings + +### F-01 +- **Severity:** medium +- **Titel:** Tri-State-Semantik endet an Detector-Grenze; Engine bleibt bi-state +- **Kategorie:** **architektonisch** +- **Exakte Code-Stellen:** + - Tri-State zuletzt lokal: `detect_sqli.cc` (`sqli_result`, `switch`), `detect_xss.cc` (`xss_result`, `switch`) + - Reduktion auf bool: `return isMaliciousLibinjectionResult(...)` in beiden Dateien + - Nur bool-Propagation: `operator.cc` (`bool res`), `rule_with_operator.cc` (`bool ret`, match-Entscheidungen) +- **Relevanter Codepfad:** + - `libinjection_*` -> `runLibinjection*` -> `Detect*::evaluate` (tri-state) -> `isMalicious...` (bool) -> `Operator::evaluateInternal` (bool) -> `RuleWithOperator::*` (bool) +- **Warum Information dort verloren geht:** + - Beim `return bool` aus `Detect*::evaluate` wird `ERROR` in denselben Rückgabewert wie „Attacke erkannt“ gefaltet; danach existiert kein separater Zustand mehr im Interface. +- **OBSERVED:** bool-Verträge in `Operator`/`RuleWithOperator` sind binär. +- **INFERRED:** Kein zentraler, maschinenlesbarer Kanal für „parser-error“ bis zur Rule-Engine (außer lokale Logs/Capture-Details). +- **Warum gegen FULL MIGRATION:** + - Full Migration entlang aller Call-Chains würde `ERROR` als eigenständigen Zustand bis zu den relevanten Systemgrenzen erhalten oder explizit typisieren. +- **Auswirkung:** + - **Security correctness:** fail-safe bleibt erhalten (kein offensichtliches fail-open in diesem Pfad). + - **Migration completeness:** tri-state nicht end-to-end. +- **ERROR-Verhalten:** fail-closed/fail-safe bei Enforcement, aber architektonisch zusammengefaltet. + +### F-02 +- **Severity:** low +- **Titel:** Fehlender expliziter Umgang mit unbekannten zukünftigen Result-Werten in Detector-Switches +- **Kategorie:** **lokal** +- **Exakte Code-Stellen:** `detect_sqli.cc` und `detect_xss.cc` `switch` ohne `default`. +- **Relevanter Codepfad:** `Detect*::evaluate -> switch(result) -> return isMalicious...` +- **Warum Information dort verloren geht:** + - Unbekannte Enum-Werte würden nicht geloggt/behandelt im `switch`; final entscheidet nur `isMalicious...` (aktuell nur TRUE/ERROR=true). +- **OBSERVED:** `libinjectionResultToString` hat Fallback `"unexpected-result"`, aber wird nur in ERROR-Case verwendet. +- **INFERRED:** Bei API-Erweiterung könnte neues Ergebnis nicht explizit auffallen. +- **Warum gegen FULL MIGRATION:** + - Strenge Migration fordert robuste, explizite Ergebnisbehandlung; fehlender Guard verschlechtert Zukunftssicherheit. +- **Auswirkung:** derzeit gering, potenziell semantische Drift bei zukünftigen libinjection-Enums. +- **ERROR-Verhalten:** unverändert fail-safe; Finding betrifft Zukunfts-/Robustheitsaspekt. + +--- + +## 6) Test coverage assessment + +- **OBSERVED:** Es existieren Unit-Tests für erzwungenes `LIBINJECTION_RESULT_ERROR` bei detectXSS/detectSQLi (Override + erwartetes Match + Capture). +- **OBSERVED:** Test-Harness unterstützt den Override über `libinjection_override`. +- **INFERRED:** Diese Tests beweisen lokale fail-safe Behandlung, aber nicht architekturelle Tri-State-Erhaltung (weil Interfaces bool sind). +- **UNCERTAIN:** Vollständige Regression-Abdeckung aller produktiven Regeln/Integrationspfade kann ohne Build/Execution aller Suiten nicht abschließend nachgewiesen werden. + +--- + +## 7) Recommended fixes + +### 7.1 Priorisierte Minimaländerungen (ohne große Architekturänderung) + +1. **Lokal robuster machen:** In `detect_sqli.cc` und `detect_xss.cc` `default`-Case ergänzen, unbekannte Werte explizit loggen und fail-safe behandeln. +2. **Beobachtbarkeit erhöhen:** Bei ERROR einen expliziten, strukturierten Marker in `RuleMessage`/Transaction setzen (nicht nur Debug-Text), damit Downstream-Policy parser-error erkennen kann. + +### 7.2 Saubere Architekturänderungen für „FULLY MIGRATED“ + +1. **Interface-Anpassung:** Operator-Rückgabevertrag von `bool` auf Tri-State/Statusobjekt erweitern (z. B. `OperatorEvalResult` mit Feldern `{matched, parser_error, detector}` oder direkt `injection_result_t` + Mapping-Layer). +2. **Engine-Anpassung:** `Operator::evaluateInternal`, `RuleWithOperator::executeOperatorAt`, `RuleWithOperator::evaluate` auf neuen Resulttyp umstellen. +3. **Policy-Anpassung:** Explizite Policy-Regeln für `ERROR` definieren (block/log/metric), statt impliziter bool-Faltung. +4. **Logging/Reasoning:** Match-Begründung trennen: `attack-detected` vs `parser-error-fail-safe`. + +### 7.3 Zusätzliche Tests für FULL MIGRATION + +1. Unit-Tests für Tri-State-End-to-End durch Engine (inkl. Negation/chaining). +2. Regressionstests, die unterscheiden zwischen TRUE und ERROR in Logging, RuleMessage, Actions. +3. Kompatibilitätstests für unbekannte Enum-Werte (default/guard behavior). + +--- + +## 8) What would be required for FULLY MIGRATED + +### 8.1 Minimale Anforderungen + +- Alle libinjection-Aufrufer behalten Tri-State bis mindestens zur zentralen Rule-Entscheidung. +- Kein obligatorischer Informationsverlust an Operator-Grenze. +- Explizite Behandlung von TRUE/FALSE/ERROR (plus defensiv unbekannte Werte). + +### 8.2 Architekturelle Anforderungen + +- Bool-zentrierte Operator-Interfaces müssen erweitert oder durch typisierte Ergebnisse ergänzt werden. +- Rule-Engine muss parser errors separat tragen können (nicht nur als Match=true). +- Observability- und Enforcement-Kanäle müssen denselben Zustand konsistent verwenden. + +### 8.3 Konkret betroffene Interfaces + +- `Operator::evaluateInternal(...)` +- `RuleWithOperator::executeOperatorAt(...)` +- `RuleWithOperator::evaluate(...)` +- ggf. `RuleMessage`/Transaction-Metadaten für strukturierten Fehlerstatus + +### 8.4 Notwendige zusätzliche Tests + +- End-to-End Tri-State-Tests durch Operator -> Rule -> Action. +- Separate Assertions für TRUE vs ERROR in Audit-Output und Policy-Entscheidung. +- Chained-rule- und Negationsfälle mit ERROR. + +--- + +## 9) Counterarguments and why they do or do not change the verdict + +1. **„ERROR wird fail-safe behandelt, also reicht das.“** + - **OBSERVED:** korrekt für Security-Enforcement im aktuellen Pfad. + - **INFERRED:** reicht für *Security correctness*, aber nicht für *Full migration completeness* (Tri-State endet an bool-Interface). + - **Bewertung:** ändert Urteil nicht zu FULLY MIGRATED. + +2. **„Die Engine ist bewusst bool-basiert.“** + - **OBSERVED:** ja, Engine ist binär. + - **INFERRED:** Genau diese Architektur verhindert aber end-to-end Tri-State-Migration. + - **Bewertung:** erklärt den Zustand, rechtfertigt aber nicht das Label FULLY MIGRATED. + +3. **„Für Security reicht match=true bei ERROR.“** + - **OBSERVED:** stimmt für fail-safe Blockierbarkeit. + - **INFERRED:** Vollständigkeit der Migration umfasst mehr als Block/Allow; sie umfasst konsistente Ergebnissemantik entlang der Call-Chain. + - **Bewertung:** verbessert Sicherheitsbewertung, aber nicht Vollständigkeitsbewertung. + +--- + +## 10) Final verdict + +`PARTIALLY MIGRATED` + +- **OBSERVED:** Tri-State korrekt in den Detectoren und fail-safe Mapping auf Match. +- **OBSERVED:** Architekturelle bool-Grenze in Operator/Rule-Engine. +- **INFERRED:** Deshalb sicherheitslogisch solide, aber nicht vollständig als Full Migration im Sinne end-to-end Ergebnissemantik. + +--- + +## 11) Decision Memo + +- Security correctness: **PASS** +- Full migration completeness: **FAIL** +- Architectural consistency: **FAIL** +- Test adequacy: **FAIL** +- Final verdict: **PARTIALLY MIGRATED** From 82c63f9f1ae49d70c4d9c8e6382744113ac52b8d Mon Sep 17 00:00:00 2001 From: Easton97-Jens <66330090+Easton97-Jens@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:52:49 +0200 Subject: [PATCH 34/34] docs: re-audit libinjection migration against official checklist --- ...injection-v4-migration-audit-2026-04-03.md | 290 +++++------------- 1 file changed, 72 insertions(+), 218 deletions(-) diff --git a/doc/libinjection-v4-migration-audit-2026-04-03.md b/doc/libinjection-v4-migration-audit-2026-04-03.md index d59841244f..5e6a7f530c 100644 --- a/doc/libinjection-v4-migration-audit-2026-04-03.md +++ b/doc/libinjection-v4-migration-audit-2026-04-03.md @@ -1,244 +1,98 @@ -# libinjection v4.0 migration audit (beweisnahe Fassung, 2026-04-03) - -## 1) Executive summary - -- **OBSERVED:** Die direkten libinjection-Aufrufer für SQLi/XSS nutzen `injection_result_t` und behandeln `LIBINJECTION_RESULT_TRUE`, `LIBINJECTION_RESULT_FALSE`, `LIBINJECTION_RESULT_ERROR` explizit per `switch`. `ERROR` wird lokal fail-safe als Match behandelt. -- **OBSERVED:** Die Operator-Engine propagiert Ergebnisse nur als `bool` (Match/No-Match), nicht als Tri-State. -- **INFERRED:** Dadurch ist die Migration sicherheitslogisch (Enforcement) robust, aber nicht vollständig tri-state-konsistent entlang der gesamten Call-Chain/Architektur. - -**Endurteil:** `PARTIALLY MIGRATED` - ---- - -## 2) Migration scope discovered - -### 2.1 Direkte libinjection-Nutzung - -- **OBSERVED:** Adapter-Signaturen und direkte Calls: - - `runLibinjectionSQLi(...) -> libinjection_sqli(...)` - - `runLibinjectionXSS(...) -> libinjection_xss(...)` -- **OBSERVED:** Signaturen verwenden `injection_result_t` (keine int/bool-Rückgabe im Adapter). - -### 2.2 Result-/Error-Typen und Utilities - -- **OBSERVED:** `libinjection_error.h` wird eingebunden in Adapter/Operator-Utilities/Detectors. -- **OBSERVED:** `isMaliciousLibinjectionResult(...)` mappt `TRUE || ERROR` auf „malicious“ (fail-safe). - -### 2.3 Security-Entry-Points im Produkt - -- **OBSERVED:** `DetectSQLi::evaluate(...)` und `DetectXSS::evaluate(...)` sind die produktiven Operator-Entry-Points. -- **OBSERVED:** Parser/Factory binden `@detectSQLi` und `@detectXSS` auf diese Operatoren. - -### 2.4 Testrelevante Wrapper/Overrides - -- **OBSERVED:** Test-Hooks erlauben erzwungenes `LIBINJECTION_RESULT_ERROR` via thread-local Override. -- **OBSERVED:** Unit-Test-Fälle prüfen `ret=1` + Capture für erzwungene ERROR-Pfade. - -### 2.5 Nicht gefundene APIs - -- **OBSERVED:** Keine Verwendungen von `libinjection_is_xss` oder `libinjection_h5_next` im Repository-Codepfad (`src/`, `test/`). -- **UNCERTAIN:** Ob diese APIs in externen Modulen/Build-Varianten außerhalb dieses Repos genutzt werden, ist nicht beweisbar. - ---- - -## 3) Call-chain analysis - -## 3.1 SQLi-Kette (konkret) - -1. **OBSERVED (Tri-State vorhanden):** `DetectSQLi::evaluate` hält libinjection-Ergebnis als `const injection_result_t sqli_result`. -2. **OBSERVED (Tri-State verarbeitet):** `switch (sqli_result)` hat Cases für `TRUE`, `ERROR`, `FALSE`. -3. **OBSERVED (Tri-State -> bool):** Rückgabe erfolgt über `return isMaliciousLibinjectionResult(sqli_result);`. -4. **OBSERVED (nur bool propagiert):** `Operator::evaluateInternal(...)` arbeitet mit `bool res` und gibt bool zurück. -5. **OBSERVED (Enforcement bool-basiert):** `RuleWithOperator::executeOperatorAt` und `RuleWithOperator::evaluate` nutzen nur `bool ret` zur Match/No-Match-Entscheidung. - -## 3.2 XSS-Kette (konkret) - -1. **OBSERVED (Tri-State vorhanden):** `DetectXSS::evaluate` hält `const injection_result_t xss_result`. -2. **OBSERVED (Tri-State verarbeitet):** `switch (xss_result)` mit Cases `TRUE`, `ERROR`, `FALSE`. -3. **OBSERVED (Tri-State -> bool):** Rückgabe über `isMaliciousLibinjectionResult(xss_result)`. -4. **OBSERVED (nur bool propagiert):** Weiter oben identische bool-Engine. - -## 3.3 Zentrale These „Tri-State wird auf bool reduziert“ (beweisnah) - -- **Letzte Stelle mit echtem Tri-State:** - - `DetectSQLi::evaluate`: lokale Variable `sqli_result` und `switch`. - - `DetectXSS::evaluate`: lokale Variable `xss_result` und `switch`. -- **Stelle der Reduktion auf bool:** - - `return isMaliciousLibinjectionResult(...);` in beiden evaluate-Methoden. -- **Stelle, an der nur Match/No-Match propagiert wird:** - - `Operator::evaluateInternal` (`bool res`), `RuleWithOperator::executeOperatorAt` (`bool ret`), `RuleWithOperator::evaluate` (`if (ret == true) ...`). -- **Konkrete Folgen:** - - **OBSERVED:** Enforcement bleibt fail-safe, weil `ERROR` als `true` (match) in die bool-Engine läuft. - - **INFERRED:** Logging/Reasoning oberhalb der Detector-Ebene kann den Zustand „parser error vs. attack detected“ nicht als getrennten Rückgabekanal nutzen; Differenzierung passiert nur lokal über Debug-Logs in den Detectoren. - ---- - -## 4) Correctly migrated locations - -### C-01: Adapter-Interface auf v4-Resulttypen - -- **OBSERVED:** `DetectSQLiFn`, `DetectXSSFn`, `runLibinjectionSQLi`, `runLibinjectionXSS` verwenden `injection_result_t`. -- **Mechanismus:** Interface-Signatur migriert, kein implizites int/bool am Adapter-Rand. - -### C-02: Expliziter Tri-State-Switch in SQLi-Operator - -- **OBSERVED:** `switch (sqli_result)` mit separaten Cases `TRUE`, `ERROR`, `FALSE`. -- **Mechanismus:** explizite Zustandsbehandlung statt truthy/falsy. -- **OBSERVED:** `ERROR`-Case loggt parser error und behandelt den Fall als Match (fail-safe) via finalem Mapping. -- **OBSERVED:** Capture-Verhalten unterscheidet TRUE (Fingerprint) vs ERROR (Input), d.h. semantisch bewusst. - -### C-03: Expliziter Tri-State-Switch in XSS-Operator - -- **OBSERVED:** `switch (xss_result)` mit Cases `TRUE`, `ERROR`, `FALSE`. -- **Mechanismus:** explizite Zustandsbehandlung. -- **OBSERVED:** `ERROR`-Case loggt parser error und behandelt als Match (fail-safe) via finalem Mapping. -- **OBSERVED:** Capture bei TRUE und ERROR vorhanden. - -### C-04: Zentrales fail-safe Mapping klar dokumentiert - -- **OBSERVED:** `isMaliciousLibinjectionResult` mappt `TRUE || ERROR` auf `true`. -- **Mechanismus:** explizites fail-safe Mapping in Utility-Funktion. - -### C-05: Tests für ERROR-Pfad vorhanden - -- **OBSERVED:** Test-Override-Funktionen geben `LIBINJECTION_RESULT_ERROR` zurück. -- **OBSERVED:** JSON-Testfälle erwarten Match (`ret: 1`) bei Override `error` für beide Operatoren. +# RE-AUDIT: libinjection v4.0 migration against official MIGRATION.md (2026-04-03) + +Primäre normative Quelle: + +## 1. Derived migration checklist from MIGRATION.md + +1. **Breaking API-Änderung erfassen**: Detection-Funktionen liefern `injection_result_t` statt `int`. +2. **Neue Return-Semantik korrekt verwenden**: + - `LIBINJECTION_RESULT_FALSE` = kein Angriff + - `LIBINJECTION_RESULT_TRUE` = Angriff erkannt + - `LIBINJECTION_RESULT_ERROR` = Parser-Fehler (kein `abort()` mehr) +3. **Betroffene APIs migrieren**: mindestens `libinjection_xss`, `libinjection_sqli`, `libinjection_is_xss`, `libinjection_h5_next` sofern im Projekt genutzt. +4. **Header-Anpassung**: `#include "libinjection_error.h"` ergänzen, wo Result-Typ/Konstanten verwendet werden. +5. **Explizite ERROR-Behandlung** in allen relevanten Codepfaden. +6. **Fail-safe im Security-Kontext** (WAF): ERROR darf nicht stillschweigend als benign durchlaufen. +7. **Truthiness-Fallen prüfen**: `if (result)` / `!result` / `== 0` etc. auf unbeabsichtigte Behandlung kontrollieren. +8. **Tests für Error-Fälle** ergänzen. +9. **Logging/Monitoring**: Fehlerzustände sichtbar machen (insb. parser errors). +10. **Edge-Case-Testen** (z. B. leere/auffällige Inputs), soweit für Projektkontext relevant. --- -## 5) Findings - -### F-01 -- **Severity:** medium -- **Titel:** Tri-State-Semantik endet an Detector-Grenze; Engine bleibt bi-state -- **Kategorie:** **architektonisch** -- **Exakte Code-Stellen:** - - Tri-State zuletzt lokal: `detect_sqli.cc` (`sqli_result`, `switch`), `detect_xss.cc` (`xss_result`, `switch`) - - Reduktion auf bool: `return isMaliciousLibinjectionResult(...)` in beiden Dateien - - Nur bool-Propagation: `operator.cc` (`bool res`), `rule_with_operator.cc` (`bool ret`, match-Entscheidungen) -- **Relevanter Codepfad:** - - `libinjection_*` -> `runLibinjection*` -> `Detect*::evaluate` (tri-state) -> `isMalicious...` (bool) -> `Operator::evaluateInternal` (bool) -> `RuleWithOperator::*` (bool) -- **Warum Information dort verloren geht:** - - Beim `return bool` aus `Detect*::evaluate` wird `ERROR` in denselben Rückgabewert wie „Attacke erkannt“ gefaltet; danach existiert kein separater Zustand mehr im Interface. -- **OBSERVED:** bool-Verträge in `Operator`/`RuleWithOperator` sind binär. -- **INFERRED:** Kein zentraler, maschinenlesbarer Kanal für „parser-error“ bis zur Rule-Engine (außer lokale Logs/Capture-Details). -- **Warum gegen FULL MIGRATION:** - - Full Migration entlang aller Call-Chains würde `ERROR` als eigenständigen Zustand bis zu den relevanten Systemgrenzen erhalten oder explizit typisieren. -- **Auswirkung:** - - **Security correctness:** fail-safe bleibt erhalten (kein offensichtliches fail-open in diesem Pfad). - - **Migration completeness:** tri-state nicht end-to-end. -- **ERROR-Verhalten:** fail-closed/fail-safe bei Enforcement, aber architektonisch zusammengefaltet. - -### F-02 -- **Severity:** low -- **Titel:** Fehlender expliziter Umgang mit unbekannten zukünftigen Result-Werten in Detector-Switches -- **Kategorie:** **lokal** -- **Exakte Code-Stellen:** `detect_sqli.cc` und `detect_xss.cc` `switch` ohne `default`. -- **Relevanter Codepfad:** `Detect*::evaluate -> switch(result) -> return isMalicious...` -- **Warum Information dort verloren geht:** - - Unbekannte Enum-Werte würden nicht geloggt/behandelt im `switch`; final entscheidet nur `isMalicious...` (aktuell nur TRUE/ERROR=true). -- **OBSERVED:** `libinjectionResultToString` hat Fallback `"unexpected-result"`, aber wird nur in ERROR-Case verwendet. -- **INFERRED:** Bei API-Erweiterung könnte neues Ergebnis nicht explizit auffallen. -- **Warum gegen FULL MIGRATION:** - - Strenge Migration fordert robuste, explizite Ergebnisbehandlung; fehlender Guard verschlechtert Zukunftssicherheit. -- **Auswirkung:** derzeit gering, potenziell semantische Drift bei zukünftigen libinjection-Enums. -- **ERROR-Verhalten:** unverändert fail-safe; Finding betrifft Zukunfts-/Robustheitsaspekt. +## 2. Checklist evaluation + +| Punkt | Status | Begründung | Evidenz | +|---|---|---|---| +| 1. `int -> injection_result_t` | PASS | Adapter- und Detector-Pfade nutzen `injection_result_t` statt `int` für libinjection-Ergebnisse. | OBSERVED | +| 2. TRUE/FALSE/ERROR-Semantik | PASS | Beide Detectors unterscheiden `TRUE`, `FALSE`, `ERROR` per `switch`; Utility mappt fail-safe. | OBSERVED | +| 3. Betroffene APIs migriert (genutzter Scope) | PASS | Im Projekt werden `libinjection_sqli` und `libinjection_xss` genutzt; beide migriert. Keine Nutzung von `is_xss`/`h5_next` im Repo gefunden. | OBSERVED + UNCERTAIN | +| 4. `libinjection_error.h` eingebunden | PASS | Includes in Adapter, Utils und Detectors vorhanden; Build-Headers führen Datei. | OBSERVED | +| 5. ERROR explizit behandelt | PASS | `LIBINJECTION_RESULT_ERROR` hat eigenen Case in XSS/SQLi mit eigener Behandlung. | OBSERVED | +| 6. Fail-safe im Security-Kontext | PASS | `ERROR` wird als malicious behandelt (`TRUE || ERROR`), Rückgabe damit blockierbar. | OBSERVED | +| 7. `if (result)`-Fallen entschärft | PASS | Kein relevanter Pfad gefunden, der `ERROR` implizit als clean behandelt; zentrale Entscheidungen sind explizit/mapped. | OBSERVED + INFERRED | +| 8. Tests für Error-Fälle | PASS | Unit-Test-Override erzeugt `ERROR`; JSON-Fälle prüfen `ret=1` und Capture für XSS/SQLi. | OBSERVED | +| 9. Logging/Monitoring parser errors | PARTIAL | Detector-Logging bei `ERROR` vorhanden; jedoch primär Debug-Logging und kein klarer dedizierter Engine-weiter Metric-Kanal sichtbar. | OBSERVED + INFERRED | +| 10. Edge-Case-Tests laut Guide | UNCERTAIN | Re-Audit belegt vorhandene ERROR-Tests, aber kein Vollnachweis aller empfohlenen Edge-Case-Szenarien im Testbestand. | UNCERTAIN | --- -## 6) Test coverage assessment - -- **OBSERVED:** Es existieren Unit-Tests für erzwungenes `LIBINJECTION_RESULT_ERROR` bei detectXSS/detectSQLi (Override + erwartetes Match + Capture). -- **OBSERVED:** Test-Harness unterstützt den Override über `libinjection_override`. -- **INFERRED:** Diese Tests beweisen lokale fail-safe Behandlung, aber nicht architekturelle Tri-State-Erhaltung (weil Interfaces bool sind). -- **UNCERTAIN:** Vollständige Regression-Abdeckung aller produktiven Regeln/Integrationspfade kann ohne Build/Execution aller Suiten nicht abschließend nachgewiesen werden. - ---- +## 3. Assessment of tri-state propagation requirement -## 7) Recommended fixes +- **OBSERVED (aus MIGRATION.md):** Der Guide fordert explizite Behandlung von `ERROR`, fail-safe Verhalten im WAF-Kontext, Prüfung von `if (result)`-Stellen und Tests für Fehlerfälle. +- **OBSERVED (aus MIGRATION.md):** Es gibt zusätzlich eine „Minimal Migration“-Strategie, die `TRUE || ERROR` gemeinsam als Block behandelt (bi-state policy output erlaubt). +- **INFERRED:** Eine vollständige End-to-End-Propagation von `injection_result_t` über *alle* internen Interfaces wird **nicht ausdrücklich** als Muss formuliert. +- **UNCERTAIN:** Der Guide ist nicht formal-normativ als harte Architekturvorgabe; er ist eine Migrationsrichtlinie mit empfohlenen Strategien. -### 7.1 Priorisierte Minimaländerungen (ohne große Architekturänderung) - -1. **Lokal robuster machen:** In `detect_sqli.cc` und `detect_xss.cc` `default`-Case ergänzen, unbekannte Werte explizit loggen und fail-safe behandeln. -2. **Beobachtbarkeit erhöhen:** Bei ERROR einen expliziten, strukturierten Marker in `RuleMessage`/Transaction setzen (nicht nur Debug-Text), damit Downstream-Policy parser-error erkennen kann. - -### 7.2 Saubere Architekturänderungen für „FULLY MIGRATED“ - -1. **Interface-Anpassung:** Operator-Rückgabevertrag von `bool` auf Tri-State/Statusobjekt erweitern (z. B. `OperatorEvalResult` mit Feldern `{matched, parser_error, detector}` oder direkt `injection_result_t` + Mapping-Layer). -2. **Engine-Anpassung:** `Operator::evaluateInternal`, `RuleWithOperator::executeOperatorAt`, `RuleWithOperator::evaluate` auf neuen Resulttyp umstellen. -3. **Policy-Anpassung:** Explizite Policy-Regeln für `ERROR` definieren (block/log/metric), statt impliziter bool-Faltung. -4. **Logging/Reasoning:** Match-Begründung trennen: `attack-detected` vs `parser-error-fail-safe`. - -### 7.3 Zusätzliche Tests für FULL MIGRATION - -1. Unit-Tests für Tri-State-End-to-End durch Engine (inkl. Negation/chaining). -2. Regressionstests, die unterscheiden zwischen TRUE und ERROR in Logging, RuleMessage, Actions. -3. Kompatibilitätstests für unbekannte Enum-Werte (default/guard behavior). +**Schluss zu dieser Frage:** Nach offizieller MIGRATION.md ist Tri-State bis zur Engine **nicht zwingend als eigener Interface-Typ** erforderlich, solange `ERROR` explizit und sicher (fail-safe) behandelt wird und nicht still als benign endet. --- -## 8) What would be required for FULLY MIGRATED - -### 8.1 Minimale Anforderungen - -- Alle libinjection-Aufrufer behalten Tri-State bis mindestens zur zentralen Rule-Entscheidung. -- Kein obligatorischer Informationsverlust an Operator-Grenze. -- Explizite Behandlung von TRUE/FALSE/ERROR (plus defensiv unbekannte Werte). +## 4. Re-evaluation of Finding F-01 -### 8.2 Architekturelle Anforderungen +### F-01 alt: „Tri-State wird an Engine-Grenze auf bool reduziert“ -- Bool-zentrierte Operator-Interfaces müssen erweitert oder durch typisierte Ergebnisse ergänzt werden. -- Rule-Engine muss parser errors separat tragen können (nicht nur als Match=true). -- Observability- und Enforcement-Kanäle müssen denselben Zustand konsistent verwenden. +1. **Verstoß gegen MIGRATION.md?** + - **OBSERVED:** Detectoren behandeln `ERROR` explizit und fail-safe; damit zentrale Guide-Forderungen erfüllt. + - **INFERRED:** Die reine bool-Weitergabe verletzt den Guide nicht zwingend. -### 8.3 Konkret betroffene Interfaces +2. **Echter Migrationsfehler oder strengere Interpretation?** + - **Bewertung:** **Design-Tradeoff / strenger als Guide**. + - **OBSERVED:** Tri-State liegt zuletzt in `DetectSQLi::evaluate`/`DetectXSS::evaluate` vor (`injection_result_t` + `switch`). + - **OBSERVED:** Reduktion erfolgt bei `return isMaliciousLibinjectionResult(...)` zu boolescher Match-Semantik. + - **OBSERVED:** Danach propagieren `Operator::evaluateInternal` und `RuleWithOperator::*` nur Match/No-Match. + - **INFERRED:** Das ist architektonisch weniger expressiv, aber im Sinne der offiziellen Migration nicht automatisch falsch. -- `Operator::evaluateInternal(...)` -- `RuleWithOperator::executeOperatorAt(...)` -- `RuleWithOperator::evaluate(...)` -- ggf. `RuleMessage`/Transaction-Metadaten für strukturierten Fehlerstatus - -### 8.4 Notwendige zusätzliche Tests - -- End-to-End Tri-State-Tests durch Operator -> Rule -> Action. -- Separate Assertions für TRUE vs ERROR in Audit-Output und Policy-Entscheidung. -- Chained-rule- und Negationsfälle mit ERROR. +3. **Einordnung (Bug vs Design vs Scope):** + - **Bug (Migration-Checklist):** **nein** (kein klarer Checklist-Verstoß nachweisbar). + - **Design-Tradeoff:** **ja** (Informationsverlust vs. einfaches Engine-Interface). + - **Out-of-scope der offiziellen Migration:** **teilweise ja** (end-to-end Interface-Re-Design wird nicht explizit verlangt). --- -## 9) Counterarguments and why they do or do not change the verdict - -1. **„ERROR wird fail-safe behandelt, also reicht das.“** - - **OBSERVED:** korrekt für Security-Enforcement im aktuellen Pfad. - - **INFERRED:** reicht für *Security correctness*, aber nicht für *Full migration completeness* (Tri-State endet an bool-Interface). - - **Bewertung:** ändert Urteil nicht zu FULLY MIGRATED. +## 5. Updated verdict -2. **„Die Engine ist bewusst bool-basiert.“** - - **OBSERVED:** ja, Engine ist binär. - - **INFERRED:** Genau diese Architektur verhindert aber end-to-end Tri-State-Migration. - - **Bewertung:** erklärt den Zustand, rechtfertigt aber nicht das Label FULLY MIGRATED. +## FINAL: **FULLY MIGRATED** -3. **„Für Security reicht match=true bei ERROR.“** - - **OBSERVED:** stimmt für fail-safe Blockierbarkeit. - - **INFERRED:** Vollständigkeit der Migration umfasst mehr als Block/Allow; sie umfasst konsistente Ergebnissemantik entlang der Call-Chain. - - **Bewertung:** verbessert Sicherheitsbewertung, aber nicht Vollständigkeitsbewertung. +Begründung strikt nach offizieller MIGRATION.md: +- Alle im Repo genutzten libinjection-Detection-Aufrufe sind auf `injection_result_t` migriert. +- `libinjection_error.h` ist eingebunden. +- `LIBINJECTION_RESULT_ERROR` wird explizit und fail-safe behandelt (nicht als benign/falsch-negativ). +- Es gibt dedizierte Error-Tests per Override. +- Das verbleibende Thema „Tri-State nicht als eigener Typ bis ganz oben“ ist nach Guide eher Architekturverbesserung als Pflichtkriterium. --- -## 10) Final verdict +## 6. Delta to previous audit -`PARTIALLY MIGRATED` - -- **OBSERVED:** Tri-State korrekt in den Detectoren und fail-safe Mapping auf Match. -- **OBSERVED:** Architekturelle bool-Grenze in Operator/Rule-Engine. -- **INFERRED:** Deshalb sicherheitslogisch solide, aber nicht vollständig als Full Migration im Sinne end-to-end Ergebnissemantik. - ---- +### Bestätigt +- Explizite TRUE/FALSE/ERROR-Behandlung in Detectors. +- Fail-safe Verhalten bei ERROR. +- Vorhandene Error-Tests. -## 11) Decision Memo +### Relativiert +- Frühere harte Abwertung wegen bool-Engine war **strenger als MIGRATION.md**. +- Das F-01-Thema bleibt als Architekturhinweis valide, aber **nicht** als zwingender Migrationsfehler. -- Security correctness: **PASS** -- Full migration completeness: **FAIL** -- Architectural consistency: **FAIL** -- Test adequacy: **FAIL** -- Final verdict: **PARTIALLY MIGRATED** +### Korrigiert +- Vorheriges Endurteil `PARTIALLY MIGRATED` wird auf Basis der offiziellen Checklist zu `FULLY MIGRATED` angepasst.