Updates to scanner with CVE saving and auto update

This commit is contained in:
Mark Gregory
2026-10-22 17:56:13 +01:00
parent d8878737fd
commit ee37674a2a
9 changed files with 106 additions and 22 deletions
+19 -3
View File
@@ -166,8 +166,8 @@ import SwiftUI
)
}
scanStage = "Checking Ubuntu advisories"
scanDetail = "Comparing CVEs with Ubuntu package advisories"
scanStage = "Checking vendor advisories"
scanDetail = "Verifying available vendor evidence; unsupported distributions remain unverified"
scanFraction = 0.95
if report.ubuntuReleaseID == "ubuntu" {
@@ -177,9 +177,25 @@ import SwiftUI
)
}
// NVD CPE matches are candidates, never proof of host exposure.
// Only Ubuntu has a vendor advisory adapter at present.
if report.ubuntuReleaseID != "ubuntu" {
all = all.map { candidate in
var item = candidate
item.vendorStatus = "Unverified"
item.vendorEvidence =
"No vendor advisory adapter for this distribution. " +
"NVD upstream version matches cannot establish package exposure."
return item
}
}
report.cveAssessments = all.sorted { $0.cveID > $1.cveID }
let verifiedCount = all.filter {
["Vulnerable", "Patched", "Not affected"].contains($0.vendorStatus ?? "")
}.count
report.cveAssessmentStatus = failures.isEmpty
? "NVD assessment complete; Ubuntu advisories checked where available"
? "NVD candidates: \(all.count); vendor-verified: \(verifiedCount). " +
"Unverified entries are not security clearance."
: "Partial/unknown: " + failures.joined(separator: "; ")
report.cveAssessedAt = Date()
}
+1 -1
View File
@@ -205,7 +205,7 @@ struct ContentView: View {
: "shield.lefthalf.filled"
)
Text("Ubuntu: " + vendorStatus)
Text("Vendor: " + vendorStatus)
.font(.subheadline.bold())
}
.foregroundStyle(
+12 -7
View File
@@ -20,6 +20,7 @@ enum SSHScanner {
. /etc/os-release
printf 'ID=%s\n' "${ID:-unknown}"
printf 'ID_LIKE=%s\n' "${ID_LIKE:-}"
printf 'PRETTY_NAME=%s\n' "${PRETTY_NAME:-}"
printf 'VERSION_ID=%s\n' "${VERSION_ID:-unknown}"
printf 'VERSION_CODENAME=%s\n' "${VERSION_CODENAME:-unknown}"
fi
@@ -69,7 +70,7 @@ enum SSHScanner {
apk)
apk info -v 2>/dev/null |
grep -E '^php[0-9]*-' |
grep -E '^php([0-9]+)?(-|[0-9])' |
sort -u || true
;;
@@ -119,11 +120,11 @@ enum SSHScanner {
while IFS= read -r entry; do
case "$entry" in php*) ;; *) continue ;; esac
# APK revisions can include suffixes such as _p1 and -r0.
# Keep the original package string if parsing is ambiguous.
name=$(printf '%s\n' "$entry" |
sed -E 's/-[0-9]+(\.[0-9]+)+(-r[0-9]+)?$//')
sed -E 's/-[0-9]+(\.[0-9]+)+([^[:space:]]*)$//')
version=${entry#"$name"-}
[ "$version" != "$entry" ] || continue
printf '%s|%s|%s|%s\n' \
@@ -557,9 +558,13 @@ enum SSHScanner {
} else {
result.dockerAuditStatus = "\(result.dockerPHP.count) PHP runtimes; \(noPHP.count) containers without PHP CLI (not a security finding)"
}
result.packageAuditStatus = result.packageOrigins.contains("APT metadata unavailable")
? "APT package provenance unavailable"
: "Installed and candidate package versions collected; vendor CVE fix status requires advisory verification"
let packageManager = fields["PACKAGE_MANAGER"]?.first ?? "unknown"
let metadataUnavailable = result.packageOrigins.contains {
$0.localizedCaseInsensitiveContains("unavailable")
}
result.packageAuditStatus = metadataUnavailable
? "\(packageManager.uppercased()) package provenance unavailable; CVE exposure unverified"
: "\(packageManager.uppercased()) inventory collected; vendor CVE status requires independent verification"
// Recommendations are derived from this server's collected evidence.
var actions: [String] = []
+15
View File
@@ -385,6 +385,21 @@ extension VulnerabilityService {
}
}
// A package from a third-party PPA or custom repository cannot be
// certified solely using the Ubuntu distribution advisory.
// Require positive repository provenance before issuing a verdict.
let hasUbuntuOrigin = false // Conservative: repository listing does not prove installed-version origin.
guard hasUbuntuOrigin else {
return assessments.map { original in
var item = original
item.vendorStatus = "Unverified"
item.vendorEvidence =
"Ubuntu archive provenance not established for installed PHP packages. " +
"Third-party or custom builds require independent verification."
return item
}
}
let installed = installedPackages(report.packageSources)
guard !installed.isEmpty else {
+9
View File
@@ -0,0 +1,9 @@
zip -r ~/Downloads/PHPGuard-Source.zip PHPGuard \
-x '*/.git/*' \
'*/.build/*' \
'*/DerivedData/*' \
'*/build/*' \
'*/dist/*' \
'*/.DS_Store' \
'*.dmg' \
'*.zip'
-11
View File
@@ -1,11 +0,0 @@
#!/bin/zsh
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
CONFIG="${CONFIGURATION:-Debug}"
DERIVED="${DERIVED_DATA_PATH:-$ROOT/.build}"
command -v xcodebuild >/dev/null || { echo "Xcode command line tools required" >&2; exit 1; }
xcodebuild -project "$ROOT/PHPGuard.xcodeproj" -scheme PHPGuard -configuration "$CONFIG" -derivedDataPath "$DERIVED" CODE_SIGNING_ALLOWED=NO build
APP="$DERIVED/Build/Products/$CONFIG/PHPGuard.app"
[[ -d "$APP" ]] || { echo "Built application not found: $APP" >&2; exit 1; }
echo "Built: $APP"
if [[ "${LAUNCH_AFTER_BUILD:-1}" == "1" ]]; then open "$APP"; fi
+1
View File
@@ -0,0 +1 @@
xcodebuild -list -project PHPGuard.xcodeproj
+22
View File
@@ -0,0 +1,22 @@
# PHPGuard security assessment readiness
## Status: not production-validated
The scan collects package inventory and upstream NVD PHP CVE *candidates*. A version match, lack of upgrades, or an available upgrade does not establish exposure or remediation.
### Verified statuses
- `Unverified` is the default for unsupported distribution/vendor combinations, unknown package provenance, absent advisories, and network failures.
- Ubuntu's existing advisory adapter is intentionally **fail closed** until installed package provenance is independently proven at the installed-version level. Seeing an Ubuntu repository in APT policy is insufficient: the installed package may be from a PPA or custom build.
- Docker PHP is not covered by host package advisories.
- NVD discovery is bounded and may omit CVEs; no-results is not a clean security bill.
### Before production release
1. Implement per-installed-version repository origin verification and test Ubuntu advisory response parsing against current fixtures.
2. Implement and test Debian, Red Hat-family, SUSE, Amazon Linux and Alpine advisory adapters using official vendor data and native package version comparison.
3. Add test fixtures for package manager outputs, package origin edge cases, CVE states, missing metadata, errors, and containers.
4. Run integration tests on each supported distribution with minimal and restricted SSH permissions.
5. Run Xcode build, UI checks, signing and notarization on macOS; this archive has not been built in Xcode here.
This package is a security-hardening development snapshot, **not a production release**.
+27
View File
@@ -0,0 +1,27 @@
"""Static safety regression tests; run with python3 -m unittest discover -s tests."""
from pathlib import Path
import subprocess
import unittest
ROOT = Path(__file__).resolve().parents[1]
class ScannerTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
source = (ROOT / 'PHPGuard/SSHScanner.swift').read_text()
cls.script = source.split('static let script = #"""',1)[1].split('"""#',1)[0]
def test_posix_shell_syntax(self):
p = subprocess.run(['sh','-n'],input=self.script,text=True,capture_output=True)
self.assertEqual(p.returncode,0,p.stderr)
def test_required_sections(self):
for section in ('OS_RELEASE','PACKAGE_MANAGER','PACKAGES','PACKAGE_SOURCES','UPDATES','PACKAGE_ORIGINS','DOCKER','END'):
self.assertIn('===%s===' % section,self.script)
def test_supported_managers(self):
for manager in ('apt','dnf','yum','rpm','zypper','apk'):
self.assertIn(manager,self.script)
def test_no_package_mutation(self):
for command in ('apt upgrade','apt install','dnf update','yum update','apk upgrade','zypper update'):
self.assertNotIn(command,self.script)
def test_unknown_vendor_not_verified(self):
store=(ROOT/'PHPGuard/AuditStore.swift').read_text()
self.assertIn('No vendor advisory adapter for this distribution',store)
self.assertIn('vendor-verified:',store)
if __name__=='__main__': unittest.main()