feat(permission): wire WebsiteDataAccessPermissionRequest through WebKit layer
- commit
- 2b3891ddef341def12196aaf0890a9b7fffcae14
- author
- brice · · 2026-03-17 19:13 UTC
- committer
- brice · · 2026-03-17 19:16 UTC
- parent
- fcdcea4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +63 -6
M internal/infrastructure/webkit/webview.go +58 -6
| @@ -998,11 +998,14 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||
| 998 | 998 | } | |
| 999 | 999 | ||
| 1000 | 1000 | // Determine permission types from the request | |
| 1001 | - | permTypes := wv.determinePermissionTypes(ctx, requestPtr) | |
| 1001 | + | permTypes, originOverride := wv.determinePermissionTypes(ctx, requestPtr) | |
| 1002 | 1002 | if len(permTypes) == 0 { | |
| 1003 | 1003 | wv.logger.Warn().Msg("permission request with unknown type, denying") | |
| 1004 | 1004 | return false | |
| 1005 | 1005 | } | |
| 1006 | + | if originOverride != "" { | |
| 1007 | + | origin = originOverride | |
| 1008 | + | } | |
| 1006 | 1009 | ||
| 1007 | 1010 | // Ref the request object to prevent use-after-free | |
| 1008 | 1011 | // The request may outlive the signal handler if we show a dialog | |
| @@ -1048,13 +1051,13 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||
| 1048 | 1051 | // We use GObject property accessors for audio/video request flags. Display detection | |
| 1049 | 1052 | // uses the dedicated WebKit API because this WebKit build does not expose an | |
| 1050 | 1053 | // "is-for-display-device" GObject property. | |
| 1051 | - | func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) []string { | |
| 1054 | + | func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, originOverride string) { | |
| 1052 | 1055 | requestKind := detectPermissionRequestKind(ctx, requestPtr) | |
| 1053 | 1056 | switch requestKind { | |
| 1054 | 1057 | case permissionRequestKindUserMedia: | |
| 1055 | 1058 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | |
| 1056 | 1059 | if userMediaReq == nil { | |
| 1057 | - | return nil | |
| 1060 | + | return nil, "" | |
| 1058 | 1061 | } | |
| 1059 | 1062 | ||
| 1060 | 1063 | // Use GObject property accessors — more reliable than the C function wrappers | |
| @@ -1069,9 +1072,34 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint | |||
| 1069 | 1072 | Bool("is_display", isDisplay). | |
| 1070 | 1073 | Msg("permission request type detection") | |
| 1071 | 1074 | ||
| 1072 | - | return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay) | |
| 1075 | + | return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), "" | |
| 1073 | 1076 | case permissionRequestKindDeviceInfo: | |
| 1074 | - | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false) | |
| 1077 | + | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), "" | |
| 1078 | + | case permissionRequestKindWebsiteDataAccess: | |
| 1079 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |
| 1080 | + | if wdaReq == nil { | |
| 1081 | + | return nil, "" | |
| 1082 | + | } | |
| 1083 | + | currentDomain := wdaReq.GetCurrentDomain() | |
| 1084 | + | requestingDomain := wdaReq.GetRequestingDomain() | |
| 1085 | + | wv.logger.Debug(). | |
| 1086 | + | Str("current_domain", currentDomain). | |
| 1087 | + | Str("requesting_domain", requestingDomain). | |
| 1088 | + | Msg("website data access permission request") | |
| 1089 | + | override := "" | |
| 1090 | + | if requestingDomain != "" { | |
| 1091 | + | candidate := "https://" + requestingDomain | |
| 1092 | + | if normalized, err := urlutil.ExtractOrigin(candidate); err == nil { | |
| 1093 | + | override = normalized | |
| 1094 | + | } else { | |
| 1095 | + | wv.logger.Warn(). | |
| 1096 | + | Str("requesting_domain", requestingDomain). | |
| 1097 | + | Err(err). | |
| 1098 | + | Msg("website data access: failed to normalize requesting domain, using raw") | |
| 1099 | + | override = candidate | |
| 1100 | + | } | |
| 1101 | + | } | |
| 1102 | + | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), override | |
| 1075 | 1103 | default: | |
| 1076 | 1104 | if requestPtr != 0 { | |
| 1077 | 1105 | typeName := permissionRequestTypeName(ctx, requestPtr) | |
| @@ -1083,7 +1111,7 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint | |||
| 1083 | 1111 | } | |
| 1084 | 1112 | // Unknown permission type - could be clipboard, notifications, geolocation, etc. | |
| 1085 | 1113 | // For now, return empty to trigger denial. Future phases will add these types. | |
| 1086 | - | return nil | |
| 1114 | + | return nil, "" | |
| 1087 | 1115 | } | |
| 1088 | 1116 | } | |
| 1089 | 1117 | ||
| @@ -1093,6 +1121,7 @@ const ( | |||
| 1093 | 1121 | permissionRequestKindUnknown permissionRequestKind = iota | |
| 1094 | 1122 | permissionRequestKindUserMedia | |
| 1095 | 1123 | permissionRequestKindDeviceInfo | |
| 1124 | + | permissionRequestKindWebsiteDataAccess | |
| 1096 | 1125 | ) | |
| 1097 | 1126 | ||
| 1098 | 1127 | func detectPermissionRequestKind(ctx context.Context, requestPtr uintptr) permissionRequestKind { | |
| @@ -1106,6 +1135,11 @@ func detectPermissionRequestKind(ctx context.Context, requestPtr uintptr) permis | |||
| 1106 | 1135 | return permissionRequestKindDeviceInfo | |
| 1107 | 1136 | } | |
| 1108 | 1137 | } | |
| 1138 | + | if gtype, ok := safeGLibType(ctx, webkit.WebsiteDataAccessPermissionRequestGLibType); ok { | |
| 1139 | + | if isPermissionRequestType(ctx, requestPtr, gtype) { | |
| 1140 | + | return permissionRequestKindWebsiteDataAccess | |
| 1141 | + | } | |
| 1142 | + | } | |
| 1109 | 1143 | return permissionRequestKindUnknown | |
| 1110 | 1144 | } | |
| 1111 | 1145 | ||
| @@ -1140,6 +1174,8 @@ func classifyPermissionRequestTypes( | |||
| 1140 | 1174 | return classifyUserMediaPermissionTypes(ctx, isAudio, isVideo, isDisplay) | |
| 1141 | 1175 | case permissionRequestKindDeviceInfo: | |
| 1142 | 1176 | return []string{"device_info"} | |
| 1177 | + | case permissionRequestKindWebsiteDataAccess: | |
| 1178 | + | return []string{"website_data_access"} | |
| 1143 | 1179 | default: | |
| 1144 | 1180 | return nil | |
| 1145 | 1181 | } | |
| @@ -1190,6 +1226,14 @@ func (wv *WebView) allowPermissionRequest(requestPtr uintptr) { | |||
| 1190 | 1226 | return | |
| 1191 | 1227 | } | |
| 1192 | 1228 | ||
| 1229 | + | // Try WebsiteDataAccessPermissionRequest | |
| 1230 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |
| 1231 | + | if wdaReq != nil { | |
| 1232 | + | wdaReq.Allow() | |
| 1233 | + | wv.logger.Debug().Msg("website data access permission request allowed") | |
| 1234 | + | return | |
| 1235 | + | } | |
| 1236 | + | ||
| 1193 | 1237 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot allow") | |
| 1194 | 1238 | } | |
| 1195 | 1239 | ||
| @@ -1211,6 +1255,14 @@ func (wv *WebView) denyPermissionRequest(requestPtr uintptr) { | |||
| 1211 | 1255 | return | |
| 1212 | 1256 | } | |
| 1213 | 1257 | ||
| 1258 | + | // Try WebsiteDataAccessPermissionRequest | |
| 1259 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |
| 1260 | + | if wdaReq != nil { | |
| 1261 | + | wdaReq.Deny() | |
| 1262 | + | wv.logger.Debug().Msg("website data access permission request denied") | |
| 1263 | + | return | |
| 1264 | + | } | |
| 1265 | + | ||
| 1214 | 1266 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot deny") | |
| 1215 | 1267 | } | |
| 1216 | 1268 | ||
| @@ -998,11 +998,14 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||||
| 998 | } | 998 | } | ||
| 999 | 999 | ||||
| 1000 | // Determine permission types from the request | 1000 | // Determine permission types from the request | ||
| 1001 | - | permTypes := wv.determinePermissionTypes(ctx, requestPtr) | 1001 | + | permTypes, originOverride := wv.determinePermissionTypes(ctx, requestPtr) |
| 1002 | if len(permTypes) == 0 { | 1002 | if len(permTypes) == 0 { | ||
| 1003 | wv.logger.Warn().Msg("permission request with unknown type, denying") | 1003 | wv.logger.Warn().Msg("permission request with unknown type, denying") | ||
| 1004 | return false | 1004 | return false | ||
| 1005 | } | 1005 | } | ||
| 1006 | + | if originOverride != "" { | |||
| 1007 | + | origin = originOverride | |||
| 1008 | + | } | |||
| 1006 | 1009 | ||||
| 1007 | // Ref the request object to prevent use-after-free | 1010 | // Ref the request object to prevent use-after-free | ||
| 1008 | // The request may outlive the signal handler if we show a dialog | 1011 | // The request may outlive the signal handler if we show a dialog | ||
| @@ -1048,13 +1051,13 @@ func (wv *WebView) connectPermissionRequestSignal() { | |||||
| 1048 | // We use GObject property accessors for audio/video request flags. Display detection | 1051 | // We use GObject property accessors for audio/video request flags. Display detection | ||
| 1049 | // uses the dedicated WebKit API because this WebKit build does not expose an | 1052 | // uses the dedicated WebKit API because this WebKit build does not expose an | ||
| 1050 | // "is-for-display-device" GObject property. | 1053 | // "is-for-display-device" GObject property. | ||
| 1051 | - | func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) []string { | 1054 | + | func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, originOverride string) { |
| 1052 | requestKind := detectPermissionRequestKind(ctx, requestPtr) | 1055 | requestKind := detectPermissionRequestKind(ctx, requestPtr) | ||
| 1053 | switch requestKind { | 1056 | switch requestKind { | ||
| 1054 | case permissionRequestKindUserMedia: | 1057 | case permissionRequestKindUserMedia: | ||
| 1055 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | 1058 | userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr) | ||
| 1056 | if userMediaReq == nil { | 1059 | if userMediaReq == nil { | ||
| 1057 | - | return nil | 1060 | + | return nil, "" |
| 1058 | } | 1061 | } | ||
| 1059 | 1062 | ||||
| 1060 | // Use GObject property accessors — more reliable than the C function wrappers | 1063 | // Use GObject property accessors — more reliable than the C function wrappers | ||
| @@ -1069,9 +1072,34 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint | |||||
| 1069 | Bool("is_display", isDisplay). | 1072 | Bool("is_display", isDisplay). | ||
| 1070 | Msg("permission request type detection") | 1073 | Msg("permission request type detection") | ||
| 1071 | 1074 | ||||
| 1072 | - | return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay) | 1075 | + | return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), "" |
| 1073 | case permissionRequestKindDeviceInfo: | 1076 | case permissionRequestKindDeviceInfo: | ||
| 1074 | - | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false) | 1077 | + | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), "" |
| 1078 | + | case permissionRequestKindWebsiteDataAccess: | |||
| 1079 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |||
| 1080 | + | if wdaReq == nil { | |||
| 1081 | + | return nil, "" | |||
| 1082 | + | } | |||
| 1083 | + | currentDomain := wdaReq.GetCurrentDomain() | |||
| 1084 | + | requestingDomain := wdaReq.GetRequestingDomain() | |||
| 1085 | + | wv.logger.Debug(). | |||
| 1086 | + | Str("current_domain", currentDomain). | |||
| 1087 | + | Str("requesting_domain", requestingDomain). | |||
| 1088 | + | Msg("website data access permission request") | |||
| 1089 | + | override := "" | |||
| 1090 | + | if requestingDomain != "" { | |||
| 1091 | + | candidate := "https://" + requestingDomain | |||
| 1092 | + | if normalized, err := urlutil.ExtractOrigin(candidate); err == nil { | |||
| 1093 | + | override = normalized | |||
| 1094 | + | } else { | |||
| 1095 | + | wv.logger.Warn(). | |||
| 1096 | + | Str("requesting_domain", requestingDomain). | |||
| 1097 | + | Err(err). | |||
| 1098 | + | Msg("website data access: failed to normalize requesting domain, using raw") | |||
| 1099 | + | override = candidate | |||
| 1100 | + | } | |||
| 1101 | + | } | |||
| 1102 | + | return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), override | |||
| 1075 | default: | 1103 | default: | ||
| 1076 | if requestPtr != 0 { | 1104 | if requestPtr != 0 { | ||
| 1077 | typeName := permissionRequestTypeName(ctx, requestPtr) | 1105 | typeName := permissionRequestTypeName(ctx, requestPtr) | ||
| @@ -1083,7 +1111,7 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint | |||||
| 1083 | } | 1111 | } | ||
| 1084 | // Unknown permission type - could be clipboard, notifications, geolocation, etc. | 1112 | // Unknown permission type - could be clipboard, notifications, geolocation, etc. | ||
| 1085 | // For now, return empty to trigger denial. Future phases will add these types. | 1113 | // For now, return empty to trigger denial. Future phases will add these types. | ||
| 1086 | - | return nil | 1114 | + | return nil, "" |
| 1087 | } | 1115 | } | ||
| 1088 | } | 1116 | } | ||
| 1089 | 1117 | ||||
| @@ -1093,6 +1121,7 @@ const ( | |||||
| 1093 | permissionRequestKindUnknown permissionRequestKind = iota | 1121 | permissionRequestKindUnknown permissionRequestKind = iota | ||
| 1094 | permissionRequestKindUserMedia | 1122 | permissionRequestKindUserMedia | ||
| 1095 | permissionRequestKindDeviceInfo | 1123 | permissionRequestKindDeviceInfo | ||
| 1124 | + | permissionRequestKindWebsiteDataAccess | |||
| 1096 | ) | 1125 | ) | ||
| 1097 | 1126 | ||||
| 1098 | func detectPermissionRequestKind(ctx context.Context, requestPtr uintptr) permissionRequestKind { | 1127 | func detectPermissionRequestKind(ctx context.Context, requestPtr uintptr) permissionRequestKind { | ||
| @@ -1106,6 +1135,11 @@ func detectPermissionRequestKind(ctx context.Context, requestPtr uintptr) permis | |||||
| 1106 | return permissionRequestKindDeviceInfo | 1135 | return permissionRequestKindDeviceInfo | ||
| 1107 | } | 1136 | } | ||
| 1108 | } | 1137 | } | ||
| 1138 | + | if gtype, ok := safeGLibType(ctx, webkit.WebsiteDataAccessPermissionRequestGLibType); ok { | |||
| 1139 | + | if isPermissionRequestType(ctx, requestPtr, gtype) { | |||
| 1140 | + | return permissionRequestKindWebsiteDataAccess | |||
| 1141 | + | } | |||
| 1142 | + | } | |||
| 1109 | return permissionRequestKindUnknown | 1143 | return permissionRequestKindUnknown | ||
| 1110 | } | 1144 | } | ||
| 1111 | 1145 | ||||
| @@ -1140,6 +1174,8 @@ func classifyPermissionRequestTypes( | |||||
| 1140 | return classifyUserMediaPermissionTypes(ctx, isAudio, isVideo, isDisplay) | 1174 | return classifyUserMediaPermissionTypes(ctx, isAudio, isVideo, isDisplay) | ||
| 1141 | case permissionRequestKindDeviceInfo: | 1175 | case permissionRequestKindDeviceInfo: | ||
| 1142 | return []string{"device_info"} | 1176 | return []string{"device_info"} | ||
| 1177 | + | case permissionRequestKindWebsiteDataAccess: | |||
| 1178 | + | return []string{"website_data_access"} | |||
| 1143 | default: | 1179 | default: | ||
| 1144 | return nil | 1180 | return nil | ||
| 1145 | } | 1181 | } | ||
| @@ -1190,6 +1226,14 @@ func (wv *WebView) allowPermissionRequest(requestPtr uintptr) { | |||||
| 1190 | return | 1226 | return | ||
| 1191 | } | 1227 | } | ||
| 1192 | 1228 | ||||
| 1229 | + | // Try WebsiteDataAccessPermissionRequest | |||
| 1230 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |||
| 1231 | + | if wdaReq != nil { | |||
| 1232 | + | wdaReq.Allow() | |||
| 1233 | + | wv.logger.Debug().Msg("website data access permission request allowed") | |||
| 1234 | + | return | |||
| 1235 | + | } | |||
| 1236 | + | ||||
| 1193 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot allow") | 1237 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot allow") | ||
| 1194 | } | 1238 | } | ||
| 1195 | 1239 | ||||
| @@ -1211,6 +1255,14 @@ func (wv *WebView) denyPermissionRequest(requestPtr uintptr) { | |||||
| 1211 | return | 1255 | return | ||
| 1212 | } | 1256 | } | ||
| 1213 | 1257 | ||||
| 1258 | + | // Try WebsiteDataAccessPermissionRequest | |||
| 1259 | + | wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr) | |||
| 1260 | + | if wdaReq != nil { | |||
| 1261 | + | wdaReq.Deny() | |||
| 1262 | + | wv.logger.Debug().Msg("website data access permission request denied") | |||
| 1263 | + | return | |||
| 1264 | + | } | |||
| 1265 | + | ||||
| 1214 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot deny") | 1266 | wv.logger.Warn().Uint64("request_ptr", uint64(requestPtr)).Msg("permission request: unknown type, cannot deny") | ||
| 1215 | } | 1267 | } | ||
| 1216 | 1268 | ||||
M internal/infrastructure/webkit/webview_permission_types_test.go +5 -0
| @@ -87,6 +87,11 @@ func TestClassifyPermissionRequestTypes(t *testing.T) { | |||
| 87 | 87 | kind: permissionRequestKindUnknown, | |
| 88 | 88 | expected: nil, | |
| 89 | 89 | }, | |
| 90 | + | { | |
| 91 | + | name: "website data access request", | |
| 92 | + | kind: permissionRequestKindWebsiteDataAccess, | |
| 93 | + | expected: []string{"website_data_access"}, | |
| 94 | + | }, | |
| 90 | 95 | } | |
| 91 | 96 | ||
| 92 | 97 | for _, tt := range tests { | |
| @@ -87,6 +87,11 @@ func TestClassifyPermissionRequestTypes(t *testing.T) { | |||||
| 87 | kind: permissionRequestKindUnknown, | 87 | kind: permissionRequestKindUnknown, | ||
| 88 | expected: nil, | 88 | expected: nil, | ||
| 89 | }, | 89 | }, | ||
| 90 | + | { | |||
| 91 | + | name: "website data access request", | |||
| 92 | + | kind: permissionRequestKindWebsiteDataAccess, | |||
| 93 | + | expected: []string{"website_data_access"}, | |||
| 94 | + | }, | |||
| 90 | } | 95 | } | ||
| 91 | 96 | ||||
| 92 | for _, tt := range tests { | 97 | for _, tt := range tests { | ||