name: PR-tests on: pull_request: branches: - master - main paths-ignore: - '.github/**' workflow_dispatch: jobs: Build_and_test_winpeas_pr: runs-on: windows-latest # environment variables env: Solution_Path: 'winPEAS\winPEASexe\winPEAS.sln' Configuration: 'Release' steps: # checkout - name: Checkout uses: actions/checkout@v5 with: ref: ${{ github.head_ref || github.ref_name }} - name: Download regexes run: | powershell.exe -ExecutionPolicy Bypass -File build_lists/download_regexes.ps1 # Add MSBuild to the PATH - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v2 # Setup NuGet - name: Setup NuGet.exe uses: nuget/setup-nuget@v2 # Restore the packages for testing - name: Restore the application run: nuget restore $env:Solution_Path # build - name: run MSBuild run: msbuild $env:Solution_Path /p:Configuration=$env:Configuration /p:UseSharedCompilation=false # Execute unit tests in the solution - name: Execute unit tests run: dotnet test $env:Solution_Path --configuration $env:Configuration # Build all versions - name: Build all versions run: | echo "build x64" msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="x64" /p:UseSharedCompilation=false echo "build x86" msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="x86" /p:UseSharedCompilation=false echo "build Any CPU" msbuild -m $env:Solution_Path /t:Rebuild /p:Configuration=$env:Configuration /p:Platform="Any CPU" /p:UseSharedCompilation=false - name: Execute winPEAS -h shell: pwsh run: | $Configuration = "Release" $exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe" if (Test-Path $exePath) { & $exePath -h } else { Write-Error "winPEAS.exe not found at $exePath" } - name: Execute winPEAS cloudinfo shell: pwsh run: | $Configuration = "Release" $exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe" if (Test-Path $exePath) { & $exePath cloudinfo } else { Write-Error "winPEAS.exe not found at $exePath" } - name: Execute winPEAS systeminfo shell: pwsh run: | $Configuration = "Release" $exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe" if (Test-Path $exePath) { & $exePath systeminfo } else { Write-Error "winPEAS.exe not found at $exePath" } - name: Execute winPEAS online package vulnerability lookup with mock shell: pwsh run: | $Configuration = "Release" $exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe" if (!(Test-Path $exePath)) { Write-Error "winPEAS.exe not found at $exePath" } $serverPath = Join-Path $env:RUNNER_TEMP "mock_host_checker.py" $bodyPath = Join-Path $env:RUNNER_TEMP "host_checker_body.json" $countPath = Join-Path $env:RUNNER_TEMP "host_checker_count.txt" @" from http.server import BaseHTTPRequestHandler, HTTPServer import json import sys import threading body_path = sys.argv[1] count_path = sys.argv[2] class Handler(BaseHTTPRequestHandler): def do_POST(self): length = int(self.headers.get("content-length", "0")) body = self.rfile.read(length).decode("utf-8", "replace") with open(body_path, "w", encoding="utf-8") as f: f.write(body) try: with open(count_path, "r", encoding="utf-8") as f: count = int((f.read() or "0").strip()) except FileNotFoundError: count = 0 with open(count_path, "w", encoding="utf-8") as f: f.write(str(count + 1)) vulns = [] for i in range(1, 56): severity = "low" if i % 10 == 0: severity = "critical" elif i % 10 == 1: severity = "high" elif i % 10 == 2: severity = "medium" vulns.append({ "name": f"pkg{i}", "version": f"1.{i}.0", "manager": "windows-registry", "severity": severity, "vulns": [f"CVE-2026-{10000 + i}"], }) response = { "malicious": False, "results": [], "package_vulnerabilities": { "checked": 300, "affected": 55, "vulnerable_packages": vulns, }, } data = json.dumps(response).encode("utf-8") self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) threading.Thread(target=self.server.shutdown, daemon=True).start() def do_GET(self): if self.path == "/ready": data = b"ready" self.send_response(200) self.send_header("Content-Type", "text/plain") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) return self.send_response(404) self.end_headers() def log_message(self, fmt, *args): pass HTTPServer(("127.0.0.1", 18768), Handler).serve_forever() "@ | Set-Content -Path $serverPath -Encoding utf8 $pythonCandidates = @( @{ FilePath = "python"; VersionArgs = @("--version"); ServerArgs = @($serverPath, $bodyPath, $countPath) }, @{ FilePath = "python3"; VersionArgs = @("--version"); ServerArgs = @($serverPath, $bodyPath, $countPath) }, @{ FilePath = "py"; VersionArgs = @("-3", "--version"); ServerArgs = @("-3", $serverPath, $bodyPath, $countPath) } ) $pythonCommand = $null foreach ($candidate in $pythonCandidates) { if (!(Get-Command $candidate.FilePath -ErrorAction SilentlyContinue)) { continue } & $candidate.FilePath @($candidate.VersionArgs) | Out-Null if ($LASTEXITCODE -eq 0) { $pythonCommand = $candidate break } } if (!$pythonCommand) { Write-Error "No Python interpreter found to launch the mock host-checker server." } $startInfo = [System.Diagnostics.ProcessStartInfo]::new() $startInfo.FileName = $pythonCommand.FilePath $startInfo.UseShellExecute = $false $startInfo.RedirectStandardOutput = $true $startInfo.RedirectStandardError = $true foreach ($arg in $pythonCommand.ServerArgs) { [void]$startInfo.ArgumentList.Add($arg) } $server = [System.Diagnostics.Process]::Start($startInfo) try { $serverReady = $false $deadline = (Get-Date).AddSeconds(15) while ((Get-Date) -lt $deadline) { if ($server.HasExited) { break } try { $readyResponse = Invoke-WebRequest -UseBasicParsing -TimeoutSec 1 -Uri "http://127.0.0.1:18768/ready" if ($readyResponse.StatusCode -eq 200) { $serverReady = $true break } } catch { } Start-Sleep -Milliseconds 250 } if (!$serverReady) { if (!$server.HasExited) { $server.Kill() [void]$server.WaitForExit(5000) } $stdout = $server.StandardOutput.ReadToEnd() $stderr = $server.StandardError.ReadToEnd() Write-Error "Mock host-checker server did not become ready before winPEAS execution. ExitCode=$($server.ExitCode)`nSTDOUT:`n$stdout`nSTDERR:`n$stderr" } $env:HACKTRICKS_HOST_CHECKER_URL = "http://127.0.0.1:18768/api/host-checker" $output = & $exePath applicationsinfo -vulnpackages notcolor 2>&1 | Out-String if ($output -notmatch "Online package vulnerabilities found: 55 vulnerable package\(s\), checked 300\.") { Write-Error "The package vulnerability summary was not printed as expected.`n$output" } $vulnLines = ([regex]::Matches($output, "(?m)^\s+- pkg\d+ ")).Count if ($vulnLines -ne 50) { Write-Error "Expected 50 vulnerable package lines, got $vulnLines.`n$output" } $firstVulnLine = [regex]::Match($output, "(?m)^\s+- pkg\d+ .*$").Value if ($firstVulnLine -notmatch "pkg10 .* \[Critical\]: CVE-2026-10010") { Write-Error "Expected critical package vulnerabilities to be printed first, got: $firstVulnLine`n$output" } $criticalIndex = $output.IndexOf("pkg10") $lowIndex = $output.IndexOf("- pkg3 ") if ($criticalIndex -lt 0 -or $lowIndex -lt 0 -or $criticalIndex -gt $lowIndex) { Write-Error "Expected critical packages to appear before low packages.`n$output" } if ($output -notmatch "\.\.\. 5 more vulnerable package\(s\) not shown\.") { Write-Error "The hidden vulnerable package count was not printed.`n$output" } if ($output -match "package_vulnerabilities") { Write-Error "Raw package_vulnerabilities JSON leaked into the rendered output.`n$output" } $requestCount = Get-Content $countPath -Raw if ([int]$requestCount.Trim() -ne 1) { Write-Error "Expected exactly one host-checker request, got $requestCount" } $request = Get-Content $bodyPath -Raw | ConvertFrom-Json if (!$request.hostname) { Write-Error "The host-checker payload did not include hostname." } if (!$request.packages -or $request.packages.Count -lt 1) { Write-Error "The host-checker payload did not include installed packages." } } finally { if ($server -and !$server.HasExited) { $server.Kill() [void]$server.WaitForExit(5000) } } - name: Execute winPEAS networkinfo shell: pwsh run: | $Configuration = "Release" $exePath = "winPEAS/winPEASexe/winPEAS/bin/$Configuration/winPEAS.exe" if (Test-Path $exePath) { & $exePath networkinfo } else { Write-Error "winPEAS.exe not found at $exePath" } Build_and_test_linpeas_pr: runs-on: ubuntu-latest steps: # Download repo - uses: actions/checkout@v5 with: ref: ${{ github.head_ref || github.ref_name }} # Setup go - uses: actions/setup-go@v6 with: go-version: '1.23' cache: false - run: go version # Build linpeas - name: Build linpeas run: | python3 -m pip install PyYAML cd linPEAS python3 -m builder.linpeas_builder --all --output linpeas_fat.sh python3 -m builder.linpeas_builder --all-no-fat --output linpeas.sh python3 -m builder.linpeas_builder --small --output linpeas_small.sh - name: Run linPEAS module metadata tests run: python3 -m unittest linPEAS.tests.test_modules_metadata - name: Run linPEAS builder tests run: python3 -m unittest discover -s linPEAS/tests -p "test_*.py" # Run linpeas help as quick test - name: Run linpeas help run: linPEAS/linpeas_fat.sh -h && linPEAS/linpeas.sh -h && linPEAS/linpeas_small.sh -h # Run linpeas as a test - name: Run linpeas system_information run: linPEAS/linpeas_fat.sh -o system_information -a - name: Run linpeas container run: linPEAS/linpeas_fat.sh -o container -a - name: Run linpeas cloud run: linPEAS/linpeas_fat.sh -o cloud -a - name: Run linpeas procs_crons_timers_srvcs_sockets run: linPEAS/linpeas_fat.sh -o procs_crons_timers_srvcs_sockets -a - name: Run linpeas network_information run: linPEAS/linpeas_fat.sh -o network_information -t -a - name: Run linpeas users_information run: linPEAS/linpeas_fat.sh -o users_information -a - name: Run linpeas software_information run: linPEAS/linpeas_fat.sh -o software_information -a - name: Run linpeas interesting_perms_files run: linPEAS/linpeas_fat.sh -o interesting_perms_files -a - name: Run linpeas interesting_files run: linPEAS/linpeas_fat.sh -o interesting_files -a Build_and_test_macpeas_pr: runs-on: macos-latest steps: # Download repo - uses: actions/checkout@v5 with: ref: ${{ github.head_ref || github.ref_name }} # Build linpeas (macpeas) - name: Build macpeas run: | python3 -m pip install PyYAML --break-system-packages python3 -m pip install requests --break-system-packages cd linPEAS python3 -m builder.linpeas_builder --all --output linpeas_fat.sh # Run linpeas help as quick test - name: Run macpeas help run: linPEAS/linpeas_fat.sh -h # Run macpeas parts to test it - name: Run macpeas system_information run: linPEAS/linpeas_fat.sh -o system_information -a # - name: Run macpeas container # run: linPEAS/linpeas_fat.sh -o container -a # - name: Run macpeas cloud # run: linPEAS/linpeas_fat.sh -o cloud -a # - name: Run macpeas procs_crons_timers_srvcs_sockets # run: linPEAS/linpeas_fat.sh -o procs_crons_timers_srvcs_sockets -a # - name: Run macpeas network_information # run: linPEAS/linpeas_fat.sh -o network_information -t -a # - name: Run macpeas users_information # run: linPEAS/linpeas_fat.sh -o users_information -a # - name: Run macpeas software_information # run: linPEAS/linpeas_fat.sh -o software_information -a