diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3350743b1..34f345d69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,3 +72,55 @@ jobs: run: make -j `sysctl -n hw.logicalcpu` - name: check run: make check + + build-windows: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-2022] + platform: [x86_64] + configuration: [Release] + configure: + - {label: "full", opt: "" } + - {label: "wo lmdb", opt: "-DWITHOUT_LMDB=ON" } + - {label: "wo lua", opt: "-DWITHOUT_LUA=ON" } + - {label: "wo maxmind", opt: "-DWITHOUT_MAXMIND=ON" } + - {label: "wo curl", opt: "-DWITHOUT_CURL=ON" } + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Install Conan + run: | + pip3 install conan --upgrade + conan profile detect + - uses: ammaraskar/msvc-problem-matcher@master + - name: Build ${{ matrix.configuration }} ${{ matrix.platform }} ${{ matrix.configure.label }} + shell: cmd + run: vcbuild.bat ${{ matrix.configuration }} ${{ matrix.platform }} NO_ASAN "${{ matrix.configure.opt }}" + - name: Set up test environment + working-directory: build\win32\build\${{ matrix.configuration }} + env: + BASE_DIR: ..\..\..\.. + shell: cmd + run: | + copy unit_tests.exe %BASE_DIR%\test + copy regression_tests.exe %BASE_DIR%\test + copy libModSecurity.dll %BASE_DIR%\test + copy %BASE_DIR%\unicode.mapping %BASE_DIR%\test + md \tmp + md \bin + copy "C:\Program Files\Git\usr\bin\echo.exe" \bin + copy "C:\Program Files\Git\usr\bin\echo.exe" \bin\echo + - name: Disable tests that don't work on Windows + working-directory: test\test-cases\regression + shell: cmd + run: | + jq "map(if .title == \"Test match variable (1/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json + jq "map(if .title == \"Test match variable (2/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json + jq "map(if .title == \"Test match variable (3/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json + jq "map(if .title == \"Variable offset - FILES_NAMES\" then .enabled = 0 else . end)" offset-variable.json > tmp.json && move /Y tmp.json offset-variable.json + - name: Run tests + working-directory: build\win32\build + run: | + ctest -C ${{ matrix.configuration }} --output-on-failure diff --git a/Makefile.am b/Makefile.am index 501fcfdf4..5983c7c47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -89,263 +89,8 @@ LOG_DRIVER = env $(SHELL) $(top_srcdir)/test/custom-test-driver AM_TESTS_ENVIRONMENT=AUTOMAKE_TESTS=true; export AUTOMAKE_TESTS; LOG_COMPILER=test/test-suite.sh -# for i in `find test/test-cases -iname *.json`; do echo TESTS+=$i; done TESTS= -TESTS+=test/test-cases/regression/action-allow.json -TESTS+=test/test-cases/regression/action-block.json -TESTS+=test/test-cases/regression/action-ctl_request_body_access.json -TESTS+=test/test-cases/regression/action-ctl_request_body_processor.json -TESTS+=test/test-cases/regression/action-ctl_request_body_processor_urlencoded.json -TESTS+=test/test-cases/regression/action-ctl_rule_engine.json -TESTS+=test/test-cases/regression/action-ctl_audit_engine.json -TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_id.json -TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_tag.json -TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_id.json -TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json -TESTS+=test/test-cases/regression/action-disruptive.json -TESTS+=test/test-cases/regression/action-exec.json -TESTS+=test/test-cases/regression/action-expirevar.json -TESTS+=test/test-cases/regression/action-id.json -TESTS+=test/test-cases/regression/action-initcol.json -TESTS+=test/test-cases/regression/action-msg.json -TESTS+=test/test-cases/regression/action-setenv.json -TESTS+=test/test-cases/regression/action-setrsc.json -TESTS+=test/test-cases/regression/action-setsid.json -TESTS+=test/test-cases/regression/action-setuid.json -TESTS+=test/test-cases/regression/actions.json -TESTS+=test/test-cases/regression/action-skip.json -TESTS+=test/test-cases/regression/action-tag.json -TESTS+=test/test-cases/regression/action-tnf-base64.json -TESTS+=test/test-cases/regression/action-xmlns.json -TESTS+=test/test-cases/regression/auditlog.json -TESTS+=test/test-cases/regression/collection-case-insensitive.json -TESTS+=test/test-cases/regression/collection-lua.json -TESTS+=test/test-cases/regression/collection-regular_expression_selection.json -TESTS+=test/test-cases/regression/collection-resource.json -TESTS+=test/test-cases/regression/collection-tx.json -TESTS+=test/test-cases/regression/collection-tx-with-macro.json -TESTS+=test/test-cases/regression/config-body_limits.json -TESTS+=test/test-cases/regression/config-calling_phases_by_name.json -TESTS+=test/test-cases/regression/config-include-bad.json -TESTS+=test/test-cases/regression/config-include.json -TESTS+=test/test-cases/regression/config-remove_by_id.json -TESTS+=test/test-cases/regression/config-remove_by_msg.json -TESTS+=test/test-cases/regression/config-remove_by_tag.json -TESTS+=test/test-cases/regression/config-response_type.json -TESTS+=test/test-cases/regression/config-secdefaultaction.json -TESTS+=test/test-cases/regression/config-secremoterules.json -TESTS+=test/test-cases/regression/config-update-action-by-id.json -TESTS+=test/test-cases/regression/config-update-target-by-id.json -TESTS+=test/test-cases/regression/config-update-target-by-msg.json -TESTS+=test/test-cases/regression/config-update-target-by-tag.json -TESTS+=test/test-cases/regression/config-xml_external_entity.json -TESTS+=test/test-cases/regression/debug_log.json -TESTS+=test/test-cases/regression/directive-sec_rule_script.json -TESTS+=test/test-cases/regression/issue-1152.json -TESTS+=test/test-cases/regression/issue-1528.json -TESTS+=test/test-cases/regression/issue-1565.json -TESTS+=test/test-cases/regression/issue-1576.json -TESTS+=test/test-cases/regression/issue-1591.json -TESTS+=test/test-cases/regression/issue-1725.json -TESTS+=test/test-cases/regression/issue-1743.json -TESTS+=test/test-cases/regression/issue-1785.json -TESTS+=test/test-cases/regression/issue-1812.json -TESTS+=test/test-cases/regression/issue-1831.json -TESTS+=test/test-cases/regression/issue-1844.json -TESTS+=test/test-cases/regression/issue-1850.json -TESTS+=test/test-cases/regression/issue-1941.json -TESTS+=test/test-cases/regression/issue-1943.json -TESTS+=test/test-cases/regression/issue-1956.json -TESTS+=test/test-cases/regression/issue-1960.json -TESTS+=test/test-cases/regression/issue-2099.json -TESTS+=test/test-cases/regression/issue-2000.json -TESTS+=test/test-cases/regression/issue-2111.json -TESTS+=test/test-cases/regression/issue-2196.json -TESTS+=test/test-cases/regression/issue-2423-msg-in-chain.json -TESTS+=test/test-cases/regression/issue-2427.json -TESTS+=test/test-cases/regression/issue-2296.json -TESTS+=test/test-cases/regression/issue-394.json -TESTS+=test/test-cases/regression/issue-849.json -TESTS+=test/test-cases/regression/issue-960.json -TESTS+=test/test-cases/regression/misc.json -TESTS+=test/test-cases/regression/misc-variable-under-quotes.json -TESTS+=test/test-cases/regression/offset-variable.json -TESTS+=test/test-cases/regression/operator-detectsqli.json -TESTS+=test/test-cases/regression/operator-detectxss.json -TESTS+=test/test-cases/regression/operator-fuzzyhash.json -TESTS+=test/test-cases/regression/operator-inpectFile.json -TESTS+=test/test-cases/regression/operator-ipMatchFromFile.json -TESTS+=test/test-cases/regression/operator-pm.json -TESTS+=test/test-cases/regression/operator-rx.json -TESTS+=test/test-cases/regression/operator-rxGlobal.json -TESTS+=test/test-cases/regression/operator-UnconditionalMatch.json -TESTS+=test/test-cases/regression/operator-validate-byte-range.json -TESTS+=test/test-cases/regression/operator-verifycc.json -TESTS+=test/test-cases/regression/operator-verifycpf.json -TESTS+=test/test-cases/regression/operator-verifyssn.json -TESTS+=test/test-cases/regression/operator-verifysvnr.json -TESTS+=test/test-cases/regression/request-body-parser-json.json -TESTS+=test/test-cases/regression/request-body-parser-multipart-crlf.json -TESTS+=test/test-cases/regression/request-body-parser-multipart.json -TESTS+=test/test-cases/regression/request-body-parser-xml.json -TESTS+=test/test-cases/regression/request-body-parser-xml-validade-dtd.json -TESTS+=test/test-cases/regression/rule-920120.json -TESTS+=test/test-cases/regression/rule-920200.json -TESTS+=test/test-cases/regression/rule-920274.json -TESTS+=test/test-cases/regression/secaction.json -TESTS+=test/test-cases/regression/secargumentslimit.json -TESTS+=test/test-cases/regression/sec_component_signature.json -TESTS+=test/test-cases/regression/secmarker.json -TESTS+=test/test-cases/regression/secruleengine.json -TESTS+=test/test-cases/regression/transformation-none.json -TESTS+=test/test-cases/regression/transformations.json -TESTS+=test/test-cases/regression/variable-ARGS_COMBINED_SIZE.json -TESTS+=test/test-cases/regression/variable-ARGS_GET.json -TESTS+=test/test-cases/regression/variable-ARGS_GET_NAMES.json -TESTS+=test/test-cases/regression/variable-ARGS.json -TESTS+=test/test-cases/regression/variable-ARGS_NAMES.json -TESTS+=test/test-cases/regression/variable-ARGS_POST.json -TESTS+=test/test-cases/regression/variable-ARGS_POST_NAMES.json -TESTS+=test/test-cases/regression/variable-AUTH_TYPE.json -TESTS+=test/test-cases/regression/variable-DURATION.json -TESTS+=test/test-cases/regression/variable-ENV.json -TESTS+=test/test-cases/regression/variable-FILES_COMBINED_SIZE.json -TESTS+=test/test-cases/regression/variable-FILES.json -TESTS+=test/test-cases/regression/variable-FILES_NAMES.json -TESTS+=test/test-cases/regression/variable-FILES_SIZES.json -TESTS+=test/test-cases/regression/variable-FULL_REQUEST.json -TESTS+=test/test-cases/regression/variable-FULL_REQUEST_LENGTH.json -TESTS+=test/test-cases/regression/variable-GEO.json -TESTS+=test/test-cases/regression/variable-HIGHEST_SEVERITY.json -TESTS+=test/test-cases/regression/variable-INBOUND_DATA_ERROR.json -TESTS+=test/test-cases/regression/variable-MATCHED_VAR.json -TESTS+=test/test-cases/regression/variable-MATCHED_VAR_NAME.json -TESTS+=test/test-cases/regression/variable-MATCHED_VARS.json -TESTS+=test/test-cases/regression/variable-MATCHED_VARS_NAMES.json -TESTS+=test/test-cases/regression/variable-MODSEC_BUILD.json -TESTS+=test/test-cases/regression/variable-MULTIPART_CRLF_LF_LINES.json -TESTS+=test/test-cases/regression/variable-MULTIPART_FILENAME.json -TESTS+=test/test-cases/regression/variable-MULTIPART_INVALID_HEADER_FOLDING.json -TESTS+=test/test-cases/regression/variable-MULTIPART_NAME.json -TESTS+=test/test-cases/regression/variable-MULTIPART_PART_HEADERS.json -TESTS+=test/test-cases/regression/variable-MULTIPART_STRICT_ERROR.json -TESTS+=test/test-cases/regression/variable-MULTIPART_UNMATCHED_BOUNDARY.json -TESTS+=test/test-cases/regression/variable-OUTBOUND_DATA_ERROR.json -TESTS+=test/test-cases/regression/variable-PATH_INFO.json -TESTS+=test/test-cases/regression/variable-QUERY_STRING.json -TESTS+=test/test-cases/regression/variable-REMOTE_ADDR.json -TESTS+=test/test-cases/regression/variable-REMOTE_HOST.json -TESTS+=test/test-cases/regression/variable-REMOTE_PORT.json -TESTS+=test/test-cases/regression/variable-REMOTE_USER.json -TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR_ERROR.json -TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR.json -TESTS+=test/test-cases/regression/variable-REQUEST_BASENAME.json -TESTS+=test/test-cases/regression/variable-REQUEST_BODY.json -TESTS+=test/test-cases/regression/variable-REQUEST_BODY_LENGTH.json -TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES.json -TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES_NAMES.json -TESTS+=test/test-cases/regression/variable-REQUEST_FILENAME.json -TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS.json -TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS_NAMES.json -TESTS+=test/test-cases/regression/variable-REQUEST_LINE.json -TESTS+=test/test-cases/regression/variable-REQUEST_METHOD.json -TESTS+=test/test-cases/regression/variable-REQUEST_PROTOCOL.json -TESTS+=test/test-cases/regression/variable-REQUEST_URI.json -TESTS+=test/test-cases/regression/variable-REQUEST_URI_RAW.json -TESTS+=test/test-cases/regression/variable-RESPONSE_BODY.json -TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_LENGTH.json -TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_TYPE.json -TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS.json -TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS_NAMES.json -TESTS+=test/test-cases/regression/variable-RESPONSE_PROTOCOL.json -TESTS+=test/test-cases/regression/variable-RULE.json -TESTS+=test/test-cases/regression/variable-SERVER_ADDR.json -TESTS+=test/test-cases/regression/variable-SERVER_NAME.json -TESTS+=test/test-cases/regression/variable-SERVER_PORT.json -TESTS+=test/test-cases/regression/variable-SESSIONID.json -TESTS+=test/test-cases/regression/variable-STATUS.json -TESTS+=test/test-cases/regression/variable-TIME_DAY.json -TESTS+=test/test-cases/regression/variable-TIME_EPOCH.json -TESTS+=test/test-cases/regression/variable-TIME_HOUR.json -TESTS+=test/test-cases/regression/variable-TIME.json -TESTS+=test/test-cases/regression/variable-TIME_MIN.json -TESTS+=test/test-cases/regression/variable-TIME_MON.json -TESTS+=test/test-cases/regression/variable-TIME_SEC.json -TESTS+=test/test-cases/regression/variable-TIME_WDAY.json -TESTS+=test/test-cases/regression/variable-TIME_YEAR.json -TESTS+=test/test-cases/regression/variable-TX.json -TESTS+=test/test-cases/regression/variable-UNIQUE_ID.json -TESTS+=test/test-cases/regression/variable-URLENCODED_ERROR.json -TESTS+=test/test-cases/regression/variable-USERID.json -TESTS+=test/test-cases/regression/variable-variation-count.json -TESTS+=test/test-cases/regression/variable-variation-exclusion.json -TESTS+=test/test-cases/regression/variable-WEBAPPID.json -TESTS+=test/test-cases/regression/variable-WEBSERVER_ERROR_LOG.json -TESTS+=test/test-cases/regression/variable-XML.json -TESTS+=test/test-cases/secrules-language-tests/operators/beginsWith.json -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/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 -TESTS+=test/test-cases/secrules-language-tests/operators/geoLookup.json -TESTS+=test/test-cases/secrules-language-tests/operators/gt.json -TESTS+=test/test-cases/secrules-language-tests/operators/ipMatch.json -TESTS+=test/test-cases/secrules-language-tests/operators/le.json -TESTS+=test/test-cases/secrules-language-tests/operators/lt.json -TESTS+=test/test-cases/secrules-language-tests/operators/noMatch.json -TESTS+=test/test-cases/secrules-language-tests/operators/pmFromFile.json -TESTS+=test/test-cases/secrules-language-tests/operators/pm.json -TESTS+=test/test-cases/secrules-language-tests/operators/rx.json -TESTS+=test/test-cases/secrules-language-tests/operators/rxGlobal.json -TESTS+=test/test-cases/secrules-language-tests/operators/streq.json -TESTS+=test/test-cases/secrules-language-tests/operators/strmatch.json -TESTS+=test/test-cases/secrules-language-tests/operators/unconditionalMatch.json -TESTS+=test/test-cases/secrules-language-tests/operators/validateByteRange.json -TESTS+=test/test-cases/secrules-language-tests/operators/validateUrlEncoding.json -TESTS+=test/test-cases/secrules-language-tests/operators/validateUtf8Encoding.json -TESTS+=test/test-cases/secrules-language-tests/operators/verifyCC.json -TESTS+=test/test-cases/secrules-language-tests/operators/verifycpf.json -TESTS+=test/test-cases/secrules-language-tests/operators/verifyssn.json -TESTS+=test/test-cases/secrules-language-tests/operators/verifysvnr.json -TESTS+=test/test-cases/secrules-language-tests/operators/within.json -TESTS+=test/test-cases/secrules-language-tests/transformations/base64DecodeExt.json -TESTS+=test/test-cases/secrules-language-tests/transformations/base64Decode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/base64Encode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/cmdLine.json -TESTS+=test/test-cases/secrules-language-tests/transformations/compressWhitespace.json -TESTS+=test/test-cases/secrules-language-tests/transformations/cssDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/escapeSeqDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/hexDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/hexEncode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/htmlEntityDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/jsDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/length.json -TESTS+=test/test-cases/secrules-language-tests/transformations/lowercase.json -TESTS+=test/test-cases/secrules-language-tests/transformations/md5.json -TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePath.json -TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePathWin.json -TESTS+=test/test-cases/secrules-language-tests/transformations/parityEven7bit.json -TESTS+=test/test-cases/secrules-language-tests/transformations/parityOdd7bit.json -TESTS+=test/test-cases/secrules-language-tests/transformations/parityZero7bit.json -TESTS+=test/test-cases/secrules-language-tests/transformations/removeCommentsChar.json -TESTS+=test/test-cases/secrules-language-tests/transformations/removeComments.json -TESTS+=test/test-cases/secrules-language-tests/transformations/removeNulls.json -TESTS+=test/test-cases/secrules-language-tests/transformations/removeWhitespace.json -TESTS+=test/test-cases/secrules-language-tests/transformations/replaceComments.json -TESTS+=test/test-cases/secrules-language-tests/transformations/replaceNulls.json -TESTS+=test/test-cases/secrules-language-tests/transformations/sha1.json -TESTS+=test/test-cases/secrules-language-tests/transformations/sqlHexDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/trim.json -TESTS+=test/test-cases/secrules-language-tests/transformations/trimLeft.json -TESTS+=test/test-cases/secrules-language-tests/transformations/trimRight.json -TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecodeUni.json -TESTS+=test/test-cases/secrules-language-tests/transformations/urlEncode.json -TESTS+=test/test-cases/secrules-language-tests/transformations/utf8toUnicode.json - +include test/test-suite.in pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = modsecurity.pc diff --git a/build/win32/CMakeLists.txt b/build/win32/CMakeLists.txt new file mode 100644 index 000000000..20cba66cc --- /dev/null +++ b/build/win32/CMakeLists.txt @@ -0,0 +1,244 @@ +cmake_minimum_required(VERSION 3.24) + +set(BASE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) + +option(WITHOUT_LMDB "Include LMDB support" OFF) +option(WITHOUT_LUA "Include LUA support" OFF) +option(WITHOUT_LIBXML2 "Include LibXML2 support" OFF) +option(WITHOUT_MAXMIND "Include MaxMind support" OFF) +option(WITHOUT_CURL "Include CURL support" OFF) + +option(USE_ASAN "Build with Address Sanitizer" OFF) + +# common compiler settings + +# NOTE: MBEDTLS_CONFIG_FILE is not only required to compile the mbedtls subset in others, but also +# when their headers are included while compiling libModSecurity +add_compile_definitions(WIN32 _CRT_SECURE_NO_WARNINGS MBEDTLS_CONFIG_FILE="mbed-tls-config.h") + +# set standards conformance preprocessor & compiler to align with cross-compiled codebase +# NOTE: otherwise visual c++'s default compiler/preprocessor behaviour generates C4067 warnings +# (unexpected tokens following preprocessor directive - expected a newline) +add_compile_options(/Zc:preprocessor /permissive-) + +if(USE_ASAN) + add_compile_options(/fsanitize=address) + add_link_options(/INFERASANLIBS /INCREMENTAL:no) +endif() + +# libinjection + +project(libinjection C) + +add_library(libinjection STATIC ${BASE_DIR}/others/libinjection/src/libinjection_sqli.c ${BASE_DIR}/others/libinjection/src/libinjection_xss.c ${BASE_DIR}/others/libinjection/src/libinjection_html5.c) + +# mbedtls + +project(mbedtls C) + +add_library(mbedtls STATIC ${BASE_DIR}/others/mbedtls/base64.c ${BASE_DIR}/others/mbedtls/sha1.c ${BASE_DIR}/others/mbedtls/md5.c) + +target_include_directories(mbedtls PRIVATE ${BASE_DIR}/others) + +# +# libModSecurity +# + +project(libModSecurity + VERSION + 3.0.12 + LANGUAGES + CXX +) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED On) +set(CMAKE_CXX_EXTENSIONS Off) + +set(PACKAGE_BUGREPORT "security@modsecurity.org") +set(PACKAGE_NAME "modsecurity") +set(PACKAGE_VERSION "${PROJECT_VERSION}") +set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") +set(PACKAGE_TARNAME "${PACKAGE_NAME}") + +set(HAVE_YAJL 1) # should always be one, mandatory dependency +set(HAVE_GEOIP 0) # should always be zero, no conan package available +set(HAVE_SSDEEP 0) # should always be zero, no conan package available + +macro(enable_feature flag option) + if(${option}) + set(${flag} 0) + else() + set(${flag} 1) + endif() +endmacro() + +enable_feature(HAVE_LMDB ${WITHOUT_LMDB}) +enable_feature(HAVE_LUA ${WITHOUT_LUA}) +enable_feature(HAVE_LIBXML2 ${WITHOUT_LIBXML2}) +enable_feature(HAVE_MAXMIND ${WITHOUT_MAXMIND}) +enable_feature(HAVE_CURL ${WITHOUT_CURL}) + +include(${CMAKE_CURRENT_LIST_DIR}/ConfigureChecks.cmake) + +configure_file(config.h.cmake ${BASE_DIR}/src/config.h) + +find_package(PCRE2 REQUIRED) +find_package(PThreads4W REQUIRED) +find_package(Poco REQUIRED) +find_package(dirent REQUIRED) # used only by tests (check dirent::dirent refernces) + +macro(include_package package flag) + if(${flag}) + find_package(${package} REQUIRED) + endif() +endmacro() + +include_package(yajl HAVE_YAJL) +include_package(libxml2 HAVE_LIBXML2) +include_package(lua HAVE_LUA) +include_package(CURL HAVE_CURL) +include_package(lmdb HAVE_LMDB) +include_package(maxminddb HAVE_MAXMIND) + +# library +# + +# NOTE: required to generate libModSecurity's import library (libModSecurity.lib), used by tests to link with shared library +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +file(GLOB_RECURSE libModSecuritySources ${BASE_DIR}/src/*.cc) + +add_library(libModSecurity SHARED ${libModSecuritySources}) + +target_compile_definitions(libModSecurity PRIVATE WITH_PCRE2) +target_include_directories(libModSecurity PRIVATE ${BASE_DIR} ${BASE_DIR}/headers ${BASE_DIR}/others) +target_link_libraries(libModSecurity PRIVATE pcre2::pcre2 pthreads4w::pthreads4w libinjection mbedtls Poco::Poco Iphlpapi.lib) + +macro(add_package_dependency project compile_definition link_library flag) + if(${flag}) + target_compile_definitions(${project} PRIVATE ${compile_definition}) + target_link_libraries(${project} PRIVATE ${link_library}) + endif() +endmacro() + +add_package_dependency(libModSecurity WITH_YAJL yajl::yajl HAVE_YAJL) +add_package_dependency(libModSecurity WITH_LIBXML2 LibXml2::LibXml2 HAVE_LIBXML2) +add_package_dependency(libModSecurity WITH_LUA lua::lua HAVE_LUA) +if(HAVE_LUA) + target_compile_definitions(libModSecurity PRIVATE WITH_LUA_5_4) +endif() +add_package_dependency(libModSecurity MSC_WITH_CURL CURL::libcurl HAVE_CURL) +add_package_dependency(libModSecurity WITH_LMDB lmdb::lmdb HAVE_LMDB) +add_package_dependency(libModSecurity WITH_MAXMIND maxminddb::maxminddb HAVE_MAXMIND) + +# tests +# + +project(libModSecurityTests) + +function(setTestTargetProperties executable) + target_compile_definitions(${executable} PRIVATE WITH_PCRE2) + target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers) + target_link_libraries(${executable} PRIVATE libModSecurity pcre2::pcre2 dirent::dirent) + add_package_dependency(${executable} WITH_YAJL yajl::yajl HAVE_YAJL) +endfunction() + +# unit tests +file(GLOB unitTestSources ${BASE_DIR}/test/unit/*.cc) +add_executable(unit_tests ${unitTestSources}) +setTestTargetProperties(unit_tests) +target_compile_options(unit_tests PRIVATE /wd4805) + +# regression tests +file(GLOB regressionTestsSources ${BASE_DIR}/test/regression/*.cc) +add_executable(regression_tests ${regressionTestsSources}) +setTestTargetProperties(regression_tests) + +macro(add_regression_test_capability compile_definition flag) + if(${flag}) + target_compile_definitions(regression_tests PRIVATE ${compile_definition}) + endif() +endmacro() + +add_regression_test_capability(WITH_LUA HAVE_LUA) +add_regression_test_capability(WITH_CURL HAVE_CURL) +add_regression_test_capability(WITH_LMDB HAVE_LMDB) +add_regression_test_capability(WITH_MAXMIND HAVE_MAXMIND) + +enable_testing() + +file(READ ${BASE_DIR}/test/test-suite.in TEST_FILES_RAW) +string(REPLACE "\n" ";" TEST_FILES ${TEST_FILES_RAW}) + +foreach(TEST_FILE ${TEST_FILES}) + # ignore comment lines + string(FIND ${TEST_FILE} "#" is_comment) + if(NOT is_comment EQUAL 0) + string(FIND ${TEST_FILE} "TESTS+=" is_valid_prefix) + if(NOT is_valid_prefix EQUAL 0) + message(FATAL_ERROR "Invalid prefix in line: ${TEST_FILE}") + endif() + + # remove 'TESTS+=' prefix and 'test/' too because tests are launched + # from that directory + string(SUBSTRING ${TEST_FILE} 12 -1 TEST_FILE) + + # test name + get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + + # determine test runner based on test path prefix + string(FIND ${TEST_FILE} "test-cases/regression/" is_regression_test) + if(is_regression_test EQUAL 0) + set(TEST_RUNNER "regression_tests") + else() + set(TEST_RUNNER "unit_tests") + endif() + + add_test(NAME ${TEST_NAME} COMMAND ${TEST_RUNNER} ${TEST_FILE} WORKING_DIRECTORY ${BASE_DIR}/test) + endif() +endforeach() + +# benchmark +add_executable(benchmark ${BASE_DIR}/test/benchmark/benchmark.cc) +setTestTargetProperties(benchmark) + +# rules_optimization +add_executable(rules_optimization ${BASE_DIR}/test/optimization/optimization.cc) +setTestTargetProperties(rules_optimization) + + +# examples +# + +project(libModSecurityExamples) + +function(setExampleTargetProperties executable) + target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers) + target_link_libraries(${executable} PRIVATE libModSecurity) +endfunction() + +# simple_example_using_c +add_executable(simple_example_using_c ${BASE_DIR}/examples/simple_example_using_c/test.c) +setExampleTargetProperties(simple_example_using_c) + +# using_bodies_in_chunks +add_executable(using_bodies_in_chunks ${BASE_DIR}/examples/using_bodies_in_chunks/simple_request.cc) +setExampleTargetProperties(using_bodies_in_chunks) + +# reading_logs_via_rule_message +add_executable(reading_logs_via_rule_message ${BASE_DIR}/examples/reading_logs_via_rule_message/simple_request.cc) +setExampleTargetProperties(reading_logs_via_rule_message) +target_link_libraries(reading_logs_via_rule_message PRIVATE libModSecurity pthreads4w::pthreads4w) + +# reading_logs_with_offset +add_executable(reading_logs_with_offset ${BASE_DIR}/examples/reading_logs_with_offset/read.cc) +setExampleTargetProperties(reading_logs_with_offset) + +# tools +# + +# rules_check +add_executable(rules_check ${BASE_DIR}/tools/rules-check/rules-check.cc) +target_include_directories(rules_check PRIVATE ${BASE_DIR} ${BASE_DIR}/headers) +target_link_libraries(rules_check PRIVATE libModSecurity) diff --git a/build/win32/ConfigureChecks.cmake b/build/win32/ConfigureChecks.cmake new file mode 100644 index 000000000..6322b6965 --- /dev/null +++ b/build/win32/ConfigureChecks.cmake @@ -0,0 +1,18 @@ +include(CheckIncludeFile) +include(CheckIncludeFiles) + +check_include_file("dlfcn.h" HAVE_DLFCN_H) +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stdio.h" HAVE_STDIO_H) +check_include_file("stdlib.h" HAVE_STDLIB_H) +check_include_file("string" HAVE_STRING) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("string.h" HAVE_STRING_H) +check_include_file("sys/stat.h" HAVE_SYS_STAT_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) +check_include_file("sys/utsname.h" HAVE_SYS_UTSNAME_H) +check_include_file("unistd.h" HAVE_UNISTD_H) + +#/* Define to 1 if you have the ANSI C header files. */ +check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) diff --git a/build/win32/README.md b/build/win32/README.md new file mode 100644 index 000000000..7b70cfbdf --- /dev/null +++ b/build/win32/README.md @@ -0,0 +1,111 @@ +# libModSecurity Windows build information + +The Windows build of libModSecurity uses Build Tools for Visual Studio 2022 (for Visual C++ & CMake) and Conan package manager. + +## Contents + +- [Prerequisites](#prerequisites) +- [Build](#build) + - [Optional features](#optional-features) + - [Address Sanitizer](#address-sanitizer) + - [Docker container](#docker-container) + +## Prerequisites + + * [Build Tools for Visual Studio 2022](https://aka.ms/vs/17/release/vs_buildtools.exe) + * Install *Desktop development with C++* workload, which includes: + * MSVC C++ compiler + * Windows SDK + * CMake + * Address Sanitizer + * [Conan package manager 2.2.2](https://github.com/conan-io/conan/releases/download/2.2.2/conan-2.2.2-windows-x86_64-installer.exe) + * Install and then setup the default Conan profile to use the MSVC C++ compiler: + 1. Open a command-prompt and set the MSVC C++ compiler environment by executing: `C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat` + 2. Execute: `conan profile detect --force` + * [Git for Windows 2.44.0](https://github.com/git-for-windows/git/releases/download/v2.44.0.windows.1/Git-2.44.0-64-bit.exe) + * To clone the libModSecurity repository. + * NOTE: Make sure to initialize and update submodules (to get `libinjection` and regression tests) + * `git submodule init` + * `git submodule update` + +## Build + +Install the prerequisites listsed in the previous section, checkout libModSecurity and from the directory where it's located execute: + +``` +vcbuild.bat [build_configuration] [arch] [USE_ASAN] +``` + +where `[build_configuration]` can be: `Release` (default), `RelWithDebInfo`, `MinSizeRel` or `Debug`, and `[arch]` can be: `x86_64` (default) or `x86`. + +Built files will be located in the directory: `build\win32\build\[build_configuration]` and include: + + * `libModSecurity.dll` + * Executable files for test projects + * `unit_tests.exe` + * `regression_tests.exe` + * `benchmark.exe` + * `rules_optimization.exe` + * Executable files for examples + * `simple_example_using_c.exe` + * `using_bodies_in_chunks.exe` + * `reading_logs_via_rule_message.exe` + * `reading_logs_with_offset.exe` + * Executable files for tools + * `rules_check.exe` + +NOTE: When building a different configuration, it's recommended to reset: + + * the build directory: `build\win32\build` + * previously built conan packages executing the command: + * `conan remove * -c` + +### Optional features + +By default the following all the following features are enabled by including the associated third-party library through a Conan package: + + * libxml2 2.12.6 for XML processing support + * libcurl 8.6.0 to support http requests from rules + * libmaxminddb 1.9.1 to support reading MaxMind DB files. + * LUA 5.4.6 to enable rules to run scripts in this language for extensibility + * lmdb 0.9.31 in-memory database + +Each of these can be turned off by updating the associated `HAVE_xxx` variable (setting it to zero) in the beginning of the libModSecurity section of `CMakeLists.txt`. + +### Address Sanitizer + +[AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) (aka ASan) is a memory error detector for C/C++. + +To generate a build with *Address Sanitizer*, add the `USE_ASAN` optional third argument to `vcbuild.bat`. For example: + * `vcbuild.bat Debug x86_64 USE_ASAN` + +NOTE: `USE_ASAN` does not work with `Release` & `MinSizeRel` configurations because they do not include debug info (it is only compatible with `Debug` & `RelWithDebInfo` builds). + + * References + * [AddressSanitizer | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-170) + * [AddressSanitizer for Windows: x64 and Debug Build Support - C++ Team Blog (microsoft.com)](https://devblogs.microsoft.com/cppblog/asan-for-windows-x64-and-debug-build-support/) + * [AddressSanitizer language, build, and debugging reference | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/sanitizers/asan-building?view=msvc-170) + +### Docker container + +A `Dockerfile` configuration file is provided in the `docker` subdir that creates a Windows container image which installs the [prerequisites](#prerequisites) and builds libModSecurity and other binaries. + +NOTE: Windows containers are supported in Docker Desktop for Windows, using the *Switch to Windows containers...* option on the context menu of the system tray icon. + +To build the docker image, execute the following command (from the `build\win32\docker` directory): + + * `docker build -t libmodsecurity:latest -m 4GB .` + * Build type, architecture and build with Address Sanitizer can be configured through build arguments (`BUILD_TYPE`, `ARCH` & `USE_ASAN` respectively). For example, to generate a debug build, add the following argument: + * `--build-arg BUILD_TYPE=Debug` + +Once the image is generated, the library and associated binaries (tests & examples) are located in the `C:\src\ModSecurity\build\win32\build\[build_type]` directory. + +To extract the library (`libModSecurity.dll`) from the image, you can execute the following commands: + + * `docker container create --name [container_name] libmodsecurity` + * `docker cp [container_name]:C:\src\ModSecurity\build\win32\build\[build_type]\libModSecurity.dll .` + * NOTE: If you leave out the `libModSecurity.dll` filename out, you can copy all the built binaries (including examples & tests). + +Additionally, the image can be used interactively for additional development work by executing: + + * `docker run -it libmodsecurity` diff --git a/build/win32/conanfile.txt b/build/win32/conanfile.txt new file mode 100644 index 000000000..b1a698215 --- /dev/null +++ b/build/win32/conanfile.txt @@ -0,0 +1,15 @@ +[requires] +yajl/2.1.0 +pcre2/10.42 +pthreads4w/3.0.0 +libxml2/2.12.6 +lua/5.4.6 +libcurl/8.6.0 +lmdb/0.9.31 +libmaxminddb/1.9.1 +dirent/1.24 +poco/1.13.3 + +[generators] +CMakeDeps +CMakeToolchain diff --git a/build/win32/config.h.cmake b/build/win32/config.h.cmake new file mode 100644 index 000000000..2f6a73085 --- /dev/null +++ b/build/win32/config.h.cmake @@ -0,0 +1,92 @@ +/* config.h.cmake. Based upon generated config.h.in. */ + +#ifndef MODSECURITY_CONFIG_H +#define MODSECURITY_CONFIG_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_IOSTREAM + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H + +/* Define if GeoIP is available */ +#cmakedefine HAVE_GEOIP + +/* Define if LMDB is available */ +#cmakedefine HAVE_LMDB + +/* Define if LUA is available */ +#cmakedefine HAVE_LUA + +/* Define if MaxMind is available */ +#cmakedefine HAVE_MAXMIND + +/* Define if SSDEEP is available */ +#cmakedefine HAVE_SSDEEP + +/* Define if YAJL is available */ +#cmakedefine HAVE_YAJL + +/* Define if libcurl is available */ +#cmakedefine HAVE_CURL + +/* Name of package */ +#define PACKAGE "@PACKAGE_NAME@" + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" + +/* Define to the full name and version of this package. */ +#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" + +/* Define to the one symbol short name of this package. */ +#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" + +/* Define to 1 if you have the ANSI C header files. */ +#ifndef STDC_HEADERS +#cmakedefine STDC_HEADERS +#endif + +#endif // ndef MODSECURITY_CONFIG_H \ No newline at end of file diff --git a/build/win32/docker/Dockerfile b/build/win32/docker/Dockerfile new file mode 100644 index 000000000..1dc604827 --- /dev/null +++ b/build/win32/docker/Dockerfile @@ -0,0 +1,115 @@ +# escape=` + +ARG FROM_IMAGE=mcr.microsoft.com/windows/servercore:ltsc2022 +FROM ${FROM_IMAGE} + +# reset the shell. +SHELL ["cmd", "/S", "/C"] + +# set up environment to collect install errors. +COPY InstallBuildTools.cmd C:\TEMP\ +ADD https://aka.ms/vscollect.exe C:\TEMP\collect.exe + +# download channel for fixed install. +ARG CHANNEL_URL=https://aka.ms/vs/17/release/channel +ADD ${CHANNEL_URL} C:\TEMP\VisualStudio.chman + +# download and install Build Tools for Visual Studio 2022 for native desktop workload. +ADD https://aka.ms/vs/17/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe +RUN C:\TEMP\InstallBuildTools.cmd C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache ` + --channelUri C:\TEMP\VisualStudio.chman ` + --installChannelUri C:\TEMP\VisualStudio.chman ` + --add Microsoft.VisualStudio.Workload.VCTools ` + --includeRecommended ` + --installPath C:\BuildTools + +# download & install GIT +ARG GIT_VERSION=2.44.0 +ARG GIT_BINARY=Git-${GIT_VERSION}-64-bit.exe +ARG GIT_URL=https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/${GIT_BINARY} + +COPY git.inf C:\TEMP\ +ARG INSTALLER=C:\TEMP\${GIT_BINARY} +ADD ${GIT_URL} ${INSTALLER} +RUN %INSTALLER% /SP- /VERYSILENT /SUPPRESSMSGBOXES /NOCANCEL ` + /NORESTART /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /LOADINF=git.inf + +# download & setup conan +ARG CONAN_VERSION=2.2.2 +ARG CONAN_BINARY=conan-${CONAN_VERSION}-windows-x86_64-installer.exe +ARG CONAN_URL=https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/${CONAN_BINARY} + +ARG INSTALLER=C:\TEMP\${CONAN_BINARY} +ADD ${CONAN_URL} ${INSTALLER} +RUN %INSTALLER% /SP- /VERYSILENT /SUPPRESSMSGBOXES + +# setup conan profile +RUN C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat && conan profile detect --force + +# download libModSecurity +# + +# create src dir +ARG SRC_DIR=C:\src + +WORKDIR C:\ +RUN cmd.exe /C md %SRC_DIR% + +# libModSecurity +WORKDIR C:\src + +ARG MOD_SECURITY_TAG=v3/master +RUN git clone -c advice.detachedHead=false --depth 1 --branch %MOD_SECURITY_TAG% https://github.com/owasp-modsecurity/ModSecurity.git + +ARG MOD_SECURITY_DIR=${SRC_DIR}\ModSecurity +WORKDIR ${MOD_SECURITY_DIR} + +# fetch submodules (bindings/python, others/libinjection, test/test-cases/secrules-language-tests) +RUN git submodule init +RUN git submodule update + +# build libraries +# + +ARG BUILD_TYPE=Release +ARG ARCH=x86_64 +ARG USE_ASAN= + +RUN C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat && vcbuild.bat %BUILD_TYPE% %ARCH% %USE_ASAN% + +# test suite +# + +# setup test environment +RUN cmd.exe /C md \tmp +RUN cmd.exe /C md \bin +RUN cmd.exe /C copy "C:\Program Files\GIT\usr\bin" \bin > NUL +RUN cmd.exe /C copy "C:\Program Files\GIT\usr\bin\echo.exe" \bin\echo > NUL + +# disable tests that don't work on windows +ARG JQ_VERSION=1.7.1 +ARG JQ_BINARY=jq-windows-amd64.exe +ARG JQ_URL=https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/${JQ_BINARY} + +ARG JQ_BIN=C:\TEMP\jq.exe +ADD ${JQ_URL} ${JQ_BIN} + +WORKDIR ${MOD_SECURITY_DIR}\test\test-cases\regression + +RUN %JQ_BIN% "map(if .title == \"Test match variable (1/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json +RUN %JQ_BIN% "map(if .title == \"Test match variable (2/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json +RUN %JQ_BIN% "map(if .title == \"Test match variable (3/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json +RUN %JQ_BIN% "map(if .title == \"Variable offset - FILES_NAMES\" then .enabled = 0 else . end)" offset-variable.json > tmp.json && move /Y tmp.json offset-variable.json + +# run tests +WORKDIR ${MOD_SECURITY_DIR}\build\win32\build + +RUN C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat && ctest -C %BUILD_TYPE% --output-on-failure + +# setup container's entrypoint +# + +WORKDIR C:\ + +# Use developer command prompt and start PowerShell if no other command specified. +ENTRYPOINT ["C:\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat", "&&", "powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass"] diff --git a/build/win32/docker/InstallBuildTools.cmd b/build/win32/docker/InstallBuildTools.cmd new file mode 100644 index 000000000..a0c07c781 --- /dev/null +++ b/build/win32/docker/InstallBuildTools.cmd @@ -0,0 +1,17 @@ +@rem Copyright (C) Microsoft Corporation. All rights reserved. +@rem Licensed under the MIT license. See LICENSE.txt in the project root for license information. + +@if not defined _echo echo off +setlocal enabledelayedexpansion + +call %* +if "%ERRORLEVEL%"=="3010" ( + exit /b 0 +) else ( + if not "%ERRORLEVEL%"=="0" ( + set ERR=%ERRORLEVEL% + call C:\TEMP\collect.exe -zip:C:\vslogs.zip + + exit /b !ERR! + ) +) diff --git a/build/win32/docker/git.inf b/build/win32/docker/git.inf new file mode 100644 index 000000000..49781dd9a --- /dev/null +++ b/build/win32/docker/git.inf @@ -0,0 +1,20 @@ +[Setup] +Lang=default +Dir=C:\Program Files\Git +Group=Git +NoIcons=0 +SetupType=default +Components=ext,ext\shellhere,ext\guihere,gitlfs,assoc,autoupdate +Tasks= +EditorOption=VIM +CustomEditorPath= +PathOption=Cmd +SSHOption=OpenSSH +TortoiseOption=false +CURLOption=WinSSL +CRLFOption=LFOnly +BashTerminalOption=ConHost +PerformanceTweaksFSCache=Enabled +UseCredentialManager=Enabled +EnableSymlinks=Disabled +EnableBuiltinInteractiveAdd=Disabled \ No newline at end of file diff --git a/examples/multiprocess_c/multi.c b/examples/multiprocess_c/multi.c index 6c2ae5218..2481db4e5 100644 --- a/examples/multiprocess_c/multi.c +++ b/examples/multiprocess_c/multi.c @@ -19,7 +19,11 @@ #include #include #include +#ifndef WIN32 #include +#else +#include +#endif #include #include #include diff --git a/examples/reading_logs_via_rule_message/reading_logs_via_rule_message.h b/examples/reading_logs_via_rule_message/reading_logs_via_rule_message.h index 9d80d9b1d..6b2800482 100644 --- a/examples/reading_logs_via_rule_message/reading_logs_via_rule_message.h +++ b/examples/reading_logs_via_rule_message/reading_logs_via_rule_message.h @@ -13,10 +13,11 @@ * */ -#include - #include #include +#include +#include +#include #define NUM_THREADS 100 @@ -72,6 +73,11 @@ struct data_ms { modsecurity::RulesSet *rules; }; +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable:4716) // avoid error C4716: 'process_request': must return a value, as MSVC C++ compiler doesn't support [[noreturn]] +#pragma warning(disable:4715) // avoid warning c4715: 'process_request' : not all control paths return a value +#endif [[noreturn]] static void *process_request(void *data) { struct data_ms *a = (struct data_ms *)data; @@ -85,7 +91,7 @@ struct data_ms { modsecTransaction->processConnection(ip, 12345, "127.0.0.1", 80); modsecTransaction->processURI(request_uri, "GET", "1.1"); - usleep(10); + std::this_thread::sleep_for(std::chrono::microseconds(10)); modsecTransaction->addRequestHeader("Host", "net.tutsplus.com"); modsecTransaction->processRequestHeaders(); @@ -105,6 +111,9 @@ struct data_ms { pthread_exit(nullptr); } +#if defined _MSC_VER +#pragma warning(pop) +#endif class ReadingLogsViaRuleMessage { public: @@ -151,7 +160,7 @@ class ReadingLogsViaRuleMessage { reinterpret_cast(&dms)); } - usleep(10000); + std::this_thread::sleep_for(std::chrono::microseconds(10000)); for (i=0; i < NUM_THREADS; i++) { pthread_join(threads[i], &status); diff --git a/examples/using_bodies_in_chunks/simple_request.cc b/examples/using_bodies_in_chunks/simple_request.cc index ec8795fe8..783e639b2 100644 --- a/examples/using_bodies_in_chunks/simple_request.cc +++ b/examples/using_bodies_in_chunks/simple_request.cc @@ -13,7 +13,11 @@ * */ +#ifndef WIN32 #include +#else +#include +#endif #include #include diff --git a/src/actions/set_env.cc b/src/actions/set_env.cc index e1c3afdba..292906531 100644 --- a/src/actions/set_env.cc +++ b/src/actions/set_env.cc @@ -37,7 +37,11 @@ bool SetENV::evaluate(RuleWithActions *rule, Transaction *t) { auto pair = utils::string::ssplit_pair(colNameExpanded, '='); ms_dbg_a(t, 8, "Setting environment variable: " + pair.first + " to " + pair.second); +#ifndef WIN32 setenv(pair.first.c_str(), pair.second.c_str(), /*overwrite*/ 1); +#else + _putenv_s(pair.first.c_str(), pair.second.c_str()); +#endif return true; } diff --git a/src/actions/transformations/html_entity_decode.cc b/src/actions/transformations/html_entity_decode.cc index b9268df55..6a6832456 100644 --- a/src/actions/transformations/html_entity_decode.cc +++ b/src/actions/transformations/html_entity_decode.cc @@ -27,6 +27,10 @@ #include "modsecurity/transaction.h" #include "src/actions/transformations/transformation.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace actions { diff --git a/src/actions/transformations/lower_case.cc b/src/actions/transformations/lower_case.cc index d00ab40cb..7bca94797 100644 --- a/src/actions/transformations/lower_case.cc +++ b/src/actions/transformations/lower_case.cc @@ -17,6 +17,7 @@ #include #include +#include #include "modsecurity/transaction.h" #include "src/actions/transformations/transformation.h" diff --git a/src/actions/transformations/upper_case.cc b/src/actions/transformations/upper_case.cc index 118696ad4..998ec725e 100644 --- a/src/actions/transformations/upper_case.cc +++ b/src/actions/transformations/upper_case.cc @@ -17,6 +17,7 @@ #include #include +#include #include "modsecurity/transaction.h" #include "src/actions/transformations/transformation.h" diff --git a/src/audit_log/writer/parallel.cc b/src/audit_log/writer/parallel.cc index e42d7871c..d23e7958a 100644 --- a/src/audit_log/writer/parallel.cc +++ b/src/audit_log/writer/parallel.cc @@ -21,7 +21,12 @@ #include #include #include +#ifndef WIN32 #include +#else +#include +#include "src/compat/msvc.h" +#endif #include #include diff --git a/src/audit_log/writer/writer.h b/src/audit_log/writer/writer.h index 093f271c1..56dada062 100644 --- a/src/audit_log/writer/writer.h +++ b/src/audit_log/writer/writer.h @@ -18,8 +18,10 @@ #include +#ifndef WIN32 #include #include +#endif #include #include diff --git a/src/collection/backend/in_memory-per_process.h b/src/collection/backend/in_memory-per_process.h index 43b8de24d..3cacdca0d 100644 --- a/src/collection/backend/in_memory-per_process.h +++ b/src/collection/backend/in_memory-per_process.h @@ -12,6 +12,7 @@ * directly using the email address security@modsecurity.org. * */ +#include #ifdef __cplusplus diff --git a/src/collection/backend/lmdb.cc b/src/collection/backend/lmdb.cc index 040ebdff1..85c68e3cd 100644 --- a/src/collection/backend/lmdb.cc +++ b/src/collection/backend/lmdb.cc @@ -18,7 +18,11 @@ #include "src/collection/backend/collection_data.h" #include +#ifndef WIN32 #include +#else +#include +#endif #include #include diff --git a/src/compat/msvc.h b/src/compat/msvc.h new file mode 100644 index 000000000..ce9a14c0a --- /dev/null +++ b/src/compat/msvc.h @@ -0,0 +1,22 @@ +#ifndef __COMPAT_MSVC +#define __COMPAT_MSVC + +#include + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define strtok_r strtok_s +#define popen _popen +#define pclose _pclose + +inline tm* localtime_r(const time_t* tin, tm* tout) { + if (!localtime_s(tout, tin)) return tout; + + return nullptr; +} + +#endif diff --git a/src/debug_log/debug_log.cc b/src/debug_log/debug_log.cc index 54d86bda0..e883177ca 100644 --- a/src/debug_log/debug_log.cc +++ b/src/debug_log/debug_log.cc @@ -15,10 +15,6 @@ #include "modsecurity/debug_log.h" -#include - -#include - #include "src/debug_log/debug_log_writer.h" #include "src/debug_log_writer_agent.h" diff --git a/src/debug_log/debug_log_writer.cc b/src/debug_log/debug_log_writer.cc index 86facfed3..b7bb1ff26 100644 --- a/src/debug_log/debug_log_writer.cc +++ b/src/debug_log/debug_log_writer.cc @@ -15,18 +15,6 @@ #include "src/debug_log/debug_log_writer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - #include "src/utils/shared_files.h" namespace modsecurity { diff --git a/src/debug_log/debug_log_writer.h b/src/debug_log/debug_log_writer.h index 1f4f43df5..46b641d68 100644 --- a/src/debug_log/debug_log_writer.h +++ b/src/debug_log/debug_log_writer.h @@ -13,15 +13,7 @@ * */ -#include -#include -#include -#include - -#include -#include #include -#include #ifndef SRC_DEBUG_LOG_DEBUG_LOG_WRITER_H_ @@ -45,18 +37,16 @@ class DebugLogWriter { static int open(const std::string& m_fileName, std::string *error); private: - DebugLogWriter() : m_first(NULL) { } - ~DebugLogWriter() { } + DebugLogWriter() = default; + ~DebugLogWriter() = default; // C++ 03 // ======== // Dont forget to declare these two. You want to make sure they // are unacceptable otherwise you may accidentally get copies of // your singleton appearing. - DebugLogWriter(DebugLogWriter const&); - void operator=(DebugLogWriter const&); - - struct debug_log_file_handler *m_first; + DebugLogWriter(DebugLogWriter const&) = delete; + void operator=(DebugLogWriter const&) = delete; }; diff --git a/src/modsecurity.cc b/src/modsecurity.cc index 4b48b7995..7319bb969 100644 --- a/src/modsecurity.cc +++ b/src/modsecurity.cc @@ -258,14 +258,11 @@ int ModSecurity::processContentOffset(const char *content, size_t len, strlen("highlight")); yajl_gen_array_open(g); - while (vars.size() > 3) { - std::string value; + for(auto [it, pending] = std::tuple{vars.rbegin(), vars.size()}; pending > 3; pending -= 3) { yajl_gen_map_open(g); - vars.pop_back(); - const std::string &startingAt = vars.back().str(); - vars.pop_back(); - const std::string &size = vars.back().str(); - vars.pop_back(); + it++; + const std::string &startingAt = it->str(); it++; + const std::string &size = it->str(); it++; yajl_gen_string(g, reinterpret_cast("startingAt"), strlen("startingAt")); @@ -284,7 +281,7 @@ int ModSecurity::processContentOffset(const char *content, size_t len, return -1; } - value = std::string(content, stoi(startingAt), stoi(size)); + const auto value = std::string(content, stoi(startingAt), stoi(size)); if (varValue.size() > 0) { varValue.append(" " + value); } else { @@ -340,16 +337,13 @@ int ModSecurity::processContentOffset(const char *content, size_t len, yajl_gen_map_open(g); - while (ops.size() > 3) { - std::string value; + for(auto [it, pending] = std::tuple{ops.rbegin(), ops.size()}; pending > 3; pending -= 3) { yajl_gen_string(g, reinterpret_cast("highlight"), strlen("highlight")); yajl_gen_map_open(g); - ops.pop_back(); - std::string startingAt = ops.back().str(); - ops.pop_back(); - std::string size = ops.back().str(); - ops.pop_back(); + it++; + const std::string &startingAt = it->str(); it++; + const std::string &size = ops.back().str(); it++; yajl_gen_string(g, reinterpret_cast("startingAt"), strlen("startingAt")); @@ -371,7 +365,7 @@ int ModSecurity::processContentOffset(const char *content, size_t len, reinterpret_cast("value"), strlen("value")); - value = std::string(varValue, stoi(startingAt), stoi(size)); + const auto value = std::string(varValue, stoi(startingAt), stoi(size)); yajl_gen_string(g, reinterpret_cast(value.c_str()), diff --git a/src/operators/inspect_file.cc b/src/operators/inspect_file.cc index 9ce43af0d..72052a32d 100644 --- a/src/operators/inspect_file.cc +++ b/src/operators/inspect_file.cc @@ -23,6 +23,10 @@ #include "src/operators/operator.h" #include "src/utils/system.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace operators { diff --git a/src/operators/rbl.cc b/src/operators/rbl.cc index ffdb17a28..f1561e7f0 100644 --- a/src/operators/rbl.cc +++ b/src/operators/rbl.cc @@ -16,10 +16,15 @@ #include "src/operators/rbl.h" #include +#ifndef WIN32 #include #include #include #include +#else +#include +#include +#endif #include diff --git a/src/operators/rbl.h b/src/operators/rbl.h index fb57d2d96..30fcaa3e3 100644 --- a/src/operators/rbl.h +++ b/src/operators/rbl.h @@ -17,10 +17,14 @@ #define SRC_OPERATORS_RBL_H_ #include +#ifndef WIN32 #include #include #include #include +#else +#include +#endif #include #include diff --git a/src/parser/seclang-scanner.cc b/src/parser/seclang-scanner.cc index c0d4d7661..92afab762 100644 --- a/src/parser/seclang-scanner.cc +++ b/src/parser/seclang-scanner.cc @@ -4952,7 +4952,11 @@ static std::stack YY_PREVIOUS_STATE; * The user has a chance to override it with an option. */ /* %if-c-only */ +#ifndef WIN32 #include +#else +#include +#endif /* %endif */ /* %if-c++-only */ /* %endif */ diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index 691a6fd3f..d9ed6c6d1 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -21,7 +21,12 @@ #include #include #include +#ifndef WIN32 #include +#else +#include +#include "src/compat/msvc.h" +#endif #include #include #include @@ -61,12 +66,11 @@ MultipartPartTmpFile::~MultipartPartTmpFile() { void MultipartPartTmpFile::Open() { struct tm timeinfo; - char tstr[300]; time_t tt = time(NULL); localtime_r(&tt, &timeinfo); - memset(tstr, '\0', 300); + char tstr[300] {}; strftime(tstr, 299, "/%Y%m%d-%H%M%S", &timeinfo); std::string path = m_transaction->m_rules->m_uploadDirectory.m_value; @@ -74,14 +78,23 @@ void MultipartPartTmpFile::Open() { path += "-file-XXXXXX"; char* tmp = strdup(path.c_str()); +#ifndef WIN32 m_tmp_file_fd = mkstemp(tmp); +#else + _mktemp_s(tmp, path.length()+1); + m_tmp_file_fd = _open(tmp, _O_CREAT | _O_EXCL | _O_RDWR); +#endif m_tmp_file_name.assign(tmp); free(tmp); ms_dbg_a(m_transaction, 4, "MultipartPartTmpFile: Create filename= " + m_tmp_file_name); int mode = m_transaction->m_rules->m_uploadFileMode.m_value; if ((m_tmp_file_fd != -1) && (mode != 0)) { +#ifndef WIN32 if (fchmod(m_tmp_file_fd, mode) == -1) { +#else + if (_chmod(m_tmp_file_name.c_str(), mode) == -1) { +#endif m_tmp_file_fd = -1; } } diff --git a/src/rule_with_actions.cc b/src/rule_with_actions.cc index df323c4bb..5ae7d407e 100644 --- a/src/rule_with_actions.cc +++ b/src/rule_with_actions.cc @@ -172,9 +172,7 @@ RuleWithActions::~RuleWithActions() { bool RuleWithActions::evaluate(Transaction *transaction) { - RuleMessage rm(this, transaction); - std::shared_ptr rm2 = std::make_shared(&rm); - return evaluate(transaction, rm2); + return evaluate(transaction, std::make_shared(this, transaction)); } @@ -330,7 +328,7 @@ inline void RuleWithActions::executeTransformation( std::string newValue = a->evaluate(*oldValue, trans); if (newValue != *oldValue) { - std::shared_ptr u(new std::string(newValue)); + auto u = std::make_shared(newValue); if (m_containsMultiMatchAction) { ret->push_back(std::make_pair(u, a->m_name)); (*nth)++; @@ -355,14 +353,13 @@ void RuleWithActions::executeTransformations( int none = 0; int transformations = 0; std::string path(""); - std::shared_ptr value = - std::shared_ptr(new std::string(in)); + auto value = std::make_shared(in); if (m_containsMultiMatchAction == true) { /* keep the original value */ ret.push_back(std::make_pair( - std::shared_ptr(new std::string(*value)), - std::shared_ptr(new std::string(path)))); + std::make_shared(*value), + std::make_shared(path))); } for (Action *a : m_transformations) { @@ -436,8 +433,8 @@ void RuleWithActions::executeTransformations( if (!m_containsMultiMatchAction) { ret.push_back(std::make_pair( - std::shared_ptr(new std::string(*value)), - std::shared_ptr(new std::string(path)))); + std::make_shared(*value), + std::make_shared(path))); } } diff --git a/src/rules_set_properties.cc b/src/rules_set_properties.cc index 075e3e877..80078b3dd 100644 --- a/src/rules_set_properties.cc +++ b/src/rules_set_properties.cc @@ -19,6 +19,10 @@ #include "src/utils/string.h" #include "src/variables/variable.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { diff --git a/src/transaction.cc b/src/transaction.cc index cf52288d9..a07621257 100644 --- a/src/transaction.cc +++ b/src/transaction.cc @@ -53,6 +53,9 @@ #include "src/actions/disruptive/allow.h" #include "src/variables/remote_user.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif using modsecurity::actions::Action; diff --git a/src/unique_id.cc b/src/unique_id.cc index a78ebd751..3106776e3 100644 --- a/src/unique_id.cc +++ b/src/unique_id.cc @@ -17,7 +17,8 @@ #include "src/config.h" #ifdef WIN32 -#include +#include "src/compat/msvc.h" +#include #include #endif @@ -48,7 +49,11 @@ #endif #include +#ifndef WIN32 #include +#else +#include +#endif #include #include "src/utils/sha1.h" @@ -207,7 +212,7 @@ std::string UniqueId::ethernetMacAddress() { pAdapter = pAdapterInfo; while (pAdapter && !mac[0] && !mac[1] && !mac[2]) { if (pAdapter->AddressLength > 4) { - apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x", + snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)pAdapter->Address[0], (unsigned char)pAdapter->Address[1], (unsigned char)pAdapter->Address[2], diff --git a/src/utils/geo_lookup.cc b/src/utils/geo_lookup.cc index 33a80be5c..4e06a1ec5 100644 --- a/src/utils/geo_lookup.cc +++ b/src/utils/geo_lookup.cc @@ -13,10 +13,13 @@ * */ +#ifndef WIN32 #include -#include #include #include +#else +#include +#endif #include #include diff --git a/src/utils/https_client.cc b/src/utils/https_client.cc index 9ee84b154..f413e8ca0 100644 --- a/src/utils/https_client.cc +++ b/src/utils/https_client.cc @@ -20,10 +20,14 @@ #include #endif +#ifndef WIN32 #include #include #include #include +#else +#include +#endif #include #include @@ -94,6 +98,11 @@ bool HttpsClient::download(const std::string &uri) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1); +#ifdef WIN32 + /* use the operating system's native CA store for certificate verification.*/ + curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); +#endif + /* send all data to this function */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &HttpsClient::handle); diff --git a/src/utils/ip_tree.cc b/src/utils/ip_tree.cc index 969213a55..124bc47f3 100644 --- a/src/utils/ip_tree.cc +++ b/src/utils/ip_tree.cc @@ -15,10 +15,14 @@ #include "src/utils/ip_tree.h" +#ifndef WIN32 #include #include #include #include +#else +#include +#endif #include #include diff --git a/src/utils/msc_tree.cc b/src/utils/msc_tree.cc index 8e36f274e..27c7461c8 100644 --- a/src/utils/msc_tree.cc +++ b/src/utils/msc_tree.cc @@ -15,9 +15,15 @@ #include #include #include +#ifndef WIN32 #include #include #include +#else +#include "src/compat/msvc.h" +#include +#include +#endif #include "src/utils/msc_tree.h" diff --git a/src/utils/shared_files.cc b/src/utils/shared_files.cc index 73216bd00..df18022f7 100644 --- a/src/utils/shared_files.cc +++ b/src/utils/shared_files.cc @@ -15,236 +15,103 @@ #include "src/utils/shared_files.h" -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include +#ifdef WIN32 +#include +#include +#endif + namespace modsecurity { namespace utils { -std::pair SharedFiles::find_handler( - const std::string &fileName) { - for (const auto &i : m_handlers) { - if (i.first == fileName) { - return i.second; - } - } - return std::pair(NULL, NULL); -} - - -std::pair SharedFiles::add_new_handler( +SharedFiles::handlers_map::iterator SharedFiles::add_new_handler( const std::string &fileName, std::string *error) { - int shm_id; - int ret; - key_t mem_key_structure; - msc_file_handler_t *new_debug_log = NULL; - struct shmid_ds shared_mem_info; - FILE *fp; - bool toBeCreated = true; - - fp = fopen(fileName.c_str(), "a"); + FILE *fp = fopen(fileName.c_str(), "a"); if (fp == 0) { error->assign("Failed to open file: " + fileName); - goto err_fh; - } - - mem_key_structure = ftok(fileName.c_str(), 1); - if (mem_key_structure < 0) { - error->assign("Failed to select key for the shared memory (1): "); - error->append(strerror(errno)); - goto err_mem_key; - } - - shm_id = shmget(mem_key_structure, sizeof (msc_file_handler_t) \ - + fileName.size() + 1, IPC_CREAT | IPC_EXCL | 0666); - if (shm_id < 0) { - shm_id = shmget(mem_key_structure, sizeof (msc_file_handler_t) - + fileName.size() + 1, IPC_CREAT | 0666); - toBeCreated = false; - if (shm_id < 0) { - error->assign("Failed to allocate shared memory (1): "); - error->append(strerror(errno)); - goto err_shmget1; - } + return m_handlers.end(); } - ret = shmctl(shm_id, IPC_STAT, &shared_mem_info); - if (ret < 0) { - error->assign("Failed to get information on shared memory (1): "); - error->append(strerror(errno)); - goto err_shmctl1; - } - - new_debug_log = reinterpret_cast( - shmat(shm_id, NULL, 0)); - if ((reinterpret_cast(new_debug_log)[0]) == -1) { - error->assign("Failed to attach shared memory (1): "); - error->append(strerror(errno)); - goto err_shmat1; - } - - if (toBeCreated == false && shared_mem_info.shm_nattch == 0) { - toBeCreated = true; - } - - if (toBeCreated) { - memset(new_debug_log, '\0', sizeof(msc_file_handler_t)); - new_debug_log->shm_id_structure = shm_id; - memcpy(new_debug_log->file_name, fileName.c_str(), fileName.size()); - new_debug_log->file_name[fileName.size()] = '\0'; - } - m_handlers.push_back(std::make_pair(fileName, - std::make_pair(new_debug_log, fp))); - - return std::make_pair(new_debug_log, fp); -err_shmat1: - shmdt(new_debug_log); -err_shmctl1: -err_shmget1: -err_mem_key: - fclose(fp); -err_fh: - return std::pair(NULL, NULL); + return m_handlers.insert({ fileName, {fp, 0} }).first; } bool SharedFiles::open(const std::string& fileName, std::string *error) { - std::pair a; - bool ret = true; - - #if MODSEC_USE_GENERAL_LOCK - pthread_mutex_lock(m_generalLock); -#endif - - a = find_handler(fileName); - if (a.first == NULL) { - a = add_new_handler(fileName, error); - if (error->size() > 0) { - ret = false; - goto out; - } + auto it = m_handlers.find(fileName); + if (it == m_handlers.end()) { + it = add_new_handler(fileName, error); + if (error->size() > 0) + return false; } - if (a.first == NULL) { + + if (it == m_handlers.end()) { error->assign("Not able to open: " + fileName); - ret = false; - goto out; + return false; } -out: -#if MODSEC_USE_GENERAL_LOCK - pthread_mutex_unlock(m_generalLock); -#endif + it->second.cnt++; - return ret; + return true; } void SharedFiles::close(const std::string& fileName) { - std::pair a; - /* int ret; */ - /* int shm_id; */ - /* struct shmid_ds shared_mem_info; */ - /* int j = 0; */ - -#if MODSEC_USE_GENERAL_LOCK - pthread_mutex_lock(m_generalLock); -#endif - - if (fileName.empty()) { - goto out; - } - - a = find_handler(fileName); - if (a.first == NULL || a.second == NULL) { - goto out; - } - - /* fclose(a.second); */ - a.second = 0; - - /* - * Delete the file structure will be welcomed, but we cannot delay - * while the process is being killed. - * - for (std::pair> i : m_handlers) { - if (i.first == fileName) { - j++; - } - } + if (fileName.empty()) + return; - m_handlers.erase(m_handlers.begin()+j); - */ + auto it = m_handlers.find(fileName); + if (it == m_handlers.end()) + return; - /* hmdt(a.second); */ - shmctl(a.first->shm_id_structure, IPC_RMID, NULL); + it->second.cnt--; + if (it->second.cnt == 0) + { + fclose(it->second.fp); - /* - * - * We could check to see how many process attached to the shared memory - * we have, prior to the deletion of the shared memory. - * - ret = shmctl(a.first->shm_id_structure, IPC_STAT, &shared_mem_info); - if (ret < 0) { - goto out; + m_handlers.erase(it); } - ret = shared_mem_info.shm_nattch; - shm_id = a.first->shm_id_structure; - */ - -out: -#if MODSEC_USE_GENERAL_LOCK - pthread_mutex_unlock(m_generalLock); -#endif - return; } bool SharedFiles::write(const std::string& fileName, const std::string &msg, std::string *error) { - std::pair a; - std::string lmsg = msg; - size_t wrote; - struct flock lock{}; bool ret = true; - a = find_handler(fileName); - if (a.first == NULL) { + auto it = m_handlers.find(fileName); + if (it == m_handlers.end()) { error->assign("file is not open: " + fileName); return false; } //Exclusively lock whole file +#ifndef WIN32 + struct flock lock {}; lock.l_start = lock.l_len = lock.l_whence = 0; lock.l_type = F_WRLCK; - fcntl(fileno(a.second), F_SETLKW, &lock); + fcntl(fileno(it->second.fp), F_SETLKW, &lock); +#else + auto handle = reinterpret_cast(_get_osfhandle(fileno(it->second.fp))); + OVERLAPPED overlapped = { 0 }; + ::LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped); +#endif - wrote = fwrite(lmsg.c_str(), 1, lmsg.size(), a.second); + auto wrote = fwrite(msg.c_str(), 1, msg.size(), it->second.fp); if (wrote < msg.size()) { error->assign("failed to write: " + fileName); ret = false; } - fflush(a.second); + fflush(it->second.fp); //Remove exclusive lock +#ifndef WIN32 lock.l_type = F_UNLCK; - fcntl(fileno(a.second), F_SETLKW, &lock); + fcntl(fileno(it->second.fp), F_SETLKW, &lock); +#else + overlapped = { 0 }; + ::UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped); +#endif return ret; } diff --git a/src/utils/shared_files.h b/src/utils/shared_files.h index d0d8ef992..4953eeff4 100644 --- a/src/utils/shared_files.h +++ b/src/utils/shared_files.h @@ -17,45 +17,18 @@ #define SRC_UTILS_SHARED_FILES_H_ -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "modsecurity/transaction.h" -#include "modsecurity/audit_log.h" +#include +#include -/** - * Not using this critical section yet. - * - */ -/* #define MODSEC_USE_GENERAL_LOCK */ namespace modsecurity { namespace utils { -typedef struct msc_file_handler { - int shm_id_structure; - char file_name[]; -} msc_file_handler_t; - - class SharedFiles { - public: +public: bool open(const std::string& fileName, std::string *error); void close(const std::string& fileName); bool write(const std::string& fileName, const std::string &msg, @@ -66,86 +39,28 @@ class SharedFiles { return instance; } - protected: - std::pair find_handler( - const std::string &fileName); - std::pair add_new_handler( - const std::string &fileName, std::string *error); - - private: - SharedFiles() -#ifdef MODSEC_USE_GENERAL_LOCK - : m_generalLock(NULL), - m_memKeyStructure(0) -#endif - { -#ifdef MODSEC_USE_GENERAL_LOCK - int shm_id; - bool toBeCreated(false); - bool err = false; - - m_memKeyStructure = ftok(".", 1); // cppcheck-suppress useInitializationList - if (m_memKeyStructure < 0) { - err = true; - goto err_mem_key; - } - - shm_id = shmget(m_memKeyStructure, sizeof(pthread_mutex_t), - IPC_CREAT | IPC_EXCL | 0666); - if (shm_id < 0) { - shm_id = shmget(m_memKeyStructure, sizeof(pthread_mutex_t), - IPC_CREAT | 0666); - toBeCreated = false; - if (shm_id < 0) { - err = true; - goto err_shmget1; - } - } - - m_generalLock = reinterpret_cast( - shmat(shm_id, NULL, 0)); - if ((reinterpret_cast(m_generalLock)[0]) == -1) { - err = true; - goto err_shmat1; - } - - if (toBeCreated) { - memset(m_generalLock, '\0', sizeof(pthread_mutex_t)); - pthread_mutex_init(m_generalLock, NULL); - pthread_mutex_unlock(m_generalLock); - } - - if (err) { -err_mem_key: - std::cerr << strerror(errno) << std::endl; -err_shmget1: - std::cerr << "err_shmget1" << std::endl; -err_shmat1: - std::cerr << "err_shmat1" << std::endl; - } -#endif - } - ~SharedFiles() { -#if MODSEC_USE_GENERAL_LOCK - shmdt(m_generalLock); - shmctl(m_memKeyStructure, IPC_RMID, NULL); -#endif - } +private: + SharedFiles() = default; + ~SharedFiles() = default; // C++ 03 // ======== // Dont forget to declare these two. You want to make sure they // are unacceptable otherwise you may accidentally get copies of // your singleton appearing. - SharedFiles(SharedFiles const&); - void operator=(SharedFiles const&); - - std::vector>> m_handlers; -#if MODSEC_USE_GENERAL_LOCK - pthread_mutex_t *m_generalLock; - key_t m_memKeyStructure; -#endif + SharedFiles(SharedFiles const&) = delete; + void operator=(SharedFiles const&) = delete; + + struct handler_info { + FILE* fp; + unsigned int cnt; + }; + + using handlers_map = std::unordered_map; + handlers_map m_handlers; + + handlers_map::iterator add_new_handler( + const std::string &fileName, std::string *error); }; diff --git a/src/utils/string.cc b/src/utils/string.cc index 750d2bd1c..8d9e08ff4 100644 --- a/src/utils/string.cc +++ b/src/utils/string.cc @@ -17,11 +17,6 @@ #include #include #include -#ifdef __OpenBSD__ -#include -#else -#include -#endif #include #include diff --git a/src/utils/system.cc b/src/utils/system.cc index 69b904042..f48afb7d6 100644 --- a/src/utils/system.cc +++ b/src/utils/system.cc @@ -19,6 +19,9 @@ #include #ifdef __OpenBSD__ #include +#elif defined(WIN32) +#include "Poco/Glob.h" +#include #else #include #endif @@ -31,6 +34,7 @@ #include #if defined _MSC_VER +#include "src/compat/msvc.h" #include #elif defined __GNUC__ #include @@ -40,6 +44,36 @@ #include "src/utils/system.h" #include "src/config.h" +#ifdef WIN32 + +// Public domain code from mingw-w64's winpthreads +// https://sourceforge.net/p/mingw-w64/code/HEAD/tree/trunk/mingw-w64-libraries/winpthreads/src/clock.c +// + +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define POW10_7 10000000 + +// NOTE: includes only CLOCK_PROCESS_CPUTIME_ID implementation, ignores clock_id argument +static int clock_gettime(int clock_id, struct timespec *tp) +{ + unsigned __int64 t; + LARGE_INTEGER pf, pc; + union { + unsigned __int64 u64; + FILETIME ft; + } ct, et, kt, ut; + + if(0 == GetProcessTimes(GetCurrentProcess(), &ct.ft, &et.ft, &kt.ft, &ut.ft)) + return -1; + t = kt.u64 + ut.u64; + tp->tv_sec = t / POW10_7; + tp->tv_nsec = ((int) (t % POW10_7)) * 100; + + return 0; +} + +#endif + namespace modsecurity { namespace utils { @@ -64,19 +98,15 @@ double cpu_seconds(void) { std::string find_resource(const std::string& resource, const std::string& config, std::string *err) { - std::ifstream *iss; err->assign("Looking at: "); // Trying absolute or relative to the current dir. - iss = new std::ifstream(resource, std::ios::in); - if (iss->is_open()) { - iss->close(); - delete iss; + auto iss = std::ifstream(resource, std::ios::in); + if (iss.is_open()) { return resource; } else { err->append("'" + resource + "', "); } - delete iss; // What about `*' ? if (utils::expandEnv(resource, 0).size() > 0) { @@ -87,15 +117,12 @@ std::string find_resource(const std::string& resource, // Trying the same path of the configuration file. std::string f = get_path(config) + "/" + resource; - iss = new std::ifstream(f, std::ios::in); - if (iss->is_open()) { - iss->close(); - delete iss; + iss = std::ifstream(f, std::ios::in); + if (iss.is_open()) { return f; } else { err->append("'" + f + "', "); } - delete iss; // What about `*' ? if (utils::expandEnv(f, 0).size() > 0) { @@ -122,8 +149,16 @@ std::string get_path(const std::string& file) { std::list expandEnv(const std::string& var, int flags) { std::list vars; - -#ifdef __OpenBSD__ +#ifdef WIN32 + // NOTE: align scopes with if & if in other versions + { + { + std::set files; + Poco::Glob::glob(var, files); + for(auto file : files) { + std::replace(file.begin(), file.end(), '\\', '/'); // preserve unix-like paths + const char* exp[] = { file.c_str() }; +#elif defined(__OpenBSD__) glob_t p; if (glob(var.c_str(), flags, NULL, &p) == false) { if (p.gl_pathc) { @@ -135,15 +170,13 @@ std::list expandEnv(const std::string& var, int flags) { if (p.we_wordc) { for (char** exp = p.we_wordv; *exp; ++exp) { #endif - std::ifstream *iss = new std::ifstream(exp[0], std::ios::in); - if (iss->is_open()) { - iss->close(); + auto iss = std::ifstream(exp[0], std::ios::in); + if (iss.is_open()) vars.push_back(exp[0]); - } - delete iss; } } -#ifdef __OpenBSD__ +#ifdef WIN32 +#elif defined(__OpenBSD__) globfree(&p); #else wordfree(&p); @@ -153,7 +186,13 @@ std::list expandEnv(const std::string& var, int flags) { } bool createDir(const std::string& dir, int mode, std::string *error) { +#ifndef WIN32 int ret = mkdir(dir.data(), mode); +#else + if (dir == ".") + return true; + int ret = _mkdir(dir.c_str()); +#endif if (ret != 0 && errno != EEXIST) { error->assign("Not able to create directory: " + dir + ": " \ + strerror(errno) + "."); diff --git a/src/utils/system.h b/src/utils/system.h index b3033b44e..d6b0adf63 100644 --- a/src/utils/system.h +++ b/src/utils/system.h @@ -13,8 +13,6 @@ * */ -#include -#include #include #include diff --git a/src/variables/env.cc b/src/variables/env.cc index dfb2c3f03..bf40954a8 100644 --- a/src/variables/env.cc +++ b/src/variables/env.cc @@ -25,9 +25,15 @@ #include #include +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + #include "modsecurity/transaction.h" +#ifndef WIN32 extern char **environ; +#endif namespace modsecurity { namespace variables { @@ -47,12 +53,20 @@ void Env::evaluate(Transaction *transaction, transaction->m_variableEnvs.insert(a); } + const auto hasName = m_name.length() > 0; for (auto& x : transaction->m_variableEnvs) { - if (x.first != m_name && m_name.length() > 0) { +#ifndef WIN32 + if (hasName && x.first != m_name) { +#else + if (hasName && strcasecmp(x.first.c_str(), m_name.c_str()) != 0) { +#endif continue; } - if (!m_keyExclusion.toOmit(x.first)) { - l->push_back(new VariableValue(&m_collectionName, &x.first, + // (Windows) we need to keep the case from the rule in case that from + // the environment differs. + const auto &key = hasName ? m_name : x.first; + if (!m_keyExclusion.toOmit(key)) { + l->push_back(new VariableValue(&m_collectionName, &key, &x.second)); } } diff --git a/src/variables/time.cc b/src/variables/time.cc index ae071b496..7d481ee6b 100644 --- a/src/variables/time.cc +++ b/src/variables/time.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_day.cc b/src/variables/time_day.cc index f74a8409b..f094d4c92 100644 --- a/src/variables/time_day.cc +++ b/src/variables/time_day.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_hour.cc b/src/variables/time_hour.cc index 0f2a42193..ab809ead0 100644 --- a/src/variables/time_hour.cc +++ b/src/variables/time_hour.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_min.cc b/src/variables/time_min.cc index 526d8e6f6..9d03be4de 100644 --- a/src/variables/time_min.cc +++ b/src/variables/time_min.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_mon.cc b/src/variables/time_mon.cc index 53ba29190..17e74f311 100644 --- a/src/variables/time_mon.cc +++ b/src/variables/time_mon.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_sec.cc b/src/variables/time_sec.cc index 20d3be7a9..5e39af7fe 100644 --- a/src/variables/time_sec.cc +++ b/src/variables/time_sec.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_wday.cc b/src/variables/time_wday.cc index ff9825c18..fd6a02784 100644 --- a/src/variables/time_wday.cc +++ b/src/variables/time_wday.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/src/variables/time_year.cc b/src/variables/time_year.cc index a46561254..f68e8cd62 100644 --- a/src/variables/time_year.cc +++ b/src/variables/time_year.cc @@ -30,6 +30,10 @@ #include "modsecurity/transaction.h" +#ifdef WIN32 +#include "src/compat/msvc.h" +#endif + namespace modsecurity { namespace variables { diff --git a/test/benchmark/download-owasp-v2-rules.sh b/test/benchmark/download-owasp-v2-rules.sh index facc48d3e..dd1623e72 100755 --- a/test/benchmark/download-owasp-v2-rules.sh +++ b/test/benchmark/download-owasp-v2-rules.sh @@ -2,7 +2,10 @@ # # -git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git owasp-v2 +git clone https://github.com/coreruleset/coreruleset.git owasp-v2 +cd owasp-v2 +git checkout 2.2.9 -b tag2.2.9 +cd - echo 'Include "owasp-v2/base_rules/*.conf"' >> basic_rules.conf echo 'Include "owasp-v2/optional_rules/*.conf"' >> basic_rules.conf diff --git a/test/benchmark/download-owasp-v3-rules.sh b/test/benchmark/download-owasp-v3-rules.sh index c3bc0dfa8..d0d9f8094 100755 --- a/test/benchmark/download-owasp-v3-rules.sh +++ b/test/benchmark/download-owasp-v3-rules.sh @@ -1,7 +1,7 @@ #!/bin/bash -git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git owasp-v3 +git clone https://github.com/coreruleset/coreruleset.git owasp-v3 cd owasp-v3 git checkout v3.0.2 -b tag3.0.2 cd - diff --git a/test/custom-test-driver b/test/custom-test-driver index aa490ee69..d9b0d0ff6 100755 --- a/test/custom-test-driver +++ b/test/custom-test-driver @@ -42,9 +42,9 @@ print_usage () { cat < #include +#ifndef WIN32 #include +#else +#include +#endif #include #include diff --git a/test/regression/regression.cc b/test/regression/regression.cc index 6343c9e1c..1420ddcd0 100644 --- a/test/regression/regression.cc +++ b/test/regression/regression.cc @@ -15,7 +15,11 @@ #include +#ifndef WIN32 #include +#else +#include +#endif #include #include @@ -60,13 +64,11 @@ bool contains(const std::string &s, const std::string &pattern) { void clearAuditLog(const std::string &filename) { if (!filename.empty()) { - std::ifstream file; - file.open(filename.c_str(), std::ifstream::out | std::ifstream::trunc); + std::ofstream file{filename.c_str(), std::ofstream::out | std::ofstream::trunc}; if (!file.is_open() || file.fail()) { std::cout << std::endl << "Failed to clear previous contents of audit log: " \ << filename << std::endl; } - file.close(); } } std::string getAuditLogContent(const std::string &filename) { @@ -506,6 +508,7 @@ int main(int argc, char **argv) { #ifdef NO_LOGS std::cout << "Test utility cannot work without logging support." \ << std::endl; + return 0; #else test.cmd_options(argc, argv); if (!test.m_automake_output && !test.m_count_all) { @@ -605,6 +608,6 @@ int main(int argc, char **argv) { delete vec; } + return failed; #endif - return 0; } diff --git a/test/test-cases/regression/offset-variable.json b/test/test-cases/regression/offset-variable.json index 9aa5120c2..7ffe9299b 100644 --- a/test/test-cases/regression/offset-variable.json +++ b/test/test-cases/regression/offset-variable.json @@ -215,7 +215,6 @@ ] }, "expected":{ - // should not match }, "rules":[ "SecRequestBodyAccess On", @@ -248,7 +247,6 @@ ] }, "expected":{ - // should not match }, "rules":[ "SecRequestBodyAccess On", diff --git a/test/test-suite.in b/test/test-suite.in new file mode 100644 index 000000000..eee696ec0 --- /dev/null +++ b/test/test-suite.in @@ -0,0 +1,255 @@ +# for i in `find test/test-cases -iname *.json`; do echo TESTS+=$i; done +TESTS+=test/test-cases/regression/action-allow.json +TESTS+=test/test-cases/regression/action-block.json +TESTS+=test/test-cases/regression/action-ctl_request_body_access.json +TESTS+=test/test-cases/regression/action-ctl_request_body_processor.json +TESTS+=test/test-cases/regression/action-ctl_request_body_processor_urlencoded.json +TESTS+=test/test-cases/regression/action-ctl_rule_engine.json +TESTS+=test/test-cases/regression/action-ctl_audit_engine.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_id.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_by_tag.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_id.json +TESTS+=test/test-cases/regression/action-ctl_rule_remove_target_by_tag.json +TESTS+=test/test-cases/regression/action-disruptive.json +TESTS+=test/test-cases/regression/action-exec.json +TESTS+=test/test-cases/regression/action-expirevar.json +TESTS+=test/test-cases/regression/action-id.json +TESTS+=test/test-cases/regression/action-initcol.json +TESTS+=test/test-cases/regression/action-msg.json +TESTS+=test/test-cases/regression/action-setenv.json +TESTS+=test/test-cases/regression/action-setrsc.json +TESTS+=test/test-cases/regression/action-setsid.json +TESTS+=test/test-cases/regression/action-setuid.json +TESTS+=test/test-cases/regression/actions.json +TESTS+=test/test-cases/regression/action-skip.json +TESTS+=test/test-cases/regression/action-tag.json +TESTS+=test/test-cases/regression/action-tnf-base64.json +TESTS+=test/test-cases/regression/action-xmlns.json +TESTS+=test/test-cases/regression/auditlog.json +TESTS+=test/test-cases/regression/collection-case-insensitive.json +TESTS+=test/test-cases/regression/collection-lua.json +TESTS+=test/test-cases/regression/collection-regular_expression_selection.json +TESTS+=test/test-cases/regression/collection-resource.json +TESTS+=test/test-cases/regression/collection-tx.json +TESTS+=test/test-cases/regression/collection-tx-with-macro.json +TESTS+=test/test-cases/regression/config-body_limits.json +TESTS+=test/test-cases/regression/config-calling_phases_by_name.json +TESTS+=test/test-cases/regression/config-include-bad.json +TESTS+=test/test-cases/regression/config-include.json +TESTS+=test/test-cases/regression/config-remove_by_id.json +TESTS+=test/test-cases/regression/config-remove_by_msg.json +TESTS+=test/test-cases/regression/config-remove_by_tag.json +TESTS+=test/test-cases/regression/config-response_type.json +TESTS+=test/test-cases/regression/config-secdefaultaction.json +TESTS+=test/test-cases/regression/config-secremoterules.json +TESTS+=test/test-cases/regression/config-update-action-by-id.json +TESTS+=test/test-cases/regression/config-update-target-by-id.json +TESTS+=test/test-cases/regression/config-update-target-by-msg.json +TESTS+=test/test-cases/regression/config-update-target-by-tag.json +TESTS+=test/test-cases/regression/config-xml_external_entity.json +TESTS+=test/test-cases/regression/debug_log.json +TESTS+=test/test-cases/regression/directive-sec_rule_script.json +TESTS+=test/test-cases/regression/issue-1152.json +TESTS+=test/test-cases/regression/issue-1528.json +TESTS+=test/test-cases/regression/issue-1565.json +TESTS+=test/test-cases/regression/issue-1576.json +TESTS+=test/test-cases/regression/issue-1591.json +TESTS+=test/test-cases/regression/issue-1725.json +TESTS+=test/test-cases/regression/issue-1743.json +TESTS+=test/test-cases/regression/issue-1785.json +TESTS+=test/test-cases/regression/issue-1812.json +TESTS+=test/test-cases/regression/issue-1831.json +TESTS+=test/test-cases/regression/issue-1844.json +TESTS+=test/test-cases/regression/issue-1850.json +TESTS+=test/test-cases/regression/issue-1941.json +TESTS+=test/test-cases/regression/issue-1943.json +TESTS+=test/test-cases/regression/issue-1956.json +TESTS+=test/test-cases/regression/issue-1960.json +TESTS+=test/test-cases/regression/issue-2099.json +TESTS+=test/test-cases/regression/issue-2000.json +TESTS+=test/test-cases/regression/issue-2111.json +TESTS+=test/test-cases/regression/issue-2196.json +TESTS+=test/test-cases/regression/issue-2423-msg-in-chain.json +TESTS+=test/test-cases/regression/issue-2427.json +TESTS+=test/test-cases/regression/issue-2296.json +TESTS+=test/test-cases/regression/issue-394.json +TESTS+=test/test-cases/regression/issue-849.json +TESTS+=test/test-cases/regression/issue-960.json +TESTS+=test/test-cases/regression/misc.json +TESTS+=test/test-cases/regression/misc-variable-under-quotes.json +TESTS+=test/test-cases/regression/offset-variable.json +TESTS+=test/test-cases/regression/operator-detectsqli.json +TESTS+=test/test-cases/regression/operator-detectxss.json +TESTS+=test/test-cases/regression/operator-fuzzyhash.json +TESTS+=test/test-cases/regression/operator-inpectFile.json +TESTS+=test/test-cases/regression/operator-ipMatchFromFile.json +TESTS+=test/test-cases/regression/operator-pm.json +TESTS+=test/test-cases/regression/operator-rx.json +TESTS+=test/test-cases/regression/operator-rxGlobal.json +TESTS+=test/test-cases/regression/operator-UnconditionalMatch.json +TESTS+=test/test-cases/regression/operator-validate-byte-range.json +TESTS+=test/test-cases/regression/operator-verifycc.json +TESTS+=test/test-cases/regression/operator-verifycpf.json +TESTS+=test/test-cases/regression/operator-verifyssn.json +TESTS+=test/test-cases/regression/operator-verifysvnr.json +TESTS+=test/test-cases/regression/request-body-parser-json.json +TESTS+=test/test-cases/regression/request-body-parser-multipart-crlf.json +TESTS+=test/test-cases/regression/request-body-parser-multipart.json +TESTS+=test/test-cases/regression/request-body-parser-xml.json +TESTS+=test/test-cases/regression/request-body-parser-xml-validade-dtd.json +TESTS+=test/test-cases/regression/rule-920120.json +TESTS+=test/test-cases/regression/rule-920200.json +TESTS+=test/test-cases/regression/rule-920274.json +TESTS+=test/test-cases/regression/secaction.json +TESTS+=test/test-cases/regression/secargumentslimit.json +TESTS+=test/test-cases/regression/sec_component_signature.json +TESTS+=test/test-cases/regression/secmarker.json +TESTS+=test/test-cases/regression/secruleengine.json +TESTS+=test/test-cases/regression/transformation-none.json +TESTS+=test/test-cases/regression/transformations.json +TESTS+=test/test-cases/regression/variable-ARGS_COMBINED_SIZE.json +TESTS+=test/test-cases/regression/variable-ARGS_GET.json +TESTS+=test/test-cases/regression/variable-ARGS_GET_NAMES.json +TESTS+=test/test-cases/regression/variable-ARGS.json +TESTS+=test/test-cases/regression/variable-ARGS_NAMES.json +TESTS+=test/test-cases/regression/variable-ARGS_POST.json +TESTS+=test/test-cases/regression/variable-ARGS_POST_NAMES.json +TESTS+=test/test-cases/regression/variable-AUTH_TYPE.json +TESTS+=test/test-cases/regression/variable-DURATION.json +TESTS+=test/test-cases/regression/variable-ENV.json +TESTS+=test/test-cases/regression/variable-FILES_COMBINED_SIZE.json +TESTS+=test/test-cases/regression/variable-FILES.json +TESTS+=test/test-cases/regression/variable-FILES_NAMES.json +TESTS+=test/test-cases/regression/variable-FILES_SIZES.json +TESTS+=test/test-cases/regression/variable-FULL_REQUEST.json +TESTS+=test/test-cases/regression/variable-FULL_REQUEST_LENGTH.json +TESTS+=test/test-cases/regression/variable-GEO.json +TESTS+=test/test-cases/regression/variable-HIGHEST_SEVERITY.json +TESTS+=test/test-cases/regression/variable-INBOUND_DATA_ERROR.json +TESTS+=test/test-cases/regression/variable-MATCHED_VAR.json +TESTS+=test/test-cases/regression/variable-MATCHED_VAR_NAME.json +TESTS+=test/test-cases/regression/variable-MATCHED_VARS.json +TESTS+=test/test-cases/regression/variable-MATCHED_VARS_NAMES.json +TESTS+=test/test-cases/regression/variable-MODSEC_BUILD.json +TESTS+=test/test-cases/regression/variable-MULTIPART_CRLF_LF_LINES.json +TESTS+=test/test-cases/regression/variable-MULTIPART_FILENAME.json +TESTS+=test/test-cases/regression/variable-MULTIPART_INVALID_HEADER_FOLDING.json +TESTS+=test/test-cases/regression/variable-MULTIPART_NAME.json +TESTS+=test/test-cases/regression/variable-MULTIPART_PART_HEADERS.json +TESTS+=test/test-cases/regression/variable-MULTIPART_STRICT_ERROR.json +TESTS+=test/test-cases/regression/variable-MULTIPART_UNMATCHED_BOUNDARY.json +TESTS+=test/test-cases/regression/variable-OUTBOUND_DATA_ERROR.json +TESTS+=test/test-cases/regression/variable-PATH_INFO.json +TESTS+=test/test-cases/regression/variable-QUERY_STRING.json +TESTS+=test/test-cases/regression/variable-REMOTE_ADDR.json +TESTS+=test/test-cases/regression/variable-REMOTE_HOST.json +TESTS+=test/test-cases/regression/variable-REMOTE_PORT.json +TESTS+=test/test-cases/regression/variable-REMOTE_USER.json +TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR_ERROR.json +TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR.json +TESTS+=test/test-cases/regression/variable-REQUEST_BASENAME.json +TESTS+=test/test-cases/regression/variable-REQUEST_BODY.json +TESTS+=test/test-cases/regression/variable-REQUEST_BODY_LENGTH.json +TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES.json +TESTS+=test/test-cases/regression/variable-REQUEST_COOKIES_NAMES.json +TESTS+=test/test-cases/regression/variable-REQUEST_FILENAME.json +TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS.json +TESTS+=test/test-cases/regression/variable-REQUEST_HEADERS_NAMES.json +TESTS+=test/test-cases/regression/variable-REQUEST_LINE.json +TESTS+=test/test-cases/regression/variable-REQUEST_METHOD.json +TESTS+=test/test-cases/regression/variable-REQUEST_PROTOCOL.json +TESTS+=test/test-cases/regression/variable-REQUEST_URI.json +TESTS+=test/test-cases/regression/variable-REQUEST_URI_RAW.json +TESTS+=test/test-cases/regression/variable-RESPONSE_BODY.json +TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_LENGTH.json +TESTS+=test/test-cases/regression/variable-RESPONSE_CONTENT_TYPE.json +TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS.json +TESTS+=test/test-cases/regression/variable-RESPONSE_HEADERS_NAMES.json +TESTS+=test/test-cases/regression/variable-RESPONSE_PROTOCOL.json +TESTS+=test/test-cases/regression/variable-RULE.json +TESTS+=test/test-cases/regression/variable-SERVER_ADDR.json +TESTS+=test/test-cases/regression/variable-SERVER_NAME.json +TESTS+=test/test-cases/regression/variable-SERVER_PORT.json +TESTS+=test/test-cases/regression/variable-SESSIONID.json +TESTS+=test/test-cases/regression/variable-STATUS.json +TESTS+=test/test-cases/regression/variable-TIME_DAY.json +TESTS+=test/test-cases/regression/variable-TIME_EPOCH.json +TESTS+=test/test-cases/regression/variable-TIME_HOUR.json +TESTS+=test/test-cases/regression/variable-TIME.json +TESTS+=test/test-cases/regression/variable-TIME_MIN.json +TESTS+=test/test-cases/regression/variable-TIME_MON.json +TESTS+=test/test-cases/regression/variable-TIME_SEC.json +TESTS+=test/test-cases/regression/variable-TIME_WDAY.json +TESTS+=test/test-cases/regression/variable-TIME_YEAR.json +TESTS+=test/test-cases/regression/variable-TX.json +TESTS+=test/test-cases/regression/variable-UNIQUE_ID.json +TESTS+=test/test-cases/regression/variable-URLENCODED_ERROR.json +TESTS+=test/test-cases/regression/variable-USERID.json +TESTS+=test/test-cases/regression/variable-variation-count.json +TESTS+=test/test-cases/regression/variable-variation-exclusion.json +TESTS+=test/test-cases/regression/variable-WEBAPPID.json +TESTS+=test/test-cases/regression/variable-WEBSERVER_ERROR_LOG.json +TESTS+=test/test-cases/regression/variable-XML.json +TESTS+=test/test-cases/secrules-language-tests/operators/beginsWith.json +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/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 +TESTS+=test/test-cases/secrules-language-tests/operators/geoLookup.json +TESTS+=test/test-cases/secrules-language-tests/operators/gt.json +TESTS+=test/test-cases/secrules-language-tests/operators/ipMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/le.json +TESTS+=test/test-cases/secrules-language-tests/operators/lt.json +TESTS+=test/test-cases/secrules-language-tests/operators/noMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/pmFromFile.json +TESTS+=test/test-cases/secrules-language-tests/operators/pm.json +TESTS+=test/test-cases/secrules-language-tests/operators/rx.json +TESTS+=test/test-cases/secrules-language-tests/operators/rxGlobal.json +TESTS+=test/test-cases/secrules-language-tests/operators/streq.json +TESTS+=test/test-cases/secrules-language-tests/operators/strmatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/unconditionalMatch.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateByteRange.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateUrlEncoding.json +TESTS+=test/test-cases/secrules-language-tests/operators/validateUtf8Encoding.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifyCC.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifycpf.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifyssn.json +TESTS+=test/test-cases/secrules-language-tests/operators/verifysvnr.json +TESTS+=test/test-cases/secrules-language-tests/operators/within.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64DecodeExt.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64Decode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/base64Encode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/cmdLine.json +TESTS+=test/test-cases/secrules-language-tests/transformations/compressWhitespace.json +TESTS+=test/test-cases/secrules-language-tests/transformations/cssDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/escapeSeqDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/hexDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/hexEncode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/htmlEntityDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/jsDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/length.json +TESTS+=test/test-cases/secrules-language-tests/transformations/lowercase.json +TESTS+=test/test-cases/secrules-language-tests/transformations/md5.json +TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePath.json +TESTS+=test/test-cases/secrules-language-tests/transformations/normalisePathWin.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityEven7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityOdd7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/parityZero7bit.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeCommentsChar.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeComments.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeNulls.json +TESTS+=test/test-cases/secrules-language-tests/transformations/removeWhitespace.json +TESTS+=test/test-cases/secrules-language-tests/transformations/replaceComments.json +TESTS+=test/test-cases/secrules-language-tests/transformations/replaceNulls.json +TESTS+=test/test-cases/secrules-language-tests/transformations/sha1.json +TESTS+=test/test-cases/secrules-language-tests/transformations/sqlHexDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trim.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trimLeft.json +TESTS+=test/test-cases/secrules-language-tests/transformations/trimRight.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlDecodeUni.json +TESTS+=test/test-cases/secrules-language-tests/transformations/urlEncode.json +TESTS+=test/test-cases/secrules-language-tests/transformations/utf8toUnicode.json diff --git a/test/test-suite.sh b/test/test-suite.sh index ce03c244e..20262239d 100755 --- a/test/test-suite.sh +++ b/test/test-suite.sh @@ -13,7 +13,7 @@ then AMOUNT=$(./regression_tests countall ../$FILE) RET=$? if [ $RET -ne 0 ]; then - echo ":test-result: SKIP: json is not enabled. (regression/$RET) ../$FILE:$i" + echo ":test-result: SKIP: json is not enabled. (regression/$RET) ../$FILE" exit 0 fi @@ -30,10 +30,10 @@ else RET=$? if [ $RET -eq 127 ] then - echo ":test-result: SKIP: json is not enabled. (unit/$RET) ../$FILE:$i" + echo ":test-result: SKIP: json is not enabled. (unit/$RET) ../$FILE" elif [ $RET -ne 0 ] then - echo ":test-result: FAIL possible segfault: (unit/$RET) ../$FILE:$i" + echo ":test-result: FAIL possible segfault: (unit/$RET) ../$FILE" fi fi diff --git a/test/unit/unit.cc b/test/unit/unit.cc index 4d75ba94d..46013ec74 100644 --- a/test/unit/unit.cc +++ b/test/unit/unit.cc @@ -195,6 +195,10 @@ int main(int argc, char **argv) { std::cout << t->print() << std::endl; } + const int skp = std::count_if(results.cbegin(), results.cend(), [](const auto &i) + { return i->skipped; }); + const int failed = results.size() - skp; + if (!test.m_automake_output) { std::cout << std::endl; @@ -202,13 +206,7 @@ int main(int argc, char **argv) { if (results.size() == 0) { std::cout << KGRN << "All tests passed" << RESET << std::endl; } else { - int skp = 0; - for (const auto &i : results) { - if (i->skipped == true) { - skp++; - } - } - std::cout << KRED << results.size()-skp << " failed."; + std::cout << KRED << failed << " failed."; std::cout << RESET << std::endl; if (skp > 0) { std::cout << " " << std::to_string(skp) << " "; @@ -217,13 +215,12 @@ int main(int argc, char **argv) { } } - for (std::pair *> a : test) { - std::vector *vec = a.second; - for (int i = 0; i < vec->size(); i++) { - delete vec->at(i); - } + for (auto a : test) { + auto *vec = a.second; + for(auto *t : *vec) + delete t; delete vec; } -} - + return failed; +} diff --git a/tools/rules-check/rules-check.cc b/tools/rules-check/rules-check.cc index f59439ee9..46c8e1a87 100644 --- a/tools/rules-check/rules-check.cc +++ b/tools/rules-check/rules-check.cc @@ -15,7 +15,11 @@ #include #include +#ifndef WIN32 #include +#else +#include +#endif #include #include diff --git a/vcbuild.bat b/vcbuild.bat new file mode 100644 index 000000000..b24572aba --- /dev/null +++ b/vcbuild.bat @@ -0,0 +1,28 @@ +@rem For Windows build information, see build\win32\README.md + +@echo off +pushd %CD% + +if not "%1"=="" (set build_type=%1) else (set build_type=Release) +echo Build type: %build_type% + +if not "%2"=="" (set arch=%2) else (set arch=x86_64) +echo Arch: %arch% + +if "%3"=="USE_ASAN" ( + echo Address Sanitizer: Enabled + set CI_ASAN=-c tools.build:cxxflags="[""/fsanitize=address""]" + set ASAN_FLAG=ON +) else ( + echo Address Sanitizer: Disabled + set CI_ASAN= + set ASAN_FLAG=OFF +) + +cd build\win32 +conan install . -s compiler.cppstd=17 %CI_ASAN% --output-folder=build --build=missing --settings=build_type=%build_type% --settings=arch=%arch% +cd build +cmake --fresh .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DUSE_ASAN=%ASAN_FLAG% %4 %5 %6 %7 %8 %9 +cmake --build . --config %build_type% + +popd