From f2f0ee10e219b969438721f83061dbc5044ffb8b Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Thu, 22 Oct 2026 12:40:21 +0100 Subject: [PATCH] Updates to scanner --- .../UserInterfaceState.xcuserstate | Bin 12576 -> 12779 bytes PHPGuard/ContentView.swift | 6 ++- PHPGuard/Models.swift | 4 ++ PHPGuard/SSHScanner.swift | 33 ++++++++++++- README.md | 46 +++++------------- 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/PHPGuard.xcodeproj/project.xcworkspace/xcuserdata/markgregory.xcuserdatad/UserInterfaceState.xcuserstate b/PHPGuard.xcodeproj/project.xcworkspace/xcuserdata/markgregory.xcuserdatad/UserInterfaceState.xcuserstate index 0f245f4dac05fa97d622b1f0cc2c6811b7430dae..f20686ed3893f11602c4eecc6b40dbe1553be995 100644 GIT binary patch delta 5839 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 S-whB_ey5Ae?-l=}r~V(B{}G}9 delta 5796 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 diff --git a/PHPGuard/ContentView.swift b/PHPGuard/ContentView.swift index 17c2cab..1ff8f65 100644 --- a/PHPGuard/ContentView.swift +++ b/PHPGuard/ContentView.swift @@ -64,7 +64,7 @@ struct ContentView: View { stat("Scanned", "\(Set(store.results.filter { $0.error == nil }.map(\.serverID)).count)", "checkmark.shield") stat("Findings", "\(store.results.filter { $0.error == nil }.prefix(store.servers.count).reduce(0) { $0 + $1.findings.count })", "exclamationmark.triangle") } - Text("Audits are read-only. CVE matching, package backport verification, Docker inspection, and compromise detection are not implemented in this initial build.") + Text("Read-only PHP inventory, APT upgrade checks and running Docker container discovery. CVE matching and vendor backport verification remain unimplemented; no server is certified secure.") .foregroundStyle(.secondary) Button("Scan all servers") { store.scanAll() }.buttonStyle(.borderedProminent).disabled(store.servers.isEmpty) Spacer() @@ -100,6 +100,10 @@ struct ContentView: View { dataSection("PHP-FPM services", result.fpmServices) dataSection("Nginx FastCGI routes", result.nginxUpstreams) dataSection("Installed packages", result.packageVersions) + dataSection("Available PHP package upgrades", result.availableUpdates) + Text("Package assessment: \(result.packageAuditStatus)").foregroundStyle(.secondary) + dataSection("PHP detected in running Docker containers", result.dockerPHP) + Text("Docker assessment: \(result.dockerAuditStatus)").foregroundStyle(.secondary) Text("Findings").font(.title2.bold()) ForEach(result.findings) { finding in VStack(alignment: .leading, spacing: 4) { diff --git a/PHPGuard/Models.swift b/PHPGuard/Models.swift index 18b7eec..54b52f4 100644 --- a/PHPGuard/Models.swift +++ b/PHPGuard/Models.swift @@ -27,6 +27,10 @@ struct AuditResult: Codable, Identifiable { var fpmServices: [String] = [] var nginxUpstreams: [String] = [] var packageVersions: [String] = [] + var availableUpdates: [String] = [] + var dockerPHP: [String] = [] + var packageAuditStatus = "Not checked" + var dockerAuditStatus = "Not checked" var findings: [Finding] = [] var rawOutput = "" var error: String? = nil diff --git a/PHPGuard/SSHScanner.swift b/PHPGuard/SSHScanner.swift index 7a06ad1..6037d79 100644 --- a/PHPGuard/SSHScanner.swift +++ b/PHPGuard/SSHScanner.swift @@ -22,6 +22,27 @@ enum SSHScanner { printf '===FPM===\n'; systemctl list-units --all --type=service --no-legend --no-pager 2>/dev/null | grep -E 'php[0-9.]+-fpm' || true printf '===NGINX===\n'; grep -Rh 'fastcgi_pass' /etc/nginx/sites-enabled/ 2>/dev/null | sort -u || true printf '===PACKAGES===\n'; dpkg-query -W -f='${Package} ${Version}\n' 'php*-fpm' 'php*-cli' 2>/dev/null || true + printf '===UPDATES===\n' + if command -v apt >/dev/null 2>&1; then + apt list --upgradable 2>/dev/null | grep -E '^php[^ /]*/' || true + else + printf 'Package manager not supported\n' + fi + printf '===DOCKER===\n' + if command -v docker >/dev/null 2>&1; then + ids=$(docker ps -q 2>/dev/null) + if [ -z "$ids" ]; then + if docker info >/dev/null 2>&1; then printf 'No running containers\n'; else printf 'Docker access unavailable\n'; fi + else + for id in $ids; do + name=$(docker inspect --format '{{.Name}}' "$id" 2>/dev/null | sed 's,^/,,') + version=$(docker exec "$id" sh -c 'php -r '''echo PHP_VERSION;''' 2>/dev/null' 2>/dev/null) + if [ -n "$version" ]; then printf '%s %s\n' "$name" "$version"; fi + done + fi + else + printf 'Docker not installed\n' + fi printf '===END===\n' """# @@ -81,6 +102,10 @@ enum SSHScanner { result.fpmServices = fields["FPM"] ?? [] result.nginxUpstreams = fields["NGINX"] ?? [] result.packageVersions = fields["PACKAGES"] ?? [] + result.availableUpdates = fields["UPDATES"] ?? [] + result.dockerPHP = (fields["DOCKER"] ?? []).filter { !$0.hasPrefix("Docker ") && $0 != "No running containers" } + result.packageAuditStatus = result.availableUpdates.contains("Package manager not supported") ? "Unsupported package manager" : "APT upgrade inventory (not a security advisory assessment)" + result.dockerAuditStatus = fields["DOCKER"]?.first(where: { $0.hasPrefix("Docker ") || $0 == "No running containers" }) ?? "Running containers inspected where accessible" if result.phpVersions.isEmpty { result.findings.append(Finding(severity: "Info", title: "No PHP CLI detected", detail: "No PHP binary found in standard paths; this does not exclude bundled runtimes or containers.")) } @@ -91,7 +116,13 @@ enum SSHScanner { result.findings.append(Finding(severity: "Warning", title: "Legacy PHP detected", detail: "\(line). Verify vendor support and distribution security maintenance.")) } } - result.findings.append(Finding(severity: "Info", title: "CVE assessment not performed", detail: "Installed package versions must be checked against current vendor advisories and backported fixes. A version string alone cannot establish vulnerability.")) + for line in result.availableUpdates where line.hasPrefix("php") && line.contains("/") { + result.findings.append(Finding(severity: "Warning", title: "PHP package update available", detail: line + ". Review the package changelog and vendor security notice before classifying CVE exposure.")) + } + for line in result.dockerPHP { + result.findings.append(Finding(severity: "Info", title: "PHP runtime in Docker", detail: line + ". Container image security status requires a separate image/advisory assessment.")) + } + result.findings.append(Finding(severity: "Info", title: "CVE status unverified", detail: "Version strings and available upgrades do not establish CVE exposure. Ubuntu backports, package origins, and current vendor advisories require independent verification. No server is labelled secure based on this scan.")) return result } } diff --git a/README.md b/README.md index b93a9f1..310d403 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,18 @@ -# PHPGuard — macOS PHP Inventory Auditor (Phase 1) +# PHPGuard — Phase 2 -Native SwiftUI macOS application for **read-only** PHP inventory scans over SSH. +Native SwiftUI macOS application for **read-only** SSH inventory of PHP installations on Linux servers. -## Open and run +## New in Phase 2 +- APT PHP package upgrade inventory (no automatic updates) +- PHP version discovery inside accessible running Docker containers +- Per-server findings and status of package/Docker checks +- Existing SSH inventory, scan history, and JSON export retained -1. Unzip and open `PHPGuard.xcodeproj` in Xcode 16 or later. -2. Select the PHPGuard scheme and **My Mac** destination. -3. Set your signing team in Signing & Capabilities if required. -4. Build and Run (⌘R). -5. Add a server (SSH hostname, username, port and optional identity file). -6. Ensure you can first connect from Terminal using SSH and that its host key is in `~/.ssh/known_hosts`. -7. Click **Scan now**. Scan history and JSON export are available. +## Security interpretation +**This is not a CVE scanner yet.** Available upgrades are not necessarily security updates; the absence of upgrades is not evidence of safety. Vendor backports and security advisories must be checked before labelling an installation vulnerable or patched. Docker inspection only checks running containers with a `php` executable accessible on PATH. No compromise detection is performed. -The app uses `/usr/bin/ssh` with `BatchMode=yes` and strict host key checking. It does not store passwords or change remote systems. SSH keys must already be configured. For the initial release, the app runs unsandboxed so it can invoke the system SSH executable and access existing SSH configuration. +## Open in Xcode +Unzip the archive, open `PHPGuard/PHPGuard.xcodeproj`, select My Mac, and build. macOS 15+; the build has not been verified in Xcode in this environment. -## Implemented - -- Native macOS SwiftUI navigation and dashboard -- Add/edit/remove SSH servers; optional private-key path -- Concurrent read-only scans of PHP CLI binaries, PHP-FPM services, Nginx FastCGI routes, Debian/Ubuntu PHP package inventory -- Local JSON persistence and history -- JSON report export -- SSH error reporting and strict host key checking - -## Not yet implemented - -- Live CVE feeds, Ubuntu package backport verification or definitive vulnerability status -- Docker and Apache scans -- Intrusion detection and web-log investigation -- PDF reports, scheduled scans, notifications, automated patching -- Integration with PegasusMonitor - -**Important:** This version is an inventory tool, not a complete vulnerability or compromise scanner. It intentionally does not label a server secure solely from a version string. - -## Privacy - -Server definitions and scan results are stored in `~/Library/Application Support/PHPGuard/`. Scan reports may contain internal hostnames and package inventory. Do not publish them publicly. +## SSH +The app invokes `/usr/bin/ssh` with BatchMode and StrictHostKeyChecking. Configure working key-based SSH access in Terminal first. Remote scripts are read-only; APT's upgrade listing may refresh no metadata and uses currently cached package lists. Docker access depends on remote user permissions.