Updates to scanner with CVE saving and auto update

This commit is contained in:
Mark Gregory
2026-10-22 13:22:35 +01:00
parent 9afc80ca18
commit be96da88f5
16 changed files with 2900 additions and 86 deletions
+18 -9
View File
@@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
B10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* Assets.xcassets */; };
C10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
@@ -18,10 +18,12 @@
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
@@ -35,6 +37,8 @@
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
@@ -55,7 +59,8 @@
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000001 /* Assets.xcassets */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
);
path = PHPGuard;
sourceTree = "<group>";
@@ -119,7 +124,9 @@
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (B10000000000000000000002 /* Assets.xcassets in Resources */,); runOnlyForDeploymentPostprocessing = 0; };
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (
C10000000000000000000002 /* Assets.xcassets in Resources */,
); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -131,8 +138,10 @@
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
A10000000000000000000002 /* VulnerabilityService.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
A10000000000000000000002 /* VulnerabilityService.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -168,8 +177,8 @@
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.8.0;
CURRENT_PROJECT_VERSION = 9;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
@@ -187,8 +196,8 @@
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.8.0;
CURRENT_PROJECT_VERSION = 9;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
@@ -0,0 +1,232 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
};
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000064 /* PHPGuardApp.swift */; };
0000000000000000000000C9 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000065 /* Models.swift */; };
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
path = VulnerabilityService.swift;
sourceTree = "<group>";
};
000000000000000000000030 /* PHPGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PHPGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000064 /* PHPGuardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPGuardApp.swift; sourceTree = "<group>"; };
000000000000000000000065 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
000000000000000000000010 = {
isa = PBXGroup;
children = (
000000000000000000000011 /* PHPGuard */,
000000000000000000000012 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000011 /* PHPGuard */ = {
isa = PBXGroup;
children = (
000000000000000000000064 /* PHPGuardApp.swift */,
000000000000000000000065 /* Models.swift */,
000000000000000000000066 /* SSHScanner.swift */,
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
B10000000000000000000001 /* Assets.xcassets */,
);
path = PHPGuard;
sourceTree = "<group>";
};
000000000000000000000012 /* Products */ = {
isa = PBXGroup;
children = (
000000000000000000000030 /* PHPGuard.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000020 /* PHPGuard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */;
buildPhases = (
000000000000000000000021 /* Sources */,
B10000000000000000000003 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = PHPGuard;
productName = PHPGuard;
productReference = 000000000000000000000030 /* PHPGuard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000020 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000010;
productRefGroup = 000000000000000000000012 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000020 /* PHPGuard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (B10000000000000000000002 /* Assets.xcassets in Resources */,); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000021 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */,
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
A10000000000000000000002 /* VulnerabilityService.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
000000000000000000000051 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000052 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
000000000000000000000053 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000054 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000051 /* Debug */,
000000000000000000000052 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000053 /* Debug */,
000000000000000000000054 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -0,0 +1,232 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
};
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000064 /* PHPGuardApp.swift */; };
0000000000000000000000C9 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000065 /* Models.swift */; };
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
path = VulnerabilityService.swift;
sourceTree = "<group>";
};
000000000000000000000030 /* PHPGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PHPGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000064 /* PHPGuardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPGuardApp.swift; sourceTree = "<group>"; };
000000000000000000000065 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
000000000000000000000010 = {
isa = PBXGroup;
children = (
000000000000000000000011 /* PHPGuard */,
000000000000000000000012 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000011 /* PHPGuard */ = {
isa = PBXGroup;
children = (
000000000000000000000064 /* PHPGuardApp.swift */,
000000000000000000000065 /* Models.swift */,
000000000000000000000066 /* SSHScanner.swift */,
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
);
path = PHPGuard;
sourceTree = "<group>";
};
000000000000000000000012 /* Products */ = {
isa = PBXGroup;
children = (
000000000000000000000030 /* PHPGuard.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000020 /* PHPGuard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */;
buildPhases = (
000000000000000000000021 /* Sources */,
B10000000000000000000003 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = PHPGuard;
productName = PHPGuard;
productReference = 000000000000000000000030 /* PHPGuard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000020 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000010;
productRefGroup = 000000000000000000000012 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000020 /* PHPGuard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (
B10000000000000000000002 /* Assets.xcassets in Resources */,
); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000021 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */,
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
000000000000000000000051 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000052 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
000000000000000000000053 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000054 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000051 /* Debug */,
000000000000000000000052 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000053 /* Debug */,
000000000000000000000054 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -0,0 +1,232 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
};
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000064 /* PHPGuardApp.swift */; };
0000000000000000000000C9 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000065 /* Models.swift */; };
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
path = VulnerabilityService.swift;
sourceTree = "<group>";
};
000000000000000000000030 /* PHPGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PHPGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000064 /* PHPGuardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPGuardApp.swift; sourceTree = "<group>"; };
000000000000000000000065 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
000000000000000000000010 = {
isa = PBXGroup;
children = (
000000000000000000000011 /* PHPGuard */,
000000000000000000000012 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000011 /* PHPGuard */ = {
isa = PBXGroup;
children = (
000000000000000000000064 /* PHPGuardApp.swift */,
000000000000000000000065 /* Models.swift */,
000000000000000000000066 /* SSHScanner.swift */,
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
);
path = PHPGuard;
sourceTree = "<group>";
};
000000000000000000000012 /* Products */ = {
isa = PBXGroup;
children = (
000000000000000000000030 /* PHPGuard.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000020 /* PHPGuard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */;
buildPhases = (
000000000000000000000021 /* Sources */,
B10000000000000000000003 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = PHPGuard;
productName = PHPGuard;
productReference = 000000000000000000000030 /* PHPGuard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000020 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000010;
productRefGroup = 000000000000000000000012 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000020 /* PHPGuard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (
B10000000000000000000002 /* Assets.xcassets in Resources */,
); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000021 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */,
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
000000000000000000000051 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000052 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
000000000000000000000053 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000054 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000051 /* Debug */,
000000000000000000000052 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000053 /* Debug */,
000000000000000000000054 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -0,0 +1,232 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
};
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000064 /* PHPGuardApp.swift */; };
0000000000000000000000C9 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000065 /* Models.swift */; };
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
path = VulnerabilityService.swift;
sourceTree = "<group>";
};
000000000000000000000030 /* PHPGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PHPGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000064 /* PHPGuardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPGuardApp.swift; sourceTree = "<group>"; };
000000000000000000000065 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
000000000000000000000010 = {
isa = PBXGroup;
children = (
000000000000000000000011 /* PHPGuard */,
000000000000000000000012 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000011 /* PHPGuard */ = {
isa = PBXGroup;
children = (
000000000000000000000064 /* PHPGuardApp.swift */,
000000000000000000000065 /* Models.swift */,
000000000000000000000066 /* SSHScanner.swift */,
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
);
path = PHPGuard;
sourceTree = "<group>";
};
000000000000000000000012 /* Products */ = {
isa = PBXGroup;
children = (
000000000000000000000030 /* PHPGuard.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000020 /* PHPGuard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */;
buildPhases = (
000000000000000000000021 /* Sources */,
B10000000000000000000003 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = PHPGuard;
productName = PHPGuard;
productReference = 000000000000000000000030 /* PHPGuard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000020 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000010;
productRefGroup = 000000000000000000000012 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000020 /* PHPGuard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (
B10000000000000000000002 /* Assets.xcassets in Resources */,
); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000021 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */,
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
000000000000000000000051 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000052 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
000000000000000000000053 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000054 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000051 /* Debug */,
000000000000000000000052 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000053 /* Debug */,
000000000000000000000054 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
@@ -0,0 +1,232 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
C10000000000000000000002 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C10000000000000000000001 /* Assets.xcassets */; };
A10000000000000000000002 /* VulnerabilityService.swift in Sources */ = {
isa = PBXBuildFile;
fileRef = A10000000000000000000001 /* VulnerabilityService.swift */;
};
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000064 /* PHPGuardApp.swift */; };
0000000000000000000000C9 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000065 /* Models.swift */; };
0000000000000000000000CA /* SSHScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000066 /* SSHScanner.swift */; };
0000000000000000000000CB /* AuditStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000067 /* AuditStore.swift */; };
0000000000000000000000CC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000000000000000000000068 /* ContentView.swift */; };
B20000000000000000000001 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000001 /* SettingsView.swift */; };
B20000000000000000000002 /* ReportPDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10000000000000000000002 /* ReportPDF.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
C10000000000000000000001 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A10000000000000000000001 /* VulnerabilityService.swift */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.swift;
path = VulnerabilityService.swift;
sourceTree = "<group>";
};
000000000000000000000030 /* PHPGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PHPGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
000000000000000000000064 /* PHPGuardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPGuardApp.swift; sourceTree = "<group>"; };
000000000000000000000065 /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = "<group>"; };
000000000000000000000066 /* SSHScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHScanner.swift; sourceTree = "<group>"; };
000000000000000000000067 /* AuditStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuditStore.swift; sourceTree = "<group>"; };
000000000000000000000068 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B10000000000000000000001 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B10000000000000000000002 /* ReportPDF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPDF.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
000000000000000000000010 = {
isa = PBXGroup;
children = (
000000000000000000000011 /* PHPGuard */,
000000000000000000000012 /* Products */,
);
sourceTree = "<group>";
};
000000000000000000000011 /* PHPGuard */ = {
isa = PBXGroup;
children = (
000000000000000000000064 /* PHPGuardApp.swift */,
000000000000000000000065 /* Models.swift */,
000000000000000000000066 /* SSHScanner.swift */,
000000000000000000000067 /* AuditStore.swift */,
A10000000000000000000001 /* VulnerabilityService.swift */,
000000000000000000000068 /* ContentView.swift */,
B10000000000000000000002 /* ReportPDF.swift */,
B10000000000000000000001 /* SettingsView.swift */,
);
path = PHPGuard;
sourceTree = "<group>";
};
000000000000000000000012 /* Products */ = {
isa = PBXGroup;
children = (
000000000000000000000030 /* PHPGuard.app */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
000000000000000000000020 /* PHPGuard */ = {
isa = PBXNativeTarget;
buildConfigurationList = 000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */;
buildPhases = (
000000000000000000000021 /* Sources */,
B10000000000000000000003 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = PHPGuard;
productName = PHPGuard;
productReference = 000000000000000000000030 /* PHPGuard.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
000000000000000000000001 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1600;
TargetAttributes = {
000000000000000000000020 = {
CreatedOnToolsVersion = 16.0;
};
};
};
buildConfigurationList = 000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 000000000000000000000010;
productRefGroup = 000000000000000000000012 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
000000000000000000000020 /* PHPGuard */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B10000000000000000000003 /* Resources */ = {isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = (
C10000000000000000000002 /* Assets.xcassets in Resources */,
); runOnlyForDeploymentPostprocessing = 0; };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
000000000000000000000021 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0000000000000000000000C8 /* PHPGuardApp.swift in Sources */,
0000000000000000000000C9 /* Models.swift in Sources */,
0000000000000000000000CA /* SSHScanner.swift in Sources */,
0000000000000000000000CB /* AuditStore.swift in Sources */,
0000000000000000000000CC /* ContentView.swift in Sources */,
B20000000000000000000002 /* ReportPDF.swift in Sources */,
B20000000000000000000001 /* SettingsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
000000000000000000000051 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000052 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
000000000000000000000053 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
000000000000000000000054 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 39R6U3437D;
ENABLE_APP_SANDBOX = NO;
GENERATE_INFOPLIST_FILE = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_KEY_CFBundleDisplayName = PHPGuard;
MARKETING_VERSION = 1.9.0;
CURRENT_PROJECT_VERSION = 10;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = net.absdev.PHPGuard;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
000000000000000000000041 /* Build configuration list for PBXProject "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000051 /* Debug */,
000000000000000000000052 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
000000000000000000000042 /* Build configuration list for PBXNativeTarget "PHPGuard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
000000000000000000000053 /* Debug */,
000000000000000000000054 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = 000000000000000000000001 /* Project object */;
}
+24 -2
View File
@@ -18,6 +18,12 @@ import SwiftUI
return path
}
init() {
let defaults = UserDefaults.standard
autoRefreshCVEs = defaults.object(forKey: "phpguard.autoRefreshCVEs") as? Bool ?? true
showTechnicalDetails = defaults.bool(forKey: "phpguard.showTechnicalDetails")
confirmBeforeClearing = defaults.object(forKey: "phpguard.confirmClear") as? Bool ?? true
preferredReportAccent = defaults.string(forKey: "phpguard.reportAccent") ?? "Blue"
retainHistoryLimit = defaults.object(forKey: "phpguard.historyLimit") as? Int ?? 250
servers = load("servers.json") ?? []
results = (load("results.json") ?? []).map { Self.normalized($0) }
if let cache: CVECache = load("cve-cache.json") {
@@ -73,7 +79,7 @@ import SwiftUI
save(CVECache(records: discoveredCVEs, fetchedAt: cveLastChecked), as: "cve-cache.json")
}
private func saveServers() { save(servers, as: "servers.json") }
private func saveResults() { save(Array(results.prefix(250)), as: "results.json") }
private func saveResults() { save(Array(results.prefix(max(1, retainHistoryLimit))), as: "results.json") }
func latest(for server: Server) -> AuditResult? { results.first { $0.serverID == server.id } }
func scan(_ server: Server) {
guard !scanning.contains(server.id) else { return }
@@ -92,7 +98,7 @@ import SwiftUI
func scanAll() { servers.forEach { scan($0) } }
func refreshCVEsOnLaunch() {
// Preserve the local cache while checking NVD for a newer result set.
refreshCVEs()
if autoRefreshCVEs { refreshCVEs() }
}
func refreshCVEs() {
guard !fetchingCVEs else { return }
@@ -111,6 +117,22 @@ import SwiftUI
} catch { cveError = error.localizedDescription }
}
}
@Published var showTechnicalDetails = false
@Published var confirmBeforeClearing = true
@Published var preferredReportAccent = "Blue"
@Published var autoRefreshCVEs = true
@Published var retainHistoryLimit = 250
func clearHistory() { results.removeAll() }
func removeHistory(_ offsets: IndexSet) { results.remove(atOffsets: offsets) }
func exportPDF(_ result: AuditResult) {
let panel = NSSavePanel()
panel.allowedContentTypes = [.pdf]
panel.nameFieldStringValue = "PHPGuard-\(result.serverName)-\(ISO8601DateFormatter().string(from: result.scannedAt)).pdf"
guard panel.runModal() == .OK, let url = panel.url else { return }
do { try ReportPDF.write(result, to: url, accent: preferredReportAccent) }
catch { lastError = "PDF export failed: \(error.localizedDescription)" }
}
func export(_ result: AuditResult) {
let panel = NSSavePanel()
panel.allowedContentTypes = [.json]
+68 -25
View File
@@ -5,6 +5,8 @@ struct ContentView: View {
@State private var selection: UUID?
@State private var showingEditor = false
@State private var editing: Server?
@State private var showClearHistoryConfirmation = false
@AppStorage("phpguard.confirmClear") private var confirmClearHistory = true
private func addServer() { editing = nil; showingEditor = true }
var body: some View {
NavigationSplitView {
@@ -110,7 +112,17 @@ struct ContentView: View {
if let error = result.error {
Label(error, systemImage: "xmark.circle.fill").foregroundStyle(.red)
} else {
Text("Last scan: \(result.scannedAt.formatted())").foregroundStyle(.secondary)
HStack(spacing: 12) {
Label("Scan completed", systemImage: "checkmark.shield.fill")
.foregroundStyle(.green)
Text(result.scannedAt.formatted()).foregroundStyle(.secondary)
Spacer()
Button { store.exportPDF(result) } label: { Label("Export PDF", systemImage: "doc.richtext") }
.buttonStyle(.borderedProminent)
Button { store.export(result) } label: { Label("JSON", systemImage: "square.and.arrow.up") }
}
.padding(16)
.background(.green.opacity(0.08), in: RoundedRectangle(cornerRadius: 14))
Text("OS: \(result.os) • Host: \(result.hostname)")
dataSection("PHP installations", result.phpVersions)
dataSection("PHP-FPM services", result.fpmServices)
@@ -123,15 +135,24 @@ 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)
Text("Findings").font(.title2.bold())
HStack {
Text("Security findings").font(.title2.bold())
Spacer()
Text("\(result.findings.count) total").foregroundStyle(.secondary)
}
ForEach(result.findings) { finding in
VStack(alignment: .leading, spacing: 4) {
Text("\(finding.severity): \(finding.title)").font(.headline)
Text(finding.detail).foregroundStyle(.secondary)
HStack(spacing: 10) {
Image(systemName: finding.severity.lowercased().contains("critical") || finding.severity.lowercased().contains("high") ? "exclamationmark.octagon.fill" : "info.circle.fill")
Text(finding.severity.uppercased()).font(.caption.bold())
Text(finding.title).font(.headline)
}
.foregroundStyle(finding.severity.lowercased().contains("critical") || finding.severity.lowercased().contains("high") ? Color.red : (finding.severity.lowercased().contains("warn") || finding.severity.lowercased().contains("medium") ? Color.orange : Color.blue))
Text(finding.detail).foregroundStyle(.secondary).textSelection(.enabled)
}.padding(12).frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.3), in: RoundedRectangle(cornerRadius: 10))
}
Button("Export JSON report") { store.export(result) }
}
} else { ContentUnavailableView("Not scanned", systemImage: "shield.lefthalf.filled", description: Text("Run a read-only SSH inventory scan.")) }
}.padding(28).frame(maxWidth: .infinity, alignment: .leading)
@@ -139,10 +160,18 @@ struct ContentView: View {
}
private func dataSection(_ title: String, _ values: [String]) -> some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.title2.bold())
HStack {
RoundedRectangle(cornerRadius: 2).fill(Color.accentColor).frame(width: 4, height: 22)
Text(title).font(.headline)
Spacer()
Text("\(values.count)").font(.caption).foregroundStyle(.secondary)
}
if values.isEmpty { Text("None detected").foregroundStyle(.secondary) }
else { ForEach(values, id: \.self) { Text($0).font(.system(.body, design: .monospaced)).textSelection(.enabled) } }
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.22), in: RoundedRectangle(cornerRadius: 14))
}
private var cveView: some View {
VStack(alignment: .leading, spacing: 14) {
@@ -177,18 +206,42 @@ struct ContentView: View {
}.padding(24)
}
private var historyView: some View {
List(store.results) { result in
VStack(spacing: 0) {
HStack {
VStack(alignment: .leading) {
Text(result.serverName).font(.headline)
Text(result.scannedAt.formatted()).foregroundStyle(.secondary)
if let error = result.error { Text(error).foregroundStyle(.red).lineLimit(2) }
VStack(alignment: .leading, spacing: 4) {
Text("Scan history").font(.largeTitle.bold())
Text("\(store.results.count) saved scans").foregroundStyle(.secondary)
}
Spacer()
Button("Export") { store.export(result) }.disabled(result.error != nil)
}.padding(.vertical, 4)
}.navigationTitle("Scan history")
Button("Clear History", role: .destructive) {
if confirmClearHistory { showClearHistoryConfirmation = true }
else { store.clearHistory() }
}.disabled(store.results.isEmpty)
}.padding(20)
List {
ForEach(store.results) { result in
HStack(spacing: 12) {
Image(systemName: result.error == nil ? "checkmark.shield.fill" : "xmark.circle.fill")
.foregroundStyle(result.error == nil ? .green : .red)
VStack(alignment: .leading, spacing: 4) {
Text(result.serverName).font(.headline)
Text(result.scannedAt.formatted()).foregroundStyle(.secondary)
if let error = result.error { Text(error).foregroundStyle(.red).lineLimit(2) }
else { Text("\(result.findings.count) findings · \(result.phpVersions.count) PHP installations")
.font(.caption).foregroundStyle(.secondary) }
}
Spacer()
Button("PDF") { store.exportPDF(result) }.disabled(result.error != nil)
Button("JSON") { store.export(result) }.disabled(result.error != nil)
}.padding(.vertical, 5)
}.onDelete(perform: store.removeHistory)
}
}
.confirmationDialog("Clear all scan history?", isPresented: $showClearHistoryConfirmation) {
Button("Clear History", role: .destructive) { store.clearHistory() }
} message: { Text("Saved scans will be permanently removed. Servers and CVE downloads will remain.") }
}
}
struct ServerEditor: View {
@@ -264,14 +317,4 @@ struct ServerEditor: View {
}
}
struct SettingsView: View {
var body: some View {
Form {
LabeledContent("SSH", value: "System OpenSSH, key-only")
LabeledContent("Host verification", value: "Strict")
LabeledContent("Scan mode", value: "Read-only")
Text("Server inventory and reports are stored locally in Application Support/PHPGuard.")
.foregroundStyle(.secondary)
}.padding(24).frame(width: 500, height: 220)
}
}
@@ -0,0 +1,330 @@
import SwiftUI
struct ContentView: View {
@EnvironmentObject var store: AuditStore
@State private var selection: UUID?
@State private var showingEditor = false
@State private var editing: Server?
@State private var showClearHistoryConfirmation = false
@AppStorage("phpguard.confirmClear") private var confirmClearHistory = true
private func addServer() { editing = nil; showingEditor = true }
var body: some View {
NavigationSplitView {
List(selection: $selection) {
Section("Servers") {
ForEach(store.servers) { server in
Label {
HStack {
Text(server.name)
Spacer()
if store.scanning.contains(server.id) { ProgressView().controlSize(.small) }
else if store.latest(for: server)?.error != nil { Image(systemName: "exclamationmark.triangle").foregroundStyle(.orange) }
}
} icon: { Image(systemName: "server.rack") }
.tag(server.id)
.contextMenu {
Button("Scan") { store.scan(server) }
Button("Edit") { editing = server; showingEditor = true }
Divider()
Button("Remove", role: .destructive) {
store.servers.removeAll { $0.id == server.id }
if selection == server.id { selection = nil }
}
}
}
}
Section("Navigation") {
Label("CVE research", systemImage: "shield.lefthalf.filled").tag(UUID(uuidString: "00000000-0000-0000-0000-000000000002")!)
Label("Scan history", systemImage: "clock.arrow.circlepath").tag(UUID(uuidString: "00000000-0000-0000-0000-000000000001")!)
}
}
.navigationTitle("PHPGuard")
.safeAreaInset(edge: .bottom) {
Button(action: addServer) {
Label("Add Server", systemImage: "plus.circle.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.padding(12)
.background(.bar)
}
} detail: {
if selection == UUID(uuidString: "00000000-0000-0000-0000-000000000002") {
cveView
} else if selection == UUID(uuidString: "00000000-0000-0000-0000-000000000001") {
historyView
} else if let server = store.servers.first(where: { $0.id == selection }) {
serverView(server)
} else { dashboard }
}
.sheet(isPresented: $showingEditor) {
ServerEditor(existing: editing) { server in
if let index = store.servers.firstIndex(where: { $0.id == server.id }) { store.servers[index] = server }
else { store.servers.append(server) }
selection = server.id
}
}
.alert("Scan error", isPresented: Binding(get: { store.lastError != nil }, set: { if !$0 { store.lastError = nil } })) {
Button("OK") { store.lastError = nil }
} message: { Text(store.lastError ?? "") }
}
private var dashboard: some View {
VStack(alignment: .leading, spacing: 24) {
Text("Security overview").font(.largeTitle.bold())
HStack(spacing: 16) {
stat("Servers", "\(store.servers.count)", "server.rack")
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("Read-only PHP inventory, APT upgrade checks and running Docker container discovery. Vendor advisory matching remains unimplemented; no server is certified secure.")
.foregroundStyle(.secondary)
HStack {
Button(action: addServer) { Label("Add Server", systemImage: "plus.circle.fill") }
.buttonStyle(.borderedProminent)
Button("Scan all servers") { store.scanAll() }.buttonStyle(.borderedProminent).disabled(store.servers.isEmpty)
Button("Import JSON scan") { store.importReport() }
}
Spacer()
}.padding(30).frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
private func stat(_ title: String, _ value: String, _ icon: String) -> some View {
VStack(alignment: .leading, spacing: 12) {
Label(title, systemImage: icon).foregroundStyle(.secondary)
Text(value).font(.system(size: 36, weight: .bold))
}.padding(20).frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.35), in: RoundedRectangle(cornerRadius: 14))
}
private func serverView(_ server: Server) -> some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
HStack {
VStack(alignment: .leading) {
Text(server.name).font(.largeTitle.bold())
Text("\(server.user)@\(server.host):\(server.port)").foregroundStyle(.secondary)
}
Spacer()
Button("Edit") { editing = server; showingEditor = true }
Button("Scan now") { store.scan(server) }.buttonStyle(.borderedProminent)
.disabled(store.scanning.contains(server.id))
}
if let result = store.latest(for: server) {
if let error = result.error {
Label(error, systemImage: "xmark.circle.fill").foregroundStyle(.red)
} else {
HStack(spacing: 12) {
Label("Scan completed", systemImage: "checkmark.shield.fill")
.foregroundStyle(.green)
Text(result.scannedAt.formatted()).foregroundStyle(.secondary)
Spacer()
Button { store.exportPDF(result) } label: { Label("Export PDF", systemImage: "doc.richtext") }
.buttonStyle(.borderedProminent)
Button { store.export(result) } label: { Label("JSON", systemImage: "square.and.arrow.up") }
}
.padding(16)
.background(.green.opacity(0.08), in: RoundedRectangle(cornerRadius: 14))
Text("OS: \(result.os) • Host: \(result.hostname)")
dataSection("PHP installations", result.phpVersions)
dataSection("PHP-FPM services", result.fpmServices)
dataSection("Nginx FastCGI routes", result.nginxUpstreams)
dataSection("Installed packages", result.packageVersions)
dataSection("Available PHP package upgrades", result.availableUpdates)
dataSection("APT package origins and candidate revisions", result.packageOrigins)
Text("Package assessment: \(result.packageAuditStatus)").foregroundStyle(.secondary)
dataSection("PHP detected in running Docker containers", result.dockerPHP)
Text("Docker assessment: \(result.dockerAuditStatus)").foregroundStyle(.secondary)
dataSection("Docker coverage (PHP CLI not detected)", result.dockerCoverage)
dataSection("Security triage — follow-up actions", result.securityTriage)
HStack {
Text("Security findings").font(.title2.bold())
Spacer()
Text("\(result.findings.count) total").foregroundStyle(.secondary)
}
ForEach(result.findings) { finding in
VStack(alignment: .leading, spacing: 4) {
HStack(spacing: 10) {
Image(systemName: finding.severity.lowercased().contains("critical") || finding.severity.lowercased().contains("high") ? "exclamationmark.octagon.fill" : "info.circle.fill")
Text(finding.severity.uppercased()).font(.caption.bold())
Text(finding.title).font(.headline)
}
.foregroundStyle(finding.severity.lowercased().contains("critical") || finding.severity.lowercased().contains("high") ? Color.red : (finding.severity.lowercased().contains("warn") || finding.severity.lowercased().contains("medium") ? Color.orange : Color.blue))
Text(finding.detail).foregroundStyle(.secondary).textSelection(.enabled)
}.padding(12).frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.3), in: RoundedRectangle(cornerRadius: 10))
}
}
} else { ContentUnavailableView("Not scanned", systemImage: "shield.lefthalf.filled", description: Text("Run a read-only SSH inventory scan.")) }
}.padding(28).frame(maxWidth: .infinity, alignment: .leading)
}
}
private func dataSection(_ title: String, _ values: [String]) -> some View {
VStack(alignment: .leading, spacing: 6) {
HStack {
RoundedRectangle(cornerRadius: 2).fill(Color.accentColor).frame(width: 4, height: 22)
Text(title).font(.headline)
Spacer()
Text("\(values.count)").font(.caption).foregroundStyle(.secondary)
}
if values.isEmpty { Text("None detected").foregroundStyle(.secondary) }
else { ForEach(values, id: \.self) { Text($0).font(.system(.body, design: .monospaced)).textSelection(.enabled) } }
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.22), in: RoundedRectangle(cornerRadius: 14))
}
private var cveView: some View {
VStack(alignment: .leading, spacing: 14) {
HStack {
Text("PHP CVE research").font(.largeTitle.bold())
Spacer()
if store.fetchingCVEs { ProgressView() }
Button("Refresh from NVD") { store.refreshCVEs() }.disabled(store.fetchingCVEs)
}
Text("Research candidates from the NVD keyword index. These records are NOT matched to installed PHP versions, Ubuntu backports or Docker images. Do not interpret them as confirmed server vulnerabilities.")
.foregroundStyle(.secondary)
if let date = store.cveLastChecked {
Text("Last updated: \(date.formatted()) · Saved locally · Automatically refreshed at launch")
.foregroundStyle(.secondary)
} else {
Text("No cached CVE research yet · Automatically fetched at launch")
.foregroundStyle(.secondary)
}
if let error = store.cveError { Text(error).foregroundStyle(.red) }
List(store.discoveredCVEs) { cve in
VStack(alignment: .leading, spacing: 6) {
HStack {
Link(cve.id, destination: URL(string: cve.referenceURL)!)
.font(.headline)
Spacer()
Text(cve.severity + (cve.score.map { " (\($0))" } ?? ""))
Text(String(cve.published.prefix(10))).foregroundStyle(.secondary)
}
Text(cve.description).lineLimit(4).textSelection(.enabled)
}.padding(.vertical, 5)
}
}.padding(24)
}
private var historyView: some View {
VStack(spacing: 0) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Scan history").font(.largeTitle.bold())
Text("\(store.results.count) saved scans").foregroundStyle(.secondary)
}
Spacer()
Button("Clear History", role: .destructive) {
if confirmClearHistory { showClearHistoryConfirmation = true }
else { store.clearHistory() }
}.disabled(store.results.isEmpty)
}.padding(20)
List {
ForEach(store.results) { result in
HStack(spacing: 12) {
Image(systemName: result.error == nil ? "checkmark.shield.fill" : "xmark.circle.fill")
.foregroundStyle(result.error == nil ? .green : .red)
VStack(alignment: .leading, spacing: 4) {
Text(result.serverName).font(.headline)
Text(result.scannedAt.formatted()).foregroundStyle(.secondary)
if let error = result.error { Text(error).foregroundStyle(.red).lineLimit(2) }
else { Text("\(result.findings.count) findings · \(result.phpVersions.count) PHP installations")
.font(.caption).foregroundStyle(.secondary) }
}
Spacer()
Button("PDF") { store.exportPDF(result) }.disabled(result.error != nil)
Button("JSON") { store.export(result) }.disabled(result.error != nil)
}.padding(.vertical, 5)
}.onDelete(perform: store.removeHistory)
}
}
.confirmationDialog("Clear all scan history?", isPresented: $showClearHistoryConfirmation) {
Button("Clear History", role: .destructive) { store.clearHistory() }
} message: { Text("Saved scans will be permanently removed. Servers and CVE downloads will remain.") }
}
}
struct ServerEditor: View {
@Environment(\.dismiss) private var dismiss
let existing: Server?
let onSave: (Server) -> Void
@State private var server: Server
init(existing: Server?, onSave: @escaping (Server) -> Void) {
self.existing = existing
self.onSave = onSave
_server = State(initialValue: existing ?? Server(name: "", host: ""))
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Label(existing == nil ? "Add a server" : "Edit server", systemImage: "server.rack")
.font(.title.bold())
Text("Connect using an existing SSH key. No agent installation required.")
.foregroundStyle(.secondary)
Form {
Section("Connection") {
TextField("Server name (e.g. Pegasus)", text: $server.name)
TextField("Hostname or IP address", text: $server.host)
.textContentType(.URL)
TextField("SSH username", text: $server.user)
TextField("SSH port", value: $server.port, format: .number)
}
Section("Advanced (optional)") {
HStack {
TextField("SSH private key path", text: $server.identityFile)
Button("Choose…") {
let panel = NSOpenPanel()
panel.canChooseDirectories = false
panel.canChooseFiles = true
if panel.runModal() == .OK, let url = panel.url {
server.identityFile = url.path
}
}
}
Text("Leave blank to use your existing SSH configuration and keys.")
.font(.caption).foregroundStyle(.secondary)
}
}.formStyle(.grouped)
if !isValid {
Label("Enter a valid server name, SSH host, username and port (1–65535).", systemImage: "info.circle")
.foregroundStyle(.secondary).font(.caption)
}
Text("Host key verification is strict. Connect in Terminal first if this server is new.")
.font(.caption).foregroundStyle(.secondary)
HStack {
Button("Cancel") { dismiss() }.keyboardShortcut(.cancelAction)
Spacer()
Button(existing == nil ? "Add Server" : "Save Changes") {
server.name = server.name.trimmingCharacters(in: .whitespacesAndNewlines)
server.host = server.host.trimmingCharacters(in: .whitespacesAndNewlines)
server.user = server.user.trimmingCharacters(in: .whitespacesAndNewlines)
onSave(server)
dismiss()
}
.buttonStyle(.borderedProminent)
.keyboardShortcut(.defaultAction)
.disabled(!isValid)
}
}.padding(24).frame(width: 580)
}
private var isValid: Bool {
let host = server.host.trimmingCharacters(in: .whitespacesAndNewlines)
let user = server.user.trimmingCharacters(in: .whitespacesAndNewlines)
return !server.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
&& !host.isEmpty && !host.hasPrefix("-")
&& host.range(of: "^[A-Za-z0-9][A-Za-z0-9.:-]*$", options: .regularExpression) != nil
&& user.range(of: "^[A-Za-z0-9_][A-Za-z0-9_.-]*$", options: .regularExpression) != nil
&& (1...65535).contains(server.port)
}
}
struct SettingsView: View {
var body: some View {
Form {
LabeledContent("SSH", value: "System OpenSSH, key-only")
LabeledContent("Host verification", value: "Strict")
LabeledContent("Scan mode", value: "Read-only")
Text("Server inventory and reports are stored locally in Application Support/PHPGuard.")
.foregroundStyle(.secondary)
}.padding(24).frame(width: 500, height: 220)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
import AppKit
import CoreGraphics
@MainActor enum ReportPDF {
static func write(_ report: AuditResult, to url: URL, accent: String) throws {
let data = NSMutableData()
guard let consumer = CGDataConsumer(data: data as CFMutableData) else { throw NSError(domain: "PHPGuardPDF", code: 1) }
var box = CGRect(x: 0, y: 0, width: 595, height: 842)
guard let ctx = CGContext(consumer: consumer, mediaBox: &box, nil) else { throw NSError(domain: "PHPGuardPDF", code: 2) }
let primary: NSColor = accent == "Teal" ? .systemTeal : accent == "Purple" ? .systemPurple : .systemBlue
let margin: CGFloat = 44
var y: CGFloat = 0
var page = 0
func startPage() {
ctx.beginPDFPage(nil); page += 1; y = 48
ctx.setFillColor(primary.cgColor)
ctx.fill(CGRect(x: 0, y: 0, width: 595, height: 13))
let heading = NSAttributedString(string: "PHPGUARD / SECURITY ASSESSMENT", attributes: [.font: NSFont.boldSystemFont(ofSize: 10), .foregroundColor: primary])
ctx.saveGState(); NSGraphicsContext.saveGraphicsState(); NSGraphicsContext.current = NSGraphicsContext(cgContext: ctx, flipped: true); ctx.translateBy(x: 0, y: 842); ctx.scaleBy(x: 1, y: -1)
heading.draw(at: CGPoint(x: margin, y: 25))
NSGraphicsContext.restoreGraphicsState(); ctx.restoreGState()
}
func endPage() {
let footer = NSAttributedString(string: "PHPGuard • Research findings require vendor advisory verification Page \(page)", attributes: [.font: NSFont.systemFont(ofSize: 8), .foregroundColor: NSColor.secondaryLabelColor])
ctx.saveGState(); NSGraphicsContext.saveGraphicsState(); NSGraphicsContext.current = NSGraphicsContext(cgContext: ctx, flipped: true); ctx.translateBy(x: 0, y: 842); ctx.scaleBy(x: 1, y: -1)
footer.draw(at: CGPoint(x: margin, y: 808))
NSGraphicsContext.restoreGraphicsState(); ctx.restoreGState(); ctx.endPDFPage()
}
func line(_ text: String, size: CGFloat = 10, bold: Bool = false, color: NSColor = .labelColor) {
let attrs: [NSAttributedString.Key: Any] = [.font: bold ? NSFont.boldSystemFont(ofSize: size) : NSFont.systemFont(ofSize: size), .foregroundColor: color]
let value = NSAttributedString(string: text, attributes: attrs)
let bound = value.boundingRect(with: CGSize(width: 507, height: 10000), options: [.usesLineFragmentOrigin, .usesFontLeading])
let height = max(size + 5, ceil(bound.height) + 5)
if y + height > 790 { endPage(); startPage() }
ctx.saveGState(); NSGraphicsContext.saveGraphicsState(); NSGraphicsContext.current = NSGraphicsContext(cgContext: ctx, flipped: true); ctx.translateBy(x: 0, y: 842); ctx.scaleBy(x: 1, y: -1)
value.draw(with: CGRect(x: margin, y: y, width: 507, height: height), options: [.usesLineFragmentOrigin, .usesFontLeading])
NSGraphicsContext.restoreGraphicsState(); ctx.restoreGState(); y += height
}
func section(_ title: String, _ rows: [String]) {
y += 14; line(title.uppercased(), size: 12, bold: true, color: primary)
if rows.isEmpty { line("No entries recorded", color: .secondaryLabelColor) }
else { for row in rows { line("• " + row) } }
}
startPage()
y += 18; line(report.serverName, size: 25, bold: true)
line("Host: \(report.hostname) OS: \(report.os)", color: .secondaryLabelColor)
line("Scan date: \(report.scannedAt.formatted(date: .abbreviated, time: .standard))", color: .secondaryLabelColor)
section("Executive summary", ["\(report.phpVersions.count) PHP runtimes • \(report.findings.count) findings • \(report.availableUpdates.count) package updates", "CVE status is unverified until matched with vendor advisories."])
section("PHP installations", report.phpVersions)
section("PHP-FPM services", report.fpmServices)
section("Available package updates", report.availableUpdates)
section("Security findings", report.findings.map { "[\($0.severity)] \($0.title): \($0.detail)" })
section("Security triage", report.securityTriage)
section("Nginx FastCGI", report.nginxUpstreams)
section("Installed packages", report.packageVersions)
section("APT package origins", report.packageOrigins)
section("Docker PHP", report.dockerPHP)
section("Docker coverage", report.dockerCoverage)
section("Recent PHP files", report.recentPHPFiles)
section("Web error signals", report.webErrorSignals)
section("Assessment notes", [report.packageAuditStatus, report.dockerAuditStatus, report.investigationStatus])
endPage(); ctx.closePDF(); try (data as Data).write(to: url, options: .atomic)
}
}
+67
View File
@@ -0,0 +1,67 @@
import SwiftUI
struct SettingsView: View {
@EnvironmentObject private var store: AuditStore
@AppStorage("phpguard.autoRefreshCVEs") private var autoRefresh = true
@AppStorage("phpguard.showTechnicalDetails") private var technical = false
@AppStorage("phpguard.confirmClear") private var confirmClear = true
@AppStorage("phpguard.reportAccent") private var accent = "Blue"
@AppStorage("phpguard.historyLimit") private var historyLimit = 250
@State private var showClearConfirmation = false
var body: some View {
TabView {
Form {
Section("Vulnerability intelligence") {
Toggle("Refresh CVE research when PHPGuard starts", isOn: $autoRefresh)
LabeledContent("Cached CVEs", value: "\(store.discoveredCVEs.count)")
LabeledContent("Last refreshed", value: store.cveLastChecked?.formatted() ?? "Never")
Button("Refresh CVEs now") { store.refreshCVEs() }.disabled(store.fetchingCVEs)
Text("NVD keyword matches are research leads, not verified server vulnerabilities.")
.font(.caption).foregroundStyle(.secondary)
}
}.formStyle(.grouped).tabItem { Label("Intelligence", systemImage: "shield.lefthalf.filled") }
Form {
Section("Reports") {
Picker("PDF accent", selection: $accent) {
ForEach(["Blue", "Teal", "Purple"], id: \.self) { Text($0) }
}
Toggle("Show technical details in reports", isOn: $technical)
}
Section("History") {
Stepper("Retain up to \(historyLimit) scans", value: $historyLimit, in: 25...2000, step: 25)
Toggle("Confirm before clearing scan history", isOn: $confirmClear)
Button("Clear scan history", role: .destructive) {
if confirmClear { showClearConfirmation = true } else { store.clearHistory() }
}.disabled(store.results.isEmpty)
}
}.formStyle(.grouped).tabItem { Label("Reports & History", systemImage: "doc.richtext") }
Form {
Section("About PHPGuard") {
LabeledContent("Version", value: "1.9.0")
LabeledContent("Build", value: "10")
LabeledContent("Stored servers", value: "\(store.servers.count)")
Text("Server scans are read-only. Security advisories require independent verification.")
.font(.caption).foregroundStyle(.secondary)
}
}.formStyle(.grouped).tabItem { Label("About", systemImage: "info.circle") }
}
.frame(width: 680, height: 460)
.onAppear(perform: syncToStore)
.onChange(of: autoRefresh) { _, _ in syncToStore() }
.onChange(of: technical) { _, _ in syncToStore() }
.onChange(of: confirmClear) { _, _ in syncToStore() }
.onChange(of: accent) { _, _ in syncToStore() }
.onChange(of: historyLimit) { _, _ in syncToStore() }
.confirmationDialog("Delete all saved scan history?", isPresented: $showClearConfirmation) {
Button("Clear History", role: .destructive) { store.clearHistory() }
} message: { Text("This permanently removes saved scan results. Server configurations and cached CVEs are kept.") }
}
private func syncToStore() {
store.autoRefreshCVEs = autoRefresh
store.showTechnicalDetails = technical
store.confirmBeforeClearing = confirmClear
store.preferredReportAccent = accent
store.retainHistoryLimit = historyLimit
}
}
-10
View File
@@ -1,10 +0,0 @@
# PHPGuard 1.8.0 — Phase 9
- Removed the redundant floating Add Server toolbar button; Add Server remains in the sidebar and dashboard.
- NVD research records are cached in Application Support/PHPGuard/cve-cache.json.
- Cached CVEs load immediately when the application opens.
- NVD research refreshes automatically on application launch and can be refreshed manually.
- Network/API failures preserve cached CVE records and show an error.
- Existing app icon, installer artwork, server scans, report history and release scripts retained.
CVE keyword results are research leads, not confirmed vulnerabilities for any scanned host.
+2 -4
View File
@@ -6,10 +6,8 @@ command -v xcodebuild >/dev/null || { echo "Xcode is required" >&2; exit 1; }
command -v hdiutil >/dev/null || { echo "hdiutil missing" >&2; exit 1; }
IDENTITY="${SIGNING_IDENTITY:-$(security find-identity -v -p codesigning | sed -nE 's/.*"(Developer ID Application: [^"]+)".*/\1/p' | head -1)}"
[[ -n "$IDENTITY" ]] || { echo "Developer ID Application signing identity required. Set SIGNING_IDENTITY." >&2; exit 1; }
# Use the existing Apple notarization profile by default.
# Override with NOTARY_PROFILE when required.
PROFILE="${NOTARY_PROFILE:-orion-notary}"
echo "Notarization profile: $PROFILE"
PROFILE="${NOTARY_PROFILE:-}"
[[ -n "$PROFILE" ]] || { echo "Set NOTARY_PROFILE to an existing notarytool keychain profile." >&2; exit 1; }
DERIVED="$ROOT/.build"
xcodebuild -project "$ROOT/PHPGuard.xcodeproj" -scheme PHPGuard -configuration Release -derivedDataPath "$DERIVED" CODE_SIGNING_ALLOWED=NO build
APP="$DERIVED/Build/Products/Release/PHPGuard.app"
-36
View File
@@ -1,36 +0,0 @@
#!/bin/zsh
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
command -v xcodebuild >/dev/null || { echo "Xcode is required" >&2; exit 1; }
command -v hdiutil >/dev/null || { echo "hdiutil missing" >&2; exit 1; }
IDENTITY="${SIGNING_IDENTITY:-$(security find-identity -v -p codesigning | sed -nE 's/.*"(Developer ID Application: [^"]+)".*/\1/p' | head -1)}"
[[ -n "$IDENTITY" ]] || { echo "Developer ID Application signing identity required. Set SIGNING_IDENTITY." >&2; exit 1; }
PROFILE="${NOTARY_PROFILE:-}"
[[ -n "$PROFILE" ]] || { echo "Set NOTARY_PROFILE to an existing notarytool keychain profile." >&2; exit 1; }
DERIVED="$ROOT/.build"
xcodebuild -project "$ROOT/PHPGuard.xcodeproj" -scheme PHPGuard -configuration Release -derivedDataPath "$DERIVED" CODE_SIGNING_ALLOWED=NO build
APP="$DERIVED/Build/Products/Release/PHPGuard.app"
[[ -d "$APP" ]] || { echo "Missing Release app" >&2; exit 1; }
VERSION="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$APP/Contents/Info.plist")"
mkdir -p "$ROOT/dist"
STAGING="$(mktemp -d)"
trap 'rm -rf "$STAGING"' EXIT
cp -R "$APP" "$STAGING/PHPGuard.app"
# No --deep signing: sign embedded executables individually if added in future.
codesign --force --options runtime --timestamp --sign "$IDENTITY" "$STAGING/PHPGuard.app"
codesign --verify --strict --verbose=2 "$STAGING/PHPGuard.app"
DMG="$ROOT/dist/PHPGuard-$VERSION.dmg"
rm -f "$DMG"
ln -s /Applications "$STAGING/Applications"
# Include a branded installer graphic in the disk image.
INSTALLER_ICON="$ROOT/PHPGuard/Assets.xcassets/InstallerIcon.imageset/icon.png"
if [[ -f "$INSTALLER_ICON" ]]; then
mkdir -p "$STAGING/.background"
cp "$INSTALLER_ICON" "$STAGING/.background/PHPGuard-Installer.png"
fi
hdiutil create -volname "PHPGuard $VERSION" -srcfolder "$STAGING" -ov -format UDZO "$DMG"
xcrun notarytool submit "$DMG" --keychain-profile "$PROFILE" --wait
xcrun stapler staple "$DMG"
xcrun stapler validate "$DMG"
echo "Release ready: $DMG"
+10
View File
@@ -0,0 +1,10 @@
# PHPGuard 1.9.0 — Phase 10
- Redesigned report presentation with grouped cards and severity colours.
- Multi-page PDF report export with a coloured header, findings, inventory and footer.
- Clear all scan history with confirmation; delete individual scans.
- New native Settings window: Intelligence, Reports & History, About.
- Persistent preferences for CVE auto-refresh, history retention, PDF accent and confirmation.
- Fixed missing SettingsView.swift source reference in 1.8.0.
Note: PDF reports are assessments, not verified CVE attestations.