From 3ce99405be8adb10d3eaa2cec64ea30fa93f6385 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Fri, 12 Jun 2026 16:03:43 +0300 Subject: [PATCH] Improve macOS logging WE2-1249 Signed-off-by: Raul Metsma --- .github/workflows/cmake-macos.yml | 1 + README.md | 16 ++++++++++ src/app/CMakeLists.txt | 5 +++- src/controller/CMakeLists.txt | 1 - src/controller/application.mm | 1 - src/mac/CMakeLists.txt | 26 ++++++++-------- src/mac/main.mm | 12 ++++++-- src/mac/safari-extension.mm | 35 ++++++++++++++-------- src/mac/web-eid-debug-logging.mobileconfig | 33 ++++++++++++++++++++ 9 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 src/mac/web-eid-debug-logging.mobileconfig diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml index c5430a89..91dbb87b 100644 --- a/.github/workflows/cmake-macos.yml +++ b/.github/workflows/cmake-macos.yml @@ -72,3 +72,4 @@ jobs: path: | build/src/app/*.pkg build/src/app/*.dmg + build/src/mac/*.pkg diff --git a/README.md b/README.md index e51defb7..6e0bc90f 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,22 @@ The application writes logs to - `~/Library/Containers/eu.web-eid.web-eid-safari/Data/Library/Application\ Support/RIA/web-eid-safari/web-eid-safari.log` of Safari in macOS - `C:/Users//AppData/Local/RIA/web-eid/web-eid.log` in Windows. +The Safari extension and its containing app also emit structured log messages via the macOS unified logging system. To stream them live: + + log stream --predicate 'subsystem BEGINSWITH "eu.web-eid.web-eid-safari"' --level debug + +To query past messages (e.g. the last hour): + + log show --predicate 'subsystem BEGINSWITH "eu.web-eid.web-eid-safari"' --last 1h + +By default, request and response payloads are redacted in the log output. To reveal them, sign and install `src/mac/web-eid-debug-logging.mobileconfig`: + + security cms -S -N "Apple Development: your@email.com (TEAMID)" \ + -i src/mac/web-eid-debug-logging.mobileconfig \ + -o /tmp/web-eid-debug-logging-signed.mobileconfig + +Open the signed file — macOS will prompt to install it via System Settings → Privacy & Security → Profiles. The profile enables private data system-wide; remove it when done. + ## Internal design The Web eID native application is built with the [Qt](https://www.qt.io/) framework. It consists of diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 676d3f56..a9b31427 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -62,7 +62,10 @@ elseif(APPLE) set(WEBEID_PATH /Applications/Utilities/web-eid.app/Contents/MacOS/web-eid) set_source_files_properties(${CMAKE_SOURCE_DIR}/install/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set_source_files_properties(InfoPlist.strings PROPERTIES MACOSX_PACKAGE_LOCATION Resources/en.lproj) - set_target_properties(web-eid PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in) + set_target_properties(web-eid PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacOSXBundleInfo.plist.in + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid" + ) configure_file(${CMAKE_SOURCE_DIR}/install/macos-postinstall.in root_firefox_scripts/postinstall @ONLY) install(TARGETS web-eid DESTINATION /Applications/Utilities COMPONENT main) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/eu.webeid.json COMPONENT chrome diff --git a/src/controller/CMakeLists.txt b/src/controller/CMakeLists.txt index 80e24e7a..26c73abd 100644 --- a/src/controller/CMakeLists.txt +++ b/src/controller/CMakeLists.txt @@ -45,7 +45,6 @@ add_library(controller STATIC if(APPLE) target_sources(controller PRIVATE application.mm) set_source_files_properties(application.mm PROPERTIES COMPILE_FLAGS "-fobjc-arc") - target_link_libraries(controller "-framework Cocoa" "-framework AppKit" "-fobjc-arc") endif() set_property(SOURCE application.cpp APPEND PROPERTY COMPILE_DEFINITIONS PROJECT_VERSION="${SEMVER_VERSION_STRING}") diff --git a/src/controller/application.mm b/src/controller/application.mm index e9e4ee1c..34a7fee4 100644 --- a/src/controller/application.mm +++ b/src/controller/application.mm @@ -6,7 +6,6 @@ #include #import -#import void Application::showAbout() { diff --git a/src/mac/CMakeLists.txt b/src/mac/CMakeLists.txt index 400787aa..6e4cc857 100644 --- a/src/mac/CMakeLists.txt +++ b/src/mac/CMakeLists.txt @@ -4,8 +4,7 @@ # Helper add_library(SafariServices INTERFACE shared.hpp) target_compile_options(SafariServices INTERFACE -fobjc-arc) -target_link_options(SafariServices INTERFACE -fobjc-arc -fobjc-link-runtime) -target_link_libraries(SafariServices INTERFACE "-framework SafariServices") +target_link_libraries(SafariServices INTERFACE $) # Extension JS find_program(NPM_EXECUTABLE NAMES npm REQUIRED) @@ -18,6 +17,7 @@ add_custom_command(OUTPUT ${EXTENSION} COMMENT "Generating extension js" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/js ) +set_source_files_properties(${EXTENSION} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) # Extension add_executable(web-eid-safari-extension MACOSX_BUNDLE safari-extension.mm web-eid-safari-extension.entitlements ${EXTENSION}) @@ -29,19 +29,15 @@ set_target_properties(web-eid-safari-extension PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/web-eid-safari-extension.entitlements" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid-safari.web-eid-safari-extension" ) -target_link_libraries(web-eid-safari-extension SafariServices "-framework AppKit") +target_link_libraries(web-eid-safari-extension SafariServices $) target_link_options(web-eid-safari-extension PRIVATE -e _NSExtensionMain -fapplication-extension) -add_custom_command(TARGET web-eid-safari-extension POST_BUILD - COMMAND mkdir -p $/Resources - COMMAND cp ${EXTENSION} $/Resources -) # Application add_executable(web-eid-safari MACOSX_BUNDLE ${CMAKE_SOURCE_DIR}/install/appicon.icns InfoPlist.strings main.mm - ../app/getCommandHandler.cpp + ../app/getcommandhandler.cpp web-eid-safari.entitlements ) set_source_files_properties(${CMAKE_SOURCE_DIR}/install/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) @@ -51,12 +47,16 @@ set_target_properties(web-eid-safari PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info-app.plist.cmake XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/web-eid-safari.entitlements" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "eu.web-eid.web-eid-safari" + XCODE_EMBED_APP_EXTENSIONS web-eid-safari-extension ) -target_link_libraries(web-eid-safari SafariServices controller ui pcsc "-framework Cocoa") -add_custom_command(TARGET web-eid-safari POST_BUILD - COMMAND mkdir -p $/PlugIns - COMMAND cp -a $ $/PlugIns -) +target_link_libraries(web-eid-safari SafariServices controller ui pcsc) +add_dependencies(web-eid-safari web-eid-safari-extension) +if(NOT XCODE) + add_custom_command(TARGET web-eid-safari POST_BUILD + COMMAND mkdir -p $/PlugIns + COMMAND cp -a $ $/PlugIns + ) +endif() add_custom_target(macdeployqt-safari DEPENDS web-eid-safari COMMAND Qt6::macdeployqt $ -appstore-compliant diff --git a/src/mac/main.mm b/src/mac/main.mm index 1322dfe8..1863a6ea 100644 --- a/src/mac/main.mm +++ b/src/mac/main.mm @@ -10,11 +10,14 @@ #include #include -#import +#import #import +#import #include "shared.hpp" +static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari", "app"); + #include @implementation NSApplication (MacController) @@ -95,7 +98,7 @@ - (void)notificationEvent:(NSNotification*)notification { NSString *nonce = notification.object; NSDictionary *req = takeValue(nonce); - NSLog(@"web-eid-safari: msg from extension nonce (%@) request: %@", nonce, req); + os_log(logger, "msg from extension nonce (%{public}@) request: %@", nonce, req); if (req == nil) { return; } @@ -122,7 +125,7 @@ Controller controller({ } } - NSLog(@"web-eid-safari: msg to extension nonce (%@) request: %@", nonce, resp); + os_log(logger, "msg to extension nonce (%{public}@) response: %@", nonce, resp); setValue(nonce, resp); [NSDistributedNotificationCenter.defaultCenter postNotificationName:WebEidExtension object:nonce userInfo:nil deliverImmediately:YES]; QCoreApplication::quit(); @@ -157,6 +160,9 @@ int main(int argc, char* argv[]) SafariApplication app(argc, argv, QStringLiteral("web-eid-safari")); auto appPtr = &app; + // Sender is not authenticated; DNCF is system-wide. This is intentional: the notification + // is a wake-up signal only — payload is read from the App Group NSUserDefaults, which is + // gated by team-ID entitlement and inaccessible to other processes. [NSDistributedNotificationCenter.defaultCenter addObserver:NSApp selector:@selector(notificationEvent:) name:WebEidApp object:nil]; id starting = [getUserDefaults() objectForKey:WebEidStarting]; diff --git a/src/mac/safari-extension.mm b/src/mac/safari-extension.mm index 349f9f5a..06124681 100644 --- a/src/mac/safari-extension.mm +++ b/src/mac/safari-extension.mm @@ -6,6 +6,9 @@ #include "shared.hpp" #import +#import + +static os_log_t logger = os_log_create("eu.web-eid.web-eid-safari.web-eid-safari-extension", "extension"); @interface SafariWebExtensionHandler : NSObject { NSMutableDictionary *contexts; @@ -16,8 +19,11 @@ @implementation SafariWebExtensionHandler - (id)init { if (self = [super init]) { - NSLog(@"web-eid-safari-extension: starting"); + os_log(logger, "starting"); contexts = [[NSMutableDictionary alloc] init]; + // Sender is not authenticated; DNCF is system-wide. This is intentional: the notification + // is a wake-up signal only — payload is read from the App Group NSUserDefaults, which is + // gated by team-ID entitlement and inaccessible to other processes. [NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(notificationEvent:) name:WebEidExtension object:nil]; } return self; @@ -25,7 +31,7 @@ - (id)init { - (void)dealloc { - NSLog(@"web-eid-safari-extension: stopping"); + os_log(logger, "stopping"); [NSDistributedNotificationCenter.defaultCenter removeObserver:self name:WebEidExtension object:nil]; } @@ -34,14 +40,19 @@ - (void)notificationEvent:(NSNotification*)notification // Received notification from App NSString *nonce = notification.object; NSDictionary *resp = takeValue(nonce); - NSLog(@"web-eid-safari-extension: from app nonce (%@) request: %@", nonce, resp); + os_log(logger, "from app nonce (%{public}@) request: %@", nonce, resp); if (resp == nil) { return; } + // Wait for the app to fully exit before returning the response to the page. + // The app is single-shot and calls QCoreApplication::quit() after posting the response. + // If we return early and the page immediately sends a follow-up request, execNativeApp + // would race with the still-shutting-down instance: launchApplication may no-op against + // it and the WebEidStarting handshake in Loop 1 would then time out. for (int i = 0; i < 20 && [NSRunningApplication runningApplicationsWithBundleIdentifier:WebEidApp].count > 0; ++i) { [NSThread sleepForTimeInterval:0.5]; - NSLog(@"web-eid-safari-extension: web-eid-safari is still running"); + os_log(logger, "web-eid-safari is still running"); } // Forward to background script @@ -56,31 +67,31 @@ - (BOOL)execNativeApp { NSURL *appURL = [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:WebEidApp]; if (appURL == nil) { - NSLog(@"web-eid-safari-extension: failed to get app url"); + os_log_error(logger, "failed to get app url"); return NO; } setValue(WebEidStarting, @(true)); if (![NSWorkspace.sharedWorkspace launchApplication:appURL.path]) { - NSLog(@"web-eid-safari-extension: failed to start app"); + os_log_error(logger, "failed to start app"); return NO; } - NSLog(@"web-eid-safari-extension: started app"); + os_log(logger, "started app"); for (int i = 0; i < 20 && [getUserDefaults() boolForKey:WebEidStarting]; ++i) { [NSThread sleepForTimeInterval:0.5]; - NSLog(@"web-eid-safari-extension: waiting to be running %@", [getUserDefaults() objectForKey:WebEidStarting]); + os_log(logger, "waiting to be running %{public}@", [getUserDefaults() objectForKey:WebEidStarting]); } if ([(NSNumber*)takeValue(WebEidStarting) boolValue]) { - NSLog(@"web-eid-safari-extension: timeout to start app"); + os_log_error(logger, "timeout to start app"); return NO; } - NSLog(@"web-eid-safari-extension: app executed"); + os_log(logger, "app executed"); return YES; } - (void)beginRequestWithExtensionContext:(NSExtensionContext*)context { id message = [context.inputItems.firstObject userInfo][SFExtensionMessageKey]; - NSLog(@"web-eid-safari-extension: msg from background.js %@", message); + os_log(logger, "msg from background.js %@", message); if ([@"status" isEqualToString:message[@"command"]]) { NSString *version = [NSString stringWithFormat:@"%@+%@", @@ -100,7 +111,7 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext*)context return; } - NSLog(@"web-eid-safari-extension: sending message to app %@", message); + os_log(logger, "sending message to app %@", message); // Save context NSString *nonce = [[[NSUUID alloc] init] UUIDString]; diff --git a/src/mac/web-eid-debug-logging.mobileconfig b/src/mac/web-eid-debug-logging.mobileconfig new file mode 100644 index 00000000..e0686467 --- /dev/null +++ b/src/mac/web-eid-debug-logging.mobileconfig @@ -0,0 +1,33 @@ + + + + + PayloadContent + + + PayloadType + com.apple.system.logging + PayloadIdentifier + eu.web-eid.web-eid-safari.logging + PayloadUUID + 4F8A1B2C-3D4E-5F6A-7B8C-9D0E1F2A3B4C + PayloadVersion + 1 + Enable-Private-Data + + + + PayloadDisplayName + web-eid Debug Logging + PayloadDescription + Enables private data in os_log output system-wide. Install for debugging only; remove when done. + PayloadIdentifier + eu.web-eid.debug-logging-profile + PayloadType + Configuration + PayloadUUID + 5A9B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D + PayloadVersion + 1 + +