Updates to scanner with CVE saving and auto update
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ struct ContentView: View {
|
||||
: "shield.lefthalf.filled"
|
||||
)
|
||||
|
||||
Text("Ubuntu: " + vendorStatus)
|
||||
Text("Vendor: " + vendorStatus)
|
||||
.font(.subheadline.bold())
|
||||
}
|
||||
.foregroundStyle(
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
zip -r ~/Downloads/PHPGuard-Source.zip PHPGuard \
|
||||
-x '*/.git/*' \
|
||||
'*/.build/*' \
|
||||
'*/DerivedData/*' \
|
||||
'*/build/*' \
|
||||
'*/dist/*' \
|
||||
'*/.DS_Store' \
|
||||
'*.dmg' \
|
||||
'*.zip'
|
||||
@@ -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
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
xcodebuild -list -project PHPGuard.xcodeproj
|
||||
@@ -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**.
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user