Updates to scanner with CVE saving and auto update

This commit is contained in:
Mark Gregory
2026-10-22 18:36:14 +01:00
parent 29aefbf64a
commit 733b9bae13
2 changed files with 99 additions and 91 deletions
+75 -62
View File
@@ -168,68 +168,6 @@ struct ContentView: View {
Text("Docker assessment: \(result.dockerAuditStatus)").foregroundStyle(.secondary)
dataSection("Docker coverage (PHP CLI not detected)", result.dockerCoverage)
dataSection("Security triage — follow-up actions", result.securityTriage)
VStack(alignment: .leading, spacing: 10) {
Text("CVE assessment").font(.title2.bold())
Text(result.cveAssessmentStatus).foregroundStyle(.secondary)
Text("NVD identifies upstream candidates. Ubuntu advisory status is checked separately. A published fix does not prove that the installed package is patched.")
.font(.caption).foregroundStyle(.secondary)
if result.cveAssessments.isEmpty {
Text("No upstream PHP CVE candidates recorded. This is not a clean security bill.")
.foregroundStyle(.secondary)
}
ForEach(result.cveAssessments) { item in
VStack(alignment: .leading, spacing: 5) {
HStack {
Link(item.cveID, destination: URL(string: item.referenceURL)!)
.font(.headline)
Text(item.severity).font(.caption.bold())
Spacer()
Text(item.status).foregroundStyle(.orange)
}
Text(item.runtime + " — " + item.summary).lineLimit(3)
Text(item.evidence)
.font(.caption)
.foregroundStyle(.secondary)
if let vendorStatus = item.vendorStatus {
HStack(spacing: 8) {
Image(
systemName:
vendorStatus == "Not affected" ||
vendorStatus == "Patched"
? "checkmark.shield.fill"
: vendorStatus == "Vulnerable"
? "exclamationmark.shield.fill"
: "shield.lefthalf.filled"
)
Text("Vendor: " + vendorStatus)
.font(.subheadline.bold())
}
.foregroundStyle(
vendorStatus == "Not affected" ||
vendorStatus == "Patched"
? Color.green
: vendorStatus == "Vulnerable"
? Color.red
: Color.orange
)
.padding(.top, 5)
}
if let vendorEvidence = item.vendorEvidence {
Text(vendorEvidence)
.font(.caption)
.foregroundStyle(.secondary)
.textSelection(.enabled)
}
}.padding(10)
.background(.quaternary.opacity(0.22), in: RoundedRectangle(cornerRadius: 10))
}
}
// Repository matching and binary verification are
// separate from vendor CVE verification.
VStack(alignment: .leading, spacing: 10) {
@@ -305,6 +243,81 @@ struct ContentView: View {
in: RoundedRectangle(cornerRadius: 10)
)
VStack(alignment: .leading, spacing: 10) {
Text("CVE assessment").font(.title2.bold())
Text(result.cveAssessmentStatus).foregroundStyle(.secondary)
Text("NVD identifies upstream candidates. Ubuntu advisory status is checked separately. A published fix does not prove that the installed package is patched.")
.font(.caption).foregroundStyle(.secondary)
if result.cveAssessments.isEmpty {
Text("No upstream PHP CVE candidates recorded. This is not a clean security bill.")
.foregroundStyle(.secondary)
}
ForEach(result.cveAssessments) { item in
VStack(alignment: .leading, spacing: 5) {
HStack {
Link(item.cveID, destination: URL(string: item.referenceURL)!)
.font(.headline)
Text(item.severity).font(.caption.bold())
Spacer()
Text(item.status).foregroundStyle(.orange)
}
Text(item.runtime + " — " + item.summary)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
Text(item.evidence)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
if let vendorStatus = item.vendorStatus {
HStack(spacing: 8) {
Image(
systemName:
vendorStatus == "Not affected" ||
vendorStatus == "Patched"
? "checkmark.shield.fill"
: vendorStatus == "Vulnerable"
? "exclamationmark.shield.fill"
: "shield.lefthalf.filled"
)
Text("Vendor: " + vendorStatus)
.font(.subheadline.bold())
}
.foregroundStyle(
vendorStatus == "Not affected" ||
vendorStatus == "Patched"
? Color.green
: vendorStatus == "Vulnerable"
? Color.red
: Color.orange
)
.padding(.top, 5)
}
if let vendorEvidence = item.vendorEvidence,
!vendorEvidence.hasPrefix(
"Installed PHP package versions match official Ubuntu repository entries."
) {
Text(vendorEvidence)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
.textSelection(.enabled)
}
}
.padding(10)
.frame(maxWidth: .infinity, alignment: .leading)
.background(
.quaternary.opacity(0.22),
in: RoundedRectangle(cornerRadius: 10)
)
}
}
HStack {
Text("Security findings").font(.title2.bold())
Spacer()
+24 -29
View File
@@ -393,35 +393,10 @@ extension VulnerabilityService {
// may unlock vendor conclusions. Repository listings alone never qualify.
line == "VERIFIED_INSTALLED_ORIGIN|ubuntu|" + report.ubuntuCodename
}
guard hasUbuntuOrigin else {
return assessments.map { original in
var item = original
item.vendorStatus = "Unverified"
let ubuntuMatches = report.packageOrigins.filter {
$0.hasPrefix("UBUNTU_REPOSITORY_MATCH|")
}
let otherMatches = report.packageOrigins.filter {
$0.hasPrefix("OTHER_REPOSITORY_MATCH|")
}
if !ubuntuMatches.isEmpty && otherMatches.isEmpty {
item.vendorEvidence =
"Installed PHP package versions match official Ubuntu repository entries. " +
"Installed binary provenance has not been independently verified; " +
"vendor CVE status remains Unverified."
} else if !otherMatches.isEmpty {
item.vendorEvidence =
"One or more PHP package versions match third-party repositories. " +
"Ubuntu vendor CVE status remains Unverified."
} else {
item.vendorEvidence =
"Ubuntu archive provenance not established for installed PHP packages. " +
"No confirmed Ubuntu repository matches were collected."
}
return item
}
}
// Repository matching is sufficient to investigate Ubuntu
// advisories, but not to certify installed binary integrity.
// Advisory results remain Unverified unless independently
// verified installed-package provenance is available.
let installed = installedPackages(report.packageSources)
@@ -496,6 +471,11 @@ extension VulnerabilityService {
forHTTPHeaderField: "Accept"
)
NSLog(
"PHPGuard Ubuntu advisory: fetching %@",
original.cveID
)
let (data, response) =
try await URLSession.shared.data(for: request)
@@ -714,6 +694,21 @@ extension VulnerabilityService {
error.localizedDescription
}
if !hasUbuntuOrigin {
let advisoryStatus = item.vendorStatus ?? "Unverified"
let advisoryEvidence = item.vendorEvidence ?? ""
item.vendorStatus = "Unverified"
item.vendorEvidence =
"Ubuntu advisory assessment: " +
advisoryStatus +
". " +
advisoryEvidence +
"\\nInstalled binary provenance has not been independently verified; " +
"this advisory result is not a confirmed host security verdict."
}
output.append(item)
}