Event message with description "Invalid function" in the Windows Application Event Log
Opened this issue · 13 comments
Describe the bug
Confused ModSecurity event message in the Windows Application Event Log.
To Reproduce
- Install ModSecurityIIS_2.9.7-64b-64.msi
- Configure ModSecurity for the website
- Open website in a browser
- Check events in the Windows Application Event Log
Actual result
Event message in the Windows Application Event Log:
Log Name: Application Source: ModSecurity Date: 6/23/2025 5:54:11 AM Event ID: 1 Task Category: None Level: Information Keywords: Classic User: N/A Computer: EC2AMAZ-UJ76N58 Description: Incorrect function. Event Xml: <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"> <System> <Provider Name="ModSecurity" /> <EventID Qualifiers="0">1</EventID> <Version>0</Version> <Level>4</Level> <Task>0</Task> <Opcode>0</Opcode> <Keywords>0x80000000000000</Keywords> <TimeCreated SystemTime="2025-06-23T05:54:11.1744601Z" /> <EventRecordID>42355</EventRecordID> <Correlation /> <Execution ProcessID="0" ThreadID="0" /> <Channel>Application</Channel> <Computer>EC2AMAZ-UJ76N58</Computer> <Security /> </System> <EventData> <Data>ModSecurity for IIS (STABLE)/2.9.7 (http://www.modsecurity.org/) configured.</Data> </EventData> </Event>
Expected behavior
Event message in the Windows Application Event Log:
Log Name: Application Source: ModSecurity Date: 6/23/2025 5:54:11 AM Event ID: 1 Task Category: None Level: Information Keywords: Classic User: N/A Computer: EC2AMAZ-UJ76N58 Description: ModSecurity for IIS (STABLE)/2.9.7 (http://www.modsecurity.org/) configured. Event Xml: <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"> <System> <Provider Name="ModSecurity" /> <EventID Qualifiers="0">1</EventID> <Version>0</Version> <Level>4</Level> <Task>0</Task> <Opcode>0</Opcode> <Keywords>0x80000000000000</Keywords> <TimeCreated SystemTime="2025-06-23T05:54:11.1744601Z" /> <EventRecordID>42355</EventRecordID> <Correlation /> <Execution ProcessID="0" ThreadID="0" /> <Channel>Application</Channel> <Computer>EC2AMAZ-UJ76N58</Computer> <Security /> </System> <EventData> <Data>ModSecurity for IIS (STABLE)/2.9.7 (http://www.modsecurity.org/) configured.</Data> </EventData> </Event>
Server (please complete the following information):
- ModSecurity version (and connector): 2.9.7 MSI installer
- WebServer: IIS
- OS (and distro): Windows
Hi @skvoboo-gh,
thanks for reporting this issue. As you remember there was an issue with Windows module source code (#3399 - that was also reported by you), so first of all could you check which version you use? I mean the source version (perhaps 2.9.10?).
There was an issue (#3373) which was fixed in #3374 - there the logging mechanism was changed (more precisely: there was a change in logging mechanism in #3192, which was reported in #3373 and fixed in #3374).
May be we should apply this modification (#3374) for Windows too.
I am using ModSecurity 2.9.7, because this is a latest binary distribution (with MSI installer in assets).
I don't think all the issues you listed above are related to this issue.
I think the root cause of this issue is the lack of registration of the ModSecurity event source in the Windows registry.
An application can use the Application log without adding a new event source to the registry. If the application calls RegisterEventSource and passes a source name that cannot be found in the registry, the event-logging service uses the Application log by default. However, because there are no message files, the Event Viewer cannot map any event identifiers or event categories to a description string, and will display an error. For this reason, you should add a unique event source to the registry for your application and specify a message file.
Ah, thanks.
I can try to take a look at that.
FYI, after testing, adding the following minimal message file in the /iis folder resolves the issue:
ModSecurity.mc:
MessageId=0x1
Language=English
%1
.Compiled using MSVC with the following commands:
mc .\ModSecurity.mc
rc .\ModSecurity.rc
link -dll -noentry .\ModSecurity.res
-> Produces ModSecurity.dllThen add the registry entry pointing to the compiled message file:
❯ Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\EventLog\Application\ModSecurity
EventMessageFile : D:\Path\To\ModSecurity.dllTest Results:
The event log now properly displays the messages instead of "Incorrect function":
❯ Get-EventLog -LogName Application -Source ModSecurity -Newest 8
Index Time EntryType Source InstanceID Message
----- ---- --------- ------ ---------- -------
265708 Aug 26 16:41 Information ModSecurity 1 ModSecurity: StatusEngine call failed. Query: GIX…
265707 Aug 26 16:41 Information ModSecurity 1 ModSecurity: StatusEngine call: "2.9.7,IIS,1.7.0/…
265706 Aug 26 16:41 Information ModSecurity 1 ModSecurity: LIBXML compiled version="2.9.14"
265705 Aug 26 16:41 Information ModSecurity 1 ModSecurity: YAJL compiled version="2.1.0"
265704 Aug 26 16:41 Information ModSecurity 1 ModSecurity: LUA compiled version="Lua 5.3"
265703 Aug 26 16:41 Information ModSecurity 1 ModSecurity: PCRE compiled version="8.45 "; loade…
265702 Aug 26 16:41 Information ModSecurity 1 ModSecurity: APR compiled version="1.7.0"; loaded…
265701 Aug 26 16:41 Information ModSecurity 1 ModSecurity for IIS (STABLE)/2.9.7 (http://www.mo…Hi @A13501350,
FYI, after testing, adding the following minimal message file in the
/iisfolder resolves the issue:
Would you consider sending a PR?
Do you think it's possible to add a new test pipeline with Windows?
I implemented a CMake-based build system and GitHub Actions CI/CD pipeline for the ModSecurity IIS module on Windows. The CMake setup leverages vcpkg for dependency management, supports both x86 and x64 architectures. The GitHub Actions workflow provides automated packaging using WiX toolset, testing with OWASP Core Ruleset. While the implementation has shown working in initial testing, this pipeline has not undergone comprehensive testing.
1. CMake:
cmake_minimum_required(VERSION 3.15)
project(ModSecurityIIS C CXX)
find_package(LibXml2 CONFIG REQUIRED)
find_package(PCRE2 CONFIG REQUIRED)
find_package(CURL CONFIG REQUIRED)
find_package(APR CONFIG REQUIRED)
# iis/CMakeLists.txt
set(IIS_MODULE_NAME "modsecurityiis") # Name should match the original output
# Source files for IIS module (reusing Apache sources)
set(IIS_APACHE_SOURCES
../apache2/mod_security2.c
../apache2/apache2_config.c
../apache2/apache2_io.c
../apache2/apache2_util.c
../apache2/re.c
../apache2/re_operators.c
../apache2/re_actions.c
../apache2/re_tfns.c
../apache2/re_variables.c
../apache2/msc_logging.c
../apache2/msc_xml.c
../apache2/msc_multipart.c
../apache2/modsecurity.c
../apache2/msc_parsers.c
../apache2/msc_util.c
../apache2/msc_pcre.c
../apache2/persist_dbm.c
../apache2/msc_reqbody.c
../apache2/msc_geo.c
../apache2/msc_gsb.c
../apache2/msc_crypt.c
../apache2/msc_tree.c
../apache2/msc_unicode.c
../apache2/acmp.c
../apache2/msc_lua.c
../apache2/msc_release.c
../apache2/msc_status_engine.c
../apache2/msc_remote_rules.c
../apache2/msc_json.c
../apache2/libinjection/libinjection_html5.c
../apache2/libinjection/libinjection_sqli.c
../apache2/libinjection/libinjection_xss.c
)
# Source files for standalone components (if they exist in the project)
set(IIS_STANDALONE_SOURCES
../standalone/api.c
../standalone/buckets.c
../standalone/config.c
../standalone/filters.c
../standalone/hooks.c
../standalone/regex.c
../standalone/server.c
)
# Determine architecture
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCHITECTURE "x64")
else()
set(ARCHITECTURE "x86")
endif()
# Check if standalone directory exists, if not, exclude those sources
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../standalone)
set(IIS_STANDALONE_SOURCES "")
endif()
set(IIS_RESOURCE_MC "${CMAKE_CURRENT_SOURCE_DIR}/ModSecurityIISMessage.mc")
set(MC_GENERATED_RC "${CMAKE_CURRENT_BINARY_DIR}/ModSecurityIISMessage.rc")
set(MC_GENERATED_H "${CMAKE_CURRENT_BINARY_DIR}/ModSecurityIISMessage.h")
add_custom_command(
OUTPUT ${MC_GENERATED_RC} ${MC_GENERATED_H}
COMMAND mc.exe
ARGS -U -h "${CMAKE_CURRENT_BINARY_DIR}/" -r "${CMAKE_CURRENT_BINARY_DIR}/" "${IIS_RESOURCE_MC}"
DEPENDS "${IIS_RESOURCE_MC}"
COMMENT "Generating resource files from ${IIS_RESOURCE_MC}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
# Source files for IIS-specific components
set(IIS_MODULE_SOURCES
main.cpp
moduleconfig.cpp
mymodule.cpp
mymodule.def
${MC_GENERATED_RC}
)
set_source_files_properties(
${MC_GENERATED_RC}
${MC_GENERATED_H}
PROPERTIES GENERATED TRUE
)
add_library(${IIS_MODULE_NAME} SHARED
${IIS_APACHE_SOURCES}
${IIS_STANDALONE_SOURCES}
${IIS_MODULE_SOURCES}
)
# Set the output name and extension
set_target_properties(${IIS_MODULE_NAME} PROPERTIES
OUTPUT_NAME ${IIS_MODULE_NAME}
PREFIX ""
SUFFIX ".dll"
)
# Include directories
target_include_directories(${IIS_MODULE_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_SOURCE_DIR}/../apache2
${CMAKE_CURRENT_SOURCE_DIR}/../apache2/libinjection
${LIBXML2_INCLUDE_DIR}/libxml
${PCRE2_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${APR_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
# Include standalone directory if it exists
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../standalone)
target_include_directories(${IIS_MODULE_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../standalone
)
endif()
# Apache-specific includes
if(APACHE_ROOT)
if(NOT EXISTS "${APACHE_ROOT}")
message(FATAL_ERROR "APACHE_ROOT is defined but the directory '${APACHE_ROOT}' does not exist. Please set APACHE_ROOT to a valid Apache installation directory.")
endif()
if(NOT EXISTS "${APACHE_ROOT}/lib")
message(FATAL_ERROR "APACHE_ROOT/lib directory does not exist. Expected: '${APACHE_ROOT}/lib'. Please ensure Apache libraries are available.")
endif()
file(TO_CMAKE_PATH "${APACHE_ROOT}" APACHE_ROOT)
# Create imported targets for Apache libraries
add_library(Apache::httpd SHARED IMPORTED)
set_target_properties(Apache::httpd PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${APACHE_ROOT}/include"
IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libhttpd.lib"
IMPORTED_LOCATION "${APACHE_ROOT}/bin/libhttpd.dll"
)
add_library(Apache::apr SHARED IMPORTED)
set_target_properties(Apache::apr PROPERTIES
IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libapr-1.lib"
IMPORTED_LOCATION "${APACHE_ROOT}/bin/libapr-1.dll"
)
add_library(Apache::aprutil SHARED IMPORTED)
set_target_properties(Apache::aprutil PROPERTIES
IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libaprutil-1.lib"
IMPORTED_LOCATION "${APACHE_ROOT}/bin/libaprutil-1.dll"
)
add_library(Apache::apriconv SHARED IMPORTED)
set_target_properties(Apache::apriconv PROPERTIES
IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libapriconv-1.lib"
IMPORTED_LOCATION "${APACHE_ROOT}/bin/libapriconv-1.dll"
)
target_include_directories(${IIS_MODULE_NAME} PRIVATE
${APACHE_ROOT}/include
)
endif()
# Compile definitions to match the original Makefile.win
set(MODSECURITY_VERSION_FLAG "VERSION_IIS") # Define the version flag string
target_compile_definitions(${IIS_MODULE_NAME} PRIVATE
WIN32
WINNT
inline=APR_INLINE
AP_DECLARE_STATIC
WITH_CURL
WITH_REMOTE_RULES
MSC_LARGE_STREAM_INPUT
WITH_YAJL
${MODSECURITY_VERSION_FLAG} # Use the defined version flag
)
option(WITH_LUA "Enable Lua support" OFF)
# Optional compile definitions
if(WITH_LUA)
find_package(Lua CONFIG REQUIRED)
target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_LUA)
target_include_directories(${IIS_MODULE_NAME} PRIVATE ${LUA_INCLUDE_DIR})
endif()
option(WITH_YAJL "Enable YAJL support" OFF)
if(WITH_YAJL)
# Manually find YAJL if config.cmake is not available (e.g., from vcpkg)
find_path(YAJL_INCLUDE_DIR yajl/yajl_common.h
PATHS "${CMAKE_CURRENT_SOURCE_DIR}/build-${ARCHITECTURE}/vcpkg_installed/${ARCHITECTURE}-windows/include"
NO_DEFAULT_PATH
)
find_library(YAJL_LIBRARY NAMES yajl
PATHS "${CMAKE_CURRENT_SOURCE_DIR}/build-${ARCHITECTURE}/vcpkg_installed/${ARCHITECTURE}-windows/lib"
NO_DEFAULT_PATH
)
if(YAJL_INCLUDE_DIR AND YAJL_LIBRARY)
set(YAJL_INCLUDE_DIRS ${YAJL_INCLUDE_DIR})
set(YAJL_LIBRARIES ${YAJL_LIBRARY})
target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_YAJL)
target_include_directories(${IIS_MODULE_NAME} PRIVATE ${YAJL_INCLUDE_DIRS})
else()
message(WARNING "YAJL not found. YAJL_INCLUDE_DIR: '${YAJL_INCLUDE_DIR}', YAJL_LIBRARY: '${YAJL_LIBRARY}'. Please ensure yajl is installed via vcpkg in the vcpkg_installed directory. Disabling YAJL support.")
option(WITH_YAJL "Enable YAJL support" OFF) # Disable if not found
endif()
endif()
option(WITH_SSDEEP "Enable SSDEEP support" OFF)
if(WITH_SSDEEP)
set(SSDEEP_ROOT "" CACHE PATH "Path to manually built ssdeep")
if(NOT SSDEEP_ROOT OR NOT EXISTS "${SSDEEP_ROOT}")
message(WARNING "SSDEEP_ROOT is not defined or path does not exist. Current SSDEEP_ROOT: '${SSDEEP_ROOT}'. Please set SSDEEP_ROOT to the ssdeep installation directory. Disabling SSDEEP support.")
set(WITH_SSDEEP OFF CACHE BOOL "Enable SSDEEP support" FORCE)
else()
file(TO_CMAKE_PATH "${SSDEEP_ROOT}" SSDEEP_ROOT)
message(STATUS "SSDEEP_ROOT: ${SSDEEP_ROOT}")
find_path(SSDEEP_INCLUDE_DIR fuzzy.h
PATHS "${SSDEEP_ROOT}/include"
NO_DEFAULT_PATH
)
if(SSDEEP_INCLUDE_DIR)
message(STATUS "Found manually built ssdeep include: ${SSDEEP_INCLUDE_DIR}")
target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_SSDEEP)
target_include_directories(${IIS_MODULE_NAME} PRIVATE ${SSDEEP_INCLUDE_DIR})
set(SSDEEP_DEF_FILE "${SSDEEP_ROOT}/fuzzy.def")
if(NOT EXISTS "${SSDEEP_DEF_FILE}")
message(WARNING "fuzzy.def not found at ${SSDEEP_DEF_FILE}. Disabling SSDEEP support.")
set(WITH_SSDEEP OFF CACHE BOOL "Enable SSDEEP support" FORCE)
else()
set(SSDEEP_GENERATED_LIB "${CMAKE_CURRENT_BINARY_DIR}/fuzzy.lib")
set(SSDEEP_GENERATED_dll "${CMAKE_CURRENT_BINARY_DIR}/bin/fuzzy.dll")
add_custom_command(
OUTPUT ${SSDEEP_GENERATED_LIB}
COMMAND lib.exe /machine:${ARCHITECTURE} /def:${SSDEEP_DEF_FILE} /out:${SSDEEP_GENERATED_LIB}
DEPENDS "${SSDEEP_DEF_FILE}"
COMMENT "Generating SSDEEP .lib from .def for MSVC"
VERBATIM
)
set_source_files_properties(${SSDEEP_GENERATED_LIB} PROPERTIES GENERATED TRUE)
add_custom_target(generate_ssdeep_lib ALL
DEPENDS ${SSDEEP_GENERATED_LIB}
COMMENT "Ensuring ssdeep lib is generated"
)
add_dependencies(${IIS_MODULE_NAME} generate_ssdeep_lib)
add_library(SSDEEP::fuzzy SHARED IMPORTED)
set_target_properties(SSDEEP::fuzzy PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${SSDEEP_INCLUDE_DIR}"
IMPORTED_LOCATION "${SSDEEP_GENERATED_dll}"
IMPORTED_IMPLIB "${SSDEEP_GENERATED_LIB}"
)
endif()
endif()
endif()
endif()
# Compiler-specific options for MSVC to match the original Makefile.win
if(MSVC)
target_compile_options(${IIS_MODULE_NAME} PRIVATE
/nologo
/O2
/W3
/wd4244
/wd4018
/MD
/Zi
)
# Linker options to match the original Makefile.win
set_target_properties(${IIS_MODULE_NAME} PROPERTIES
LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
)
endif()
# Link libraries to match the original Makefile.win
target_link_libraries(${IIS_MODULE_NAME} PRIVATE
LibXml2::LibXml2
PCRE2::8BIT
CURL::libcurl
kernel32
user32
gdi32
winspool
comdlg32
advapi32
shell32
ole32
oleaut32
uuid
odbc32
odbccp32
ws2_32
iphlpapi
)
# Apache-specific libraries
if(APACHE_ROOT)
target_link_libraries(${IIS_MODULE_NAME} PRIVATE
Apache::httpd
Apache::apr
Apache::aprutil
Apache::apriconv
)
else()
message(WARNING "APACHE_ROOT is not defined or path does not exist. Current APACHE_ROOT: '${APACHE_ROOT}'. Please set APACHE_ROOT to the Apache installation directory.")
endif()
# Optional link libraries
if(WITH_LUA)
target_link_libraries(${IIS_MODULE_NAME} PRIVATE ${LUA_LIBRARIES})
endif()
if(WITH_YAJL)
target_link_libraries(${IIS_MODULE_NAME} PRIVATE ${YAJL_LIBRARIES})
endif()
if(WITH_SSDEEP AND SSDEEP_INCLUDE_DIR AND SSDEEP_GENERATED_LIB)
target_link_libraries(${IIS_MODULE_NAME} PRIVATE SSDEEP::fuzzy)
endif()
if(APACHE_ROOT AND EXISTS "${APACHE_ROOT}/bin")
add_custom_command(TARGET ${IIS_MODULE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${APACHE_ROOT}/bin/libhttpd.dll"
$<TARGET_FILE_DIR:${IIS_MODULE_NAME}>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${APACHE_ROOT}/bin/libaprutil-1.dll"
$<TARGET_FILE_DIR:${IIS_MODULE_NAME}>
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${APACHE_ROOT}/bin/libapriconv-1.dll"
$<TARGET_FILE_DIR:${IIS_MODULE_NAME}>
COMMENT "Copying Apache DLLs to output directory"
)
else()
message(WARNING "APACHE_ROOT is not defined or path does not exist. Current APACHE_ROOT: '${APACHE_ROOT}'. Please set APACHE_ROOT to the Apache installation directory.")
endif()
if(WITH_SSDEEP AND SSDEEP_ROOT AND EXISTS "${SSDEEP_ROOT}/bin/fuzzy.dll")
add_custom_command(TARGET ${IIS_MODULE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${SSDEEP_ROOT}/bin/fuzzy.dll"
$<TARGET_FILE_DIR:${IIS_MODULE_NAME}>
COMMENT "Copying SSDEEP DLL to output directory"
)
endif()
# Install target - copy to release files directory
install(TARGETS ${IIS_MODULE_NAME}
RUNTIME DESTINATION .
LIBRARY DESTINATION .
)
if(APACHE_ROOT AND EXISTS "${APACHE_ROOT}/bin")
install(FILES
"${APACHE_ROOT}/bin/libhttpd.dll"
"${APACHE_ROOT}/bin/libaprutil-1.dll"
"${APACHE_ROOT}/bin/libapriconv-1.dll"
DESTINATION .
)
endif()
if(WITH_SSDEEP AND SSDEEP_ROOT AND EXISTS "${SSDEEP_ROOT}/bin/fuzzy.dll")
install(FILES
"${SSDEEP_ROOT}/bin/fuzzy.dll"
DESTINATION .
)
endif()
# Also install the PDB file if it's generated
install(FILES $<TARGET_PDB_FILE:${IIS_MODULE_NAME}> DESTINATION . OPTIONAL)2. Windows CI/CD
name: CI/CD for IIS Module
on:
push:
branches:
- v2/test-ci-windows
pull_request:
branches:
- v2/test-ci-windows
jobs:
build:
strategy:
matrix:
arch: [x86, x64]
runs-on: windows-latest
# For Caching
permissions:
actions: read
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Install Apache for x86
if: matrix.arch == 'x86'
shell: pwsh
run: |
$apachePath = "${{ github.workspace }}\apache-x86"
New-Item -ItemType Directory -Path $apachePath -Force
choco install apache-httpd -y --force --forcex86 --no-progress -r --params="'/installLocation:$apachePath /noService'"
echo "APACHE_ROOT=$apachePath\Apache24" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Set Apache path for x64
if: matrix.arch == 'x64'
shell: pwsh
run: |
echo "APACHE_ROOT=C:\tools\Apache24" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
# - name: Setup MSYS2
# uses: msys2/setup-msys2@v2
# with:
# msystem: ${{ matrix.arch == 'x86' && 'MINGW32' || 'UCRT64' }}
# update: true
# install: >
# git
# make
# autoconf
# automake
# libtool
# ${{ matrix.arch == 'x86' && 'mingw-w64-i686-gcc' || 'mingw-w64-ucrt-x86_64-gcc' }}
# ${{ matrix.arch == 'x86' && 'mingw-w64-i686-pkg-config' || 'mingw-w64-ucrt-x86_64-pkg-config' }}
# - name: Clone and build ssdeep
# shell: msys2 {0}
# run: |
# MSYS2_WORKSPACE=$(cygpath -u '${{ github.workspace }}')
# echo "Converted workspace path: $MSYS2_WORKSPACE"
# git clone https://github.com/ssdeep-project/ssdeep.git --depth 1
# cd ssdeep
# autoreconf -i
# if [ "${{ matrix.arch }}" = "x86" ]; then
# ./configure --enable-shared --disable-static CFLAGS="-O3" CXXFLAGS="-O3" --build=i686-pc-mingw32
# else
# ./configure --enable-shared --disable-static CFLAGS="-O3" CXXFLAGS="-O3"
# fi
# make dll
# mkdir -p "${MSYS2_WORKSPACE}/ssdeep-install-${{ matrix.arch }}/bin"
# mkdir -p "${MSYS2_WORKSPACE}/ssdeep-install-${{ matrix.arch }}/include"
# cp -v fuzzy.dll "${MSYS2_WORKSPACE}/ssdeep-install-${{ matrix.arch }}/bin/"
# cp -v fuzzy.h "${MSYS2_WORKSPACE}/ssdeep-install-${{ matrix.arch }}/include/"
# cp -v fuzzy.def "${MSYS2_WORKSPACE}/ssdeep-install-${{ matrix.arch }}/"
- name: Restore vcpkg cache
id: vcpkg-cache
uses: TAServers/vcpkg-cache@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
prefix: vcpkg-iis-module-${{ matrix.arch }}/
- name: Configure CMake for IIS Module
env:
VCPKG_FEATURE_FLAGS: "binarycaching"
VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite"
VCPKG_DEFAULT_TRIPLET: ${{ matrix.arch }}-windows
run: |
$archFlag = "${{ matrix.arch }}"
$cmakeArch = if ($archFlag -eq "x86") { "Win32" } else { "x64" }
$installDir = if ($archFlag -eq "x86") { "x86" } else { "amd64" }
cmake `
-DAPACHE_ROOT="$env:APACHE_ROOT" `
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}\iis\release\$installDir" `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" `
-A $cmakeArch `
-DWITH_LUA=ON `
-DWITH_YAJL=ON `
-S IIS -B "iis\build-${{ matrix.arch }}"
# -DSSDEEP_ROOT="${{ github.workspace }}\ssdeep-install-${{ matrix.arch }}" `
# -DWITH_SSDEEP=ON `
- name: Build IIS Module
shell: pwsh
run: |
cmake --build "iis\build-${{ matrix.arch }}" --config Release
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: iis-module-${{ matrix.arch }}
path: iis/build-${{ matrix.arch }}/Release/
package:
needs: build
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download x64 artifacts
uses: actions/download-artifact@v4
with:
name: iis-module-x64
path: iis/release/amd64/
- name: Download x86 artifacts
uses: actions/download-artifact@v4
with:
name: iis-module-x86
path: iis/release/x86/
- name: Generate MSI files
shell: pwsh
run: |
heat dir "iis\release\amd64" -cg ModSec64Components -dr inetsrv64 -gg -sreg -srd -var var.ModSecurityIISRelease64 -out "iis\ModSec64.wxs"
heat dir "iis\release\x86" -cg ModSec32Components -dr inetsrv32 -gg -sreg -srd -var var.ModSecurityIISRelease32 -out "iis\ModSec32.wxs"
candle.exe -ext WixUtilExtension -ext WixUIExtension "iis\installer.wxs" "iis\ModSec64.wxs" -arch x64 -dModSecurityIISRelease64="iis\release\amd64\" -out iis\
candle.exe -ext WixUtilExtension -ext WixUIExtension "iis\ModSec32.wxs" -arch x86 -dModSecurityIISRelease32="iis\release\x86\" -out iis\
light.exe -ext WixUtilExtension -ext WixUIExtension "iis\installer.wixobj" "iis\ModSec32.wixobj" "iis\ModSec64.wixobj" -out "iis\modsecurityiis.msi"
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: modsecurityiis-installers
path: iis/modsecurityiis.msi
test:
needs: package
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download MSI files
uses: actions/download-artifact@v4
with:
name: modsecurityiis-installers
path: ${{ github.workspace }}\
- name: Install MSI
shell: pwsh
run: |
$msiPath = "${{ github.workspace }}\modsecurityiis.msi"
if (-not (Test-Path $msiPath)) {
Write-Error "MSI file not found at $msiPath"
exit 1
}
# Install with logging for debugging
$installLog = "${{ github.workspace }}\install.log"
$installResult = Start-Process -FilePath "msiexec.exe" -ArgumentList @(
"/i", "`"$msiPath`"",
"/qn",
"/norestart",
"/l*", "`"$installLog`""
) -Wait -PassThru
if ($installResult.ExitCode -ne 0) {
Write-Error "MSI installation failed with exit code $($installResult.ExitCode)"
Get-Content $installLog | Write-Host
exit 1
}
$installDir = "C:\Program Files\ModSecurity IIS"
$requiredFiles = @(
"modsecurity.conf",
"modsecurity_iis.conf"
)
foreach ($file in $requiredFiles) {
$filePath = Join-Path $installDir $file
if (-not (Test-Path $filePath)) {
Write-Error "Required file $file not found in installation directory"
exit 1
}
}
- name: Install OWASP Core Rules
shell: pwsh
run: |
$crsVersion = "v4.18.0"
$crsUrl = "https://github.com/coreruleset/coreruleset/archive/refs/tags/$crsVersion.tar.gz"
$crsDir = "C:\Program Files\ModSecurity IIS\coreruleset"
$modSecurityConfigDir = "C:\Program Files\ModSecurity IIS"
try {
New-Item -ItemType Directory -Path $crsDir -Force
Invoke-WebRequest -Uri $crsUrl -OutFile "$crsDir\$crsVersion.tar.gz"
tar -xzf "$crsDir\$crsVersion.tar.gz" -C $crsDir --strip-components=1
Get-ChildItem "$crsDir" -Recurse -Filter "*.example" | ForEach-Object {
$newName = $_.Name.Replace(".example", "")
Rename-Item -Path $_.FullName -NewName $newName
}
$modSecurityConfigFile = "$modSecurityConfigDir\modsecurity_iis.conf"
$crsRules = @(
"Include coreruleset/crs-setup.conf",
"Include coreruleset/rules/*.conf",
"Include coreruleset/plugins/*-config.conf",
"Include coreruleset/plugins/*-before.conf",
"Include coreruleset/rules/*.conf",
"Include coreruleset/plugins/*-after.conf"
)
Add-Content -Path $modSecurityConfigFile -Value $crsRules
(Get-Content -Path $modSecurityConfigDir\modsecurity.conf) -replace 'SecRuleEngine DetectionOnly', 'SecRuleEngine On' | Set-Content -Path $modSecurityConfigDir\modsecurity.conf
}
catch {
Write-Error "Failed to install OWASP Core Rules: $($_.Exception.Message)"
exit 1
}
- name: Test IIS Module
shell: pwsh
run: |
$iisConfigDir = "C:\Program Files\ModSecurity IIS\"
Restart-Service W3SVC -Force
$modules = & "$env:SystemRoot\system32\inetsrv\appcmd.exe" list modules
if ($LASTEXITCODE -ne 0) {
Write-Error "appcmd failed with exit code $LASTEXITCODE"
exit 1
}
if (-not ($modules -match "ModSecurity")) {
Write-Error "ModSecurity module not found in IIS modules"
Write-Host "IIS modules: $modules"
exit 1
}
$testCases = @(
@{Url = "http://localhost/"; Description = "Normal request"; ExpectedCode = 200},
@{Url = "http://localhost/?id=1' OR '1'='1"; Description = "SQL injection attempt"; ExpectedCode = 403},
@{Url = "http://localhost/?q=<script>alert('test')</script>"; Description = "XSS attempt"; ExpectedCode = 403}
)
foreach ($test in $testCases) {
try {
$response = Invoke-WebRequest $test.Url -UseBasicParsing -SkipHttpErrorCheck -TimeoutSec 30
if ($response.StatusCode -eq $test.ExpectedCode) {
Write-Host "PASS: $($test.Description) - returned $($response.StatusCode)"
}
else {
Write-Host "FAIL: $($test.Description) - expected $($test.ExpectedCode) but got $($response.StatusCode)"
}
}
catch {
Write-Host "ERROR: $($test.Description) - request failed: $($_.Exception.Message)"
}
}
# Check event log
$badMessagePattern = 'Failed to find the RegisterModule entrypoint|The description for Event ID|The data is the error|dll failed to load'
$events = Get-EventLog -LogName Application -Newest 100 |
Where-Object { $_.Message -match $badMessagePattern } |
Where-Object { $_.Source -match 'IIS|W3SVC|mscor|IIS-W3SVC|IIS-W3WP|ModSecurity' }
if ($events -and $events.Count -gt 0) {
Write-Host '::error:: Found errors in event log'
$events | Select-Object TimeGenerated, Source, EntryType, EventID, Message | Format-List
Exit 1
}
Get-EventLog -LogName Application -Source ModSecurity -Newest 10See:
https://github.com/A13501350/ModSecurity/actions/runs/17894429285
Hi @A13501350,
I implemented a CMake-based build system and GitHub Actions CI/CD pipeline for the ModSecurity IIS module on Windows.
This is AWESOME!
Many thanks for this, I try to add it to our CI soon!
Thanks again!
Glad you found it useful. This implementation is more of a proof-of-concept, especially the CMake scripts. There are a few additional elements needed for full functionality, such as a proper vcpkg.json manifest file and some modifications to the installer.wxs WiX configuration.
Glad you found it useful. This implementation is more of a proof-of-concept, especially the CMake scripts. There are a few additional elements needed for full functionality, such as a proper vcpkg.json manifest file and some modifications to the installer.wxs WiX configuration.
Thanks. I reviewed that branch and I think that would be very useful. As I see this file does the tasks, right? And you add not just the IIS module build and test but Apache module too, right?
Please send that as a PR (after you remove the commented (unnecessary) lines). Thanks!
I'm not sure what you mean by the Apache module. I only added the build and test for the IIS module, along with some related modifications, of course.
I'm not sure what you mean by the Apache module. I only added the build and test for the IIS module, along with some related modifications, of course.
Sorry, may be this line confused me, because here you (probably) install Apache2. But may be it needs only because it's necessary to build IIS module?
I am not following the above with reagrds to a fix, but I am getting "Incorrect function" all the time in events for modsecurity.
how can I fix this please
I'm not sure what you mean by the Apache module. I only added the build and test for the IIS module, along with some related modifications, of course.
Sorry, may be this line confused me, because here you (probably) install Apache2. But may be it needs only because it's necessary to build IIS module?
Yes. The IIS module for ModSecurity v2 essentially wraps the core ModSecurity components, which requires the Apache libraries.