chore: update Windows build to use PCRE2 as default #65
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD for IIS Module | |
| on: | |
| push: | |
| pull_request: | |
| jobs: | |
| build: | |
| strategy: | |
| matrix: | |
| arch: [x64, x86] | |
| config: [Release, RelWithDebInfo] | |
| 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@4f806de0a5a7294ffabaff804b38a9b435a73bda #v2.30.0 | |
| 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 }}') | |
| 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/" | |
| cp -v fuzzy.dll "${MSYS2_WORKSPACE}/ssdeep-install/" | |
| cp -v fuzzy.h "${MSYS2_WORKSPACE}/ssdeep-install/" | |
| cp -v fuzzy.def "${MSYS2_WORKSPACE}/ssdeep-install/" | |
| - name: Restore vcpkg cache | |
| id: vcpkg-cache | |
| uses: TAServers/vcpkg-cache@e848939f754daf406a06006be2e05eb5b17cc481 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| prefix: vcpkg-iis-module-${{ matrix.arch }}/ | |
| - uses: ammaraskar/msvc-problem-matcher@1ebcb382869bfdc2cc645e8a2a43b6d319ea1cc0 | |
| - 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" ` | |
| -DSSDEEP_ROOT="${{ github.workspace }}\ssdeep-install" ` | |
| -DWITH_SSDEEP=ON ` | |
| -A $cmakeArch ` | |
| -DWITH_LUA=ON ` | |
| -DWITH_YAJL=ON ` | |
| -DWITH_PCRE_JIT=ON ` | |
| -S IIS -B "iis\build" | |
| - name: Build IIS Module | |
| shell: pwsh | |
| run: | | |
| cmake --build "iis\build" --config ${{ matrix.config }} | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: iis-module-${{ matrix.arch }}-${{ matrix.config }} | |
| path: iis/build/${{ matrix.config }}/ | |
| package: | |
| needs: build | |
| runs-on: windows-latest | |
| strategy: | |
| matrix: | |
| config: [Release, RelWithDebInfo] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Download x64 artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: iis-module-x64-${{ matrix.config }} | |
| path: iis/release/amd64/ | |
| - name: Download x86 artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: iis-module-x86-${{ matrix.config }} | |
| 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-${{ matrix.config }} | |
| path: iis/modsecurityiis.msi | |
| test: | |
| needs: package | |
| runs-on: windows-latest | |
| strategy: | |
| matrix: | |
| config: [Release, RelWithDebInfo] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Download MSI files | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: modsecurityiis-installers-${{ matrix.config }} | |
| path: ${{ github.workspace }}/ | |
| - name: Install VC++ 2019 Redistributables (Prerequisites) | |
| shell: pwsh | |
| run: | | |
| Write-Host "=== Checking Pre-Existing VC++ Installations ===" | |
| Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue | | |
| Where-Object { $_.DisplayName -like "*Visual C++*" -or $_.DisplayName -like "*Microsoft Visual Studio*Runtime*" } | | |
| Select-Object DisplayName, DisplayVersion, PSChildName | | |
| Format-Table -AutoSize | |
| Write-Host "`n=== Installing Visual C++ 2019 Redistributable (x64) ===" | |
| $vc_x64_url = "https://aka.ms/vs/17/release/vc_redist.x64.exe" | |
| $vc_x64_installer = "${{ github.workspace }}\vc_redist.x64.exe" | |
| Invoke-WebRequest -Uri $vc_x64_url -OutFile $vc_x64_installer | |
| $x64Process = Start-Process -FilePath $vc_x64_installer -ArgumentList "/install", "/quiet", "/norestart" -Wait -PassThru | |
| Write-Host "VC++ x64 installer exit code: $($x64Process.ExitCode)" | |
| # Exit codes: 0 = success, 1638 = already installed (newer version), 3010 = success (reboot required) | |
| if ($x64Process.ExitCode -notin @(0, 1638, 3010)) { | |
| Write-Error "VC++ x64 installation failed with exit code $($x64Process.ExitCode)" | |
| exit 1 | |
| } | |
| Write-Host "`n=== Installing Visual C++ 2019 Redistributable (x86) ===" | |
| $vc_x86_url = "https://aka.ms/vs/17/release/vc_redist.x86.exe" | |
| $vc_x86_installer = "${{ github.workspace }}\vc_redist.x86.exe" | |
| Invoke-WebRequest -Uri $vc_x86_url -OutFile $vc_x86_installer | |
| $x86Process = Start-Process -FilePath $vc_x86_installer -ArgumentList "/install", "/quiet", "/norestart" -Wait -PassThru | |
| Write-Host "VC++ x86 installer exit code: $($x86Process.ExitCode)" | |
| # Exit codes: 0 = success, 1638 = already installed (newer version), 3010 = success (reboot required) | |
| if ($x86Process.ExitCode -notin @(0, 1638, 3010)) { | |
| Write-Error "VC++ x86 installation failed with exit code $($x86Process.ExitCode)" | |
| exit 1 | |
| } | |
| Write-Host "VC++ 2019 Redistributables installed successfully" | |
| # Verify installations | |
| Write-Host "`nVerifying VC++ installations in registry..." | |
| $vc142x64 = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" -Name "Installed" -ErrorAction SilentlyContinue).Installed | |
| # On 64-bit Windows, x86 runtime is in WOW6432Node | |
| $vc142x86 = (Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" -Name "Installed" -ErrorAction SilentlyContinue).Installed | |
| Write-Host "VC++ 2019 x64 in registry: $vc142x64" | |
| Write-Host "VC++ 2019 x86 in registry: $vc142x86" | |
| if (-not $vc142x64 -or -not $vc142x86) { | |
| Write-Error "VC++ redistributables not properly registered. x64=$vc142x64, x86=$vc142x86" | |
| exit 1 | |
| } | |
| - name: Install MSI | |
| shell: pwsh | |
| run: | | |
| Write-Host "=== Pre-Installation Debug Info ===" | |
| # Check MSI file | |
| $msiPath = "${{ github.workspace }}\modsecurityiis.msi" | |
| if (-not (Test-Path $msiPath)) { | |
| Write-Error "MSI file not found at $msiPath" | |
| exit 1 | |
| } | |
| Write-Host "MSI file found: $msiPath" | |
| Write-Host "MSI file size: $((Get-Item $msiPath).Length) bytes" | |
| # Check IIS version | |
| $iisVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\InetStp" -Name "MajorVersion" -ErrorAction SilentlyContinue).MajorVersion | |
| Write-Host "IIS Version: $iisVersion" | |
| # Check VC++ redistributables | |
| $vc142x64 = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" -Name "Installed" -ErrorAction SilentlyContinue).Installed | |
| $vc142x86 = (Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" -Name "Installed" -ErrorAction SilentlyContinue).Installed | |
| Write-Host "VC++ 2019 x64 installed: $vc142x64" | |
| Write-Host "VC++ 2019 x86 installed: $vc142x86" | |
| Write-Host "`n=== Starting MSI Installation ===" | |
| # Install with verbose logging | |
| $installLog = "${{ github.workspace }}\install.log" | |
| $installResult = Start-Process -FilePath "msiexec.exe" -ArgumentList @( | |
| "/i", "`"$msiPath`"", | |
| "/qn", | |
| "/norestart", | |
| "/l*vx", "`"$installLog`"" | |
| ) -Wait -PassThru | |
| Write-Host "Installation process completed with exit code: $($installResult.ExitCode)" | |
| # Check if log file was created | |
| if (Test-Path $installLog) { | |
| $logSize = (Get-Item $installLog).Length | |
| Write-Host "Install log created: $installLog ($logSize bytes)" | |
| Write-Host "`n=== Installation Log Contents ===" | |
| if ($logSize -gt 0) { | |
| Get-Content $installLog -Raw | Write-Host | |
| } else { | |
| Write-Host "WARNING: Log file is empty!" | |
| } | |
| } else { | |
| Write-Host "WARNING: Install log was not created at $installLog" | |
| } | |
| if ($installResult.ExitCode -ne 0) { | |
| Write-Host "`n=== Installation Failed ===" | |
| Write-Host "Exit code: $($installResult.ExitCode)" | |
| Write-Host "Common MSI error codes:" | |
| Write-Host " 1603 - Fatal error during installation" | |
| Write-Host " 1619 - Package could not be opened" | |
| Write-Host " 1620 - Package could not be opened (corrupt)" | |
| Write-Host " 1633 - Platform not supported" | |
| Write-Error "MSI installation failed with exit code $($installResult.ExitCode)" | |
| exit 1 | |
| } | |
| Write-Host "`n=== Installation Successful ===" | |
| $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 CRS | |
| shell: pwsh | |
| run: | | |
| $crsVersion = "4.23.0" | |
| $crsUrl = "https://github.com/coreruleset/coreruleset/archive/refs/tags/v$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/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 | |
| Write-Host "IIS modules: $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 | Format-List | |
| - name: Install go-ftw | |
| shell: pwsh | |
| run: | | |
| go install github.com/coreruleset/go-ftw@latest | |
| # Certain rules are disabled due to specific IIS behavior patterns. | |
| # Using go-ftw in cloud mode as the IIS connector does not generate logs in file format. | |
| # Technically, Event logs can be streamed to files, but this requires implementing rate limits to avoid log overflow. | |
| - name: Test ModSecurity Rules | |
| shell: pwsh | |
| run: | | |
| $testRuleDir = "C:\Program Files\ModSecurity IIS\coreruleset\tests\regression\tests" | |
| $goBinPath = "" | |
| if ($env:GOBIN) { | |
| $goBinPath = $env:GOBIN | |
| } elseif ($env:GOPATH) { | |
| $goBinPath = Join-Path $env:GOPATH "bin" | |
| } else { | |
| $goBinPath = Join-Path $env:USERPROFILE "go\bin" | |
| } | |
| & "$goBinPath\go-ftw.exe" run -d $testRuleDir --cloud -e "920100-2$|920100-4$|920100-8$|920100-12$|920272-5$|920290-1$|920620-1$|920380-1$" --show-failures-only | |