Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
58fc3da
Update libinjection to v4.0.0
Mar 29, 2026
e1a527e
Make detect_sqli and detect_xss compatible with libinjection v4
Mar 29, 2026
d2cef41
Update regression tests for libinjection v4 compatibility
Mar 29, 2026
9e66822
syntax adjustment
Mar 30, 2026
633f2eb
Add capture/non-capture regression coverage for detectSQLi/XSS
Easton97-Jens Mar 30, 2026
b264dbf
Merge pull request #27 from Easton97-Jens/codex/add-regression-tests-…
Easton97-Jens Mar 30, 2026
7cd1d67
Fix Windows test include path and case-insensitive override matching
Easton97-Jens Mar 31, 2026
2aed15b
Merge pull request #31 from Easton97-Jens/codex/add-regression-tests-…
Easton97-Jens Mar 31, 2026
0af7e13
Update libinjection_adapter.cc
Easton97-Jens Apr 1, 2026
d8c7395
Merge branch 'owasp-modsecurity:v3/master' into v3/master-libinjectio…
Easton97-Jens Apr 1, 2026
d6648d1
Add libinjection_error.h to Makefile.am
Easton97-Jens Apr 1, 2026
19ea6d0
Isolate transaction state in multithreaded unit tests
Easton97-Jens Apr 1, 2026
e152a09
Merge pull request #32 from Easton97-Jens/codex/review-multithreaded-…
Easton97-Jens Apr 1, 2026
719d172
Merge branch 'v3/master' into v3/master-libinjection-v4.0-final
Easton97-Jens Apr 1, 2026
468f681
Update libinjection_adapter.cc
Easton97-Jens Apr 1, 2026
d19f58b
Update libinjection_adapter.h
Easton97-Jens Apr 1, 2026
91fbf35
Hide testing override functions from symbol table
Easton97-Jens Apr 3, 2026
e10e9e0
Log input in hex format for SQLi detection
Easton97-Jens Apr 3, 2026
29a461b
Add logging for input in XSS detection
Easton97-Jens Apr 3, 2026
7c104e4
Update multithreaded unit test implementation
Easton97-Jens Apr 3, 2026
0cf4f3c
Update libinjection_adapter.h
Easton97-Jens Apr 3, 2026
3e98c81
Guard log-only detect operator variables under NO_LOGS
Easton97-Jens Apr 3, 2026
e500702
Merge pull request #42 from Easton97-Jens/codex/fix-cppcheck-unreadva…
Easton97-Jens Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/win32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()

Expand Down
1 change: 1 addition & 0 deletions others/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
2 changes: 1 addition & 1 deletion others/libinjection
Submodule libinjection updated 155 files
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
95 changes: 67 additions & 28 deletions src/operators/detect_sqli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,84 @@

#include <string>
#include <list>
#include <array>

#include "src/operators/operator.h"
#include "libinjection/src/libinjection.h"

namespace modsecurity {
namespace operators {
#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) {
char fingerprint[8];
int issqli;
#ifndef NO_LOGS
const std::string loggable_input =
utils::string::limitTo(80, utils::string::toHexIfNeeded(input));
#endif

std::array<char, 8> fingerprint{};

issqli = libinjection_sqli(input.c_str(), input.length(), fingerprint);
const injection_result_t sqli_result =
runLibinjectionSQLi(input.c_str(), input.length(), fingerprint.data());

if (!t) {
goto tisempty;
if (t == nullptr) {
return isMaliciousLibinjectionResult(sqli_result);
}

if (issqli) {
t->m_matched.push_back(fingerprint);
ms_dbg_a(t, 4, "detected SQLi using libinjection with " \
"fingerprint '" + std::string(fingerprint) + "' at: '" +
input + "'");
if (rule && rule->hasCaptureAction()) {
t->m_collections.m_tx_collection->storeOrUpdateFirst(
"0", std::string(fingerprint));
ms_dbg_a(t, 7, "Added DetectSQLi match TX.0: " + \
std::string(fingerprint));
}
} else {
ms_dbg_a(t, 9, "detected SQLi: not able to find an " \
"inject on '" + input + "'");
switch (sqli_result) {
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(
"0", std::string(fingerprint.data()));

ms_dbg_a(t, 7,
std::string("Added DetectSQLi match TX.0: ")
+ fingerprint.data());
}
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(
"0", input);

ms_dbg_a(t, 7,
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:
#ifndef NO_LOGS
ms_dbg_a(t, 9,
std::string("libinjection was not able to find any SQLi in: ")
+ loggable_input);
#endif
break;
}

tisempty:
return issqli != 0;
return isMaliciousLibinjectionResult(sqli_result);
}


} // namespace operators
} // namespace modsecurity
} // namespace modsecurity::operators
72 changes: 48 additions & 24 deletions src/operators/detect_xss.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,60 @@
#include <string>

#include "src/operators/operator.h"
#include "libinjection/src/libinjection.h"


namespace modsecurity {
namespace operators {
#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) {
int is_xss;

is_xss = libinjection_xss(input.c_str(), input.length());

if (t) {
if (is_xss) {
ms_dbg_a(t, 5, "detected XSS using libinjection.");
if (rule && rule->hasCaptureAction()) {
t->m_collections.m_tx_collection->storeOrUpdateFirst(
"0", std::string(input));
ms_dbg_a(t, 7, "Added DetectXSS match TX.0: " + \
std::string(input));
#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());

if (t == nullptr) {
return isMaliciousLibinjectionResult(xss_result);
}

switch (xss_result) {
case LIBINJECTION_RESULT_TRUE:
ms_dbg_a(t, 5, std::string("detected XSS using libinjection."));
if (rule != nullptr && rule->hasCaptureAction()) {
t->m_collections.m_tx_collection->storeOrUpdateFirst("0", input);
ms_dbg_a(t, 7, std::string("Added DetectXSS match TX.0: ") + input);

Check warning on line 47 in src/operators/detect_xss.cc

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Modify the macro definition so that it needs to be followed by a semicolon, or remove this empty statement.

See more on https://sonarcloud.io/project/issues?id=owasp-modsecurity_ModSecurity&issues=AZ1TRzQnLkg-kfoMnyds&open=AZ1TRzQnLkg-kfoMnyds&pullRequest=3528
}
} else {
ms_dbg_a(t, 9, "libinjection was not able to " \
"find any XSS in: " + input);
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);
}
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;
}
return is_xss != 0;
}

return isMaliciousLibinjectionResult(xss_result);
}

} // namespace operators
} // namespace modsecurity
} // namespace modsecurity::operators
68 changes: 68 additions & 0 deletions src/operators/libinjection_adapter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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"

#include "libinjection/src/libinjection.h"

namespace modsecurity::operators {
namespace {

// Per-thread overrides avoid cross-thread interference during mtstress tests.
// 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,
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);
}

// 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;
}

// 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;
}

void clearLibinjectionOverridesForTesting() {
g_sqli_override = nullptr;
g_xss_override = nullptr;
}

} // namespace modsecurity::operators
38 changes: 38 additions & 0 deletions src/operators/libinjection_adapter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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_
#define SRC_OPERATORS_LIBINJECTION_ADAPTER_H_

#include <cstddef>

#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_
48 changes: 48 additions & 0 deletions src/operators/libinjection_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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_UTILS_H_
#define SRC_OPERATORS_LIBINJECTION_UTILS_H_

#include "libinjection/src/libinjection_error.h"

namespace modsecurity::operators {

/*
* libinjection parser errors are handled in fail-safe mode as suspicious
* results, so callers can block on both confirmed detections and parser
* failures.
*/
static inline bool isMaliciousLibinjectionResult(injection_result_t result) {
return result == LIBINJECTION_RESULT_TRUE
|| result == LIBINJECTION_RESULT_ERROR;
}

static inline const char *libinjectionResultToString(injection_result_t result) {
switch (result) {
case LIBINJECTION_RESULT_TRUE:
return "attack-detected";
case LIBINJECTION_RESULT_FALSE:
return "no-attack";
case LIBINJECTION_RESULT_ERROR:
return "parser-error";
}

return "unexpected-result";
}

} // namespace modsecurity::operators

#endif // SRC_OPERATORS_LIBINJECTION_UTILS_H_
1 change: 1 addition & 0 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
Loading