add fingerprinting configuration

This commit is contained in:
Fabian Freund
2025-10-11 14:52:09 +02:00
parent 723f22e04c
commit 36a67ee09a
24 changed files with 642 additions and 10 deletions
+1
View File
@@ -43,6 +43,7 @@ import 'package:weblibre/features/settings/presentation/screens/addon_collection
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/developer_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/developer_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/settings.dart'; import 'package:weblibre/features/settings/presentation/screens/settings.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart'; import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
+27
View File
@@ -111,6 +111,11 @@ RouteBase get $settingsRoute => GoRouteData.$route(
name: 'DohSettingsRoute', name: 'DohSettingsRoute',
factory: $DohSettingsRoute._fromState, factory: $DohSettingsRoute._fromState,
), ),
GoRouteData.$route(
path: 'fingerprint',
name: 'FingerprintSettingsRoute',
factory: $FingerprintSettingsRoute._fromState,
),
], ],
), ),
GoRouteData.$route( GoRouteData.$route(
@@ -279,6 +284,28 @@ mixin $DohSettingsRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); void replace(BuildContext context) => context.replace(location);
} }
mixin $FingerprintSettingsRoute on GoRouteData {
static FingerprintSettingsRoute _fromState(GoRouterState state) =>
FingerprintSettingsRoute();
@override
String get location =>
GoRouteData.$location('/settings/web_engine/fingerprint');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin $DeveloperSettingsRoute on GoRouteData { mixin $DeveloperSettingsRoute on GoRouteData {
static DeveloperSettingsRoute _fromState(GoRouterState state) => static DeveloperSettingsRoute _fromState(GoRouterState state) =>
DeveloperSettingsRoute(); DeveloperSettingsRoute();
+12
View File
@@ -43,6 +43,10 @@ part of 'routes.dart';
], ],
), ),
TypedGoRoute<DohSettingsRoute>(name: 'DohSettingsRoute', path: 'doh'), TypedGoRoute<DohSettingsRoute>(name: 'DohSettingsRoute', path: 'doh'),
TypedGoRoute<FingerprintSettingsRoute>(
name: 'FingerprintSettingsRoute',
path: 'fingerprint',
),
], ],
), ),
TypedGoRoute<DeveloperSettingsRoute>( TypedGoRoute<DeveloperSettingsRoute>(
@@ -85,6 +89,14 @@ class DohSettingsRoute extends GoRouteData with $DohSettingsRoute {
} }
} }
class FingerprintSettingsRoute extends GoRouteData
with $FingerprintSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const FingerprintSettingsScreen();
}
}
class WebEngineSettingsRoute extends GoRouteData with $WebEngineSettingsRoute { class WebEngineSettingsRoute extends GoRouteData with $WebEngineSettingsRoute {
@override @override
Widget build(BuildContext context, GoRouterState state) { Widget build(BuildContext context, GoRouterState state) {
@@ -128,6 +128,12 @@ class EngineSettingsReplicationService
if (previous.value?.dohSettings != settings.dohSettings) { if (previous.value?.dohSettings != settings.dohSettings) {
await _service.dohSettings(settings.dohSettings); await _service.dohSettings(settings.dohSettings);
} }
if (previous.value?.fingerprintingProtectionOverrides !=
settings.fingerprintingProtectionOverrides) {
await _service.fingerprintingProtectionOverrides(
settings.fingerprintingProtectionOverrides,
);
}
} else { } else {
await _service.setDefaultSettings(settings); await _service.setDefaultSettings(settings);
initialSettingsSent = true; initialSettingsSent = true;
@@ -44,7 +44,7 @@ final class EngineSettingsReplicationServiceProvider
} }
String _$engineSettingsReplicationServiceHash() => String _$engineSettingsReplicationServiceHash() =>
r'0bbc80fe0cff79f9e419ffa8c486d232f9eac2ee'; r'3c96e6e45537399582fdf6e8b1fafc44c613b89d';
abstract class _$EngineSettingsReplicationService extends $Notifier<void> { abstract class _$EngineSettingsReplicationService extends $Notifier<void> {
void build(); void build();
@@ -0,0 +1,155 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
import 'package:weblibre/features/user/data/models/engine_settings.dart';
import 'package:weblibre/features/user/domain/entities/fingerprint_overrides.dart';
import 'package:weblibre/features/user/domain/providers.dart';
import 'package:weblibre/features/user/domain/services/fingerprinting.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
class FingerprintSettingsScreen extends HookConsumerWidget {
const FingerprintSettingsScreen();
@override
Widget build(BuildContext context, WidgetRef ref) {
final targetsAsync = ref.watch(fingerprintTargetsProvider);
final settingsAsync = ref.watch(fingerprintOverrideSettingsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Fingerprint Protection'),
actions: [
MenuAnchor(
builder: (context, controller, child) {
return IconButton(
onPressed: () {
controller.open();
},
icon: const Icon(Icons.more_vert),
);
},
menuChildren: [
MenuItemButton(
leadingIcon: const Icon(MdiIcons.restore),
child: const Text('Load Defaults'),
onPressed: () async {
await ref
.read(saveEngineSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings.copyWith
.fingerprintingProtectionOverrides(
FingerprintOverrides.defaults().toString(),
),
);
},
),
MenuItemButton(
leadingIcon: const Icon(MdiIcons.restore),
child: const Text('Load Hardened Defaults'),
onPressed: () async {
await ref
.read(saveEngineSettingsControllerProvider.notifier)
.save(
(currentSettings) => currentSettings.copyWith
.fingerprintingProtectionOverrides(
FingerprintOverrides.hardenedDefaults()
.toString(),
),
);
},
),
],
),
],
),
body: SafeArea(
child: settingsAsync.when(
data: (result) {
return result.fold(
(overrides) {
return targetsAsync.when(
data: (targets) {
return ListView.builder(
itemCount: targets.length,
itemBuilder: (context, index) {
final target = targets[index];
final state = overrides.targets[target.name];
return CheckboxListTile.adaptive(
value:
(overrides.allTargets == true &&
state != false) ||
state == true,
onChanged: (value) async {
if (value != null) {
final newOverrides = overrides
.copyWithTarget(target.name, value)
.toString();
await ref
.read(
saveEngineSettingsControllerProvider
.notifier,
)
.save(
(currentSettings) => currentSettings
.copyWith
.fingerprintingProtectionOverrides(
newOverrides,
),
);
}
},
title: Text(target.name),
subtitle: target.description.mapNotNull(
(desc) => Text(desc),
),
);
},
);
},
error: (error, stackTrace) {
return Center(
child: FailureWidget(
exception: error,
onRetry: () {
ref.invalidate(fingerprintTargetsProvider);
},
),
);
},
loading: () =>
const Center(child: CircularProgressIndicator()),
);
},
onFailure: (errorMessage) {
return Center(
child: FailureWidget(
title: errorMessage.message,
exception: errorMessage.details,
onRetry: () {
ref.invalidate(fingerprintOverrideSettingsProvider);
},
),
);
},
);
},
error: (error, stackTrace) {
return Center(
child: FailureWidget(
exception: error,
onRetry: () {
ref.invalidate(fingerprintOverrideSettingsProvider);
},
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
),
),
);
}
}
@@ -475,6 +475,18 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
await WebEngineHardeningRoute().push(context); await WebEngineHardeningRoute().push(context);
}, },
), ),
ListTile(
title: const Text('Fingerprint Protection'),
contentPadding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 16.0,
),
leading: const Icon(MdiIcons.fingerprint),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
await FingerprintSettingsRoute().push(context);
},
),
], ],
); );
}, },
@@ -24,6 +24,7 @@ import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:weblibre/features/user/domain/entities/fingerprint_overrides.dart';
part 'engine_settings.g.dart'; part 'engine_settings.g.dart';
@@ -125,6 +126,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
required this.dohProviderUrl, required this.dohProviderUrl,
required this.dohDefaultProviderUrl, required this.dohDefaultProviderUrl,
required this.dohExceptionsList, required this.dohExceptionsList,
required super.fingerprintingProtectionOverrides,
}); });
EngineSettings.withDefaults({ EngineSettings.withDefaults({
@@ -147,6 +149,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
String? dohProviderUrl, String? dohProviderUrl,
String? dohDefaultProviderUrl, String? dohDefaultProviderUrl,
List<String>? dohExceptionsList, List<String>? dohExceptionsList,
String? fingerprintingProtectionOverrides,
}) : queryParameterStripping = }) : queryParameterStripping =
queryParameterStripping ?? QueryParameterStripping.disabled, queryParameterStripping ?? QueryParameterStripping.disabled,
bounceTrackingProtectionMode = bounceTrackingProtectionMode =
@@ -177,6 +180,9 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
webContentIsolationStrategy ?? webContentIsolationStrategy ??
WebContentIsolationStrategy.isolateHighValue, WebContentIsolationStrategy.isolateHighValue,
enterpriseRootsEnabled: enterpriseRootsEnabled ?? false, enterpriseRootsEnabled: enterpriseRootsEnabled ?? false,
fingerprintingProtectionOverrides:
fingerprintingProtectionOverrides ??
FingerprintOverrides.defaults().toString(),
); );
static AddonCollection? _addonCollectionFromJson(String? json) => static AddonCollection? _addonCollectionFromJson(String? json) =>
@@ -201,5 +207,6 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
dohProviderUrl, dohProviderUrl,
dohDefaultProviderUrl, dohDefaultProviderUrl,
dohExceptionsList, dohExceptionsList,
fingerprintingProtectionOverrides,
]; ];
} }
@@ -61,6 +61,10 @@ abstract class _$EngineSettingsCWProxy {
EngineSettings dohExceptionsList(List<String> dohExceptionsList); EngineSettings dohExceptionsList(List<String> dohExceptionsList);
EngineSettings fingerprintingProtectionOverrides(
String? fingerprintingProtectionOverrides,
);
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
/// ///
@@ -88,6 +92,7 @@ abstract class _$EngineSettingsCWProxy {
String dohProviderUrl, String dohProviderUrl,
String dohDefaultProviderUrl, String dohDefaultProviderUrl,
List<String> dohExceptionsList, List<String> dohExceptionsList,
String? fingerprintingProtectionOverrides,
}); });
} }
@@ -188,6 +193,13 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
EngineSettings dohExceptionsList(List<String> dohExceptionsList) => EngineSettings dohExceptionsList(List<String> dohExceptionsList) =>
call(dohExceptionsList: dohExceptionsList); call(dohExceptionsList: dohExceptionsList);
@override
EngineSettings fingerprintingProtectionOverrides(
String? fingerprintingProtectionOverrides,
) => call(
fingerprintingProtectionOverrides: fingerprintingProtectionOverrides,
);
@override @override
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
@@ -218,6 +230,7 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
Object? dohProviderUrl = const $CopyWithPlaceholder(), Object? dohProviderUrl = const $CopyWithPlaceholder(),
Object? dohDefaultProviderUrl = const $CopyWithPlaceholder(), Object? dohDefaultProviderUrl = const $CopyWithPlaceholder(),
Object? dohExceptionsList = const $CopyWithPlaceholder(), Object? dohExceptionsList = const $CopyWithPlaceholder(),
Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
}) { }) {
return EngineSettings( return EngineSettings(
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder() javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
@@ -319,6 +332,11 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
? _value.dohExceptionsList ? _value.dohExceptionsList
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: dohExceptionsList as List<String>, : dohExceptionsList as List<String>,
fingerprintingProtectionOverrides:
fingerprintingProtectionOverrides == const $CopyWithPlaceholder()
? _value.fingerprintingProtectionOverrides
// ignore: cast_nullable_to_non_nullable
: fingerprintingProtectionOverrides as String?,
); );
} }
} }
@@ -388,12 +406,16 @@ EngineSettings _$EngineSettingsFromJson(Map<String, dynamic> json) =>
dohExceptionsList: (json['dohExceptionsList'] as List<dynamic>?) dohExceptionsList: (json['dohExceptionsList'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toList(), .toList(),
fingerprintingProtectionOverrides:
json['fingerprintingProtectionOverrides'] as String?,
); );
Map<String, dynamic> _$EngineSettingsToJson( Map<String, dynamic> _$EngineSettingsToJson(
EngineSettings instance, EngineSettings instance,
) => <String, dynamic>{ ) => <String, dynamic>{
'userAgent': instance.userAgent, 'userAgent': instance.userAgent,
'fingerprintingProtectionOverrides':
instance.fingerprintingProtectionOverrides,
'javascriptEnabled': instance.javascriptEnabled, 'javascriptEnabled': instance.javascriptEnabled,
'trackingProtectionPolicy': 'trackingProtectionPolicy':
_$TrackingProtectionPolicyEnumMap[instance.trackingProtectionPolicy]!, _$TrackingProtectionPolicyEnumMap[instance.trackingProtectionPolicy]!,
@@ -0,0 +1,27 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart';
part 'rfp_target.g.dart';
@JsonSerializable()
class RFPTarget with FastEquatable {
final String name;
final int id;
final String? description;
final List<String> keywords;
RFPTarget({
required this.name,
required this.id,
this.description,
required this.keywords,
});
factory RFPTarget.fromJson(Map<String, dynamic> json) =>
_$RFPTargetFromJson(json);
Map<String, dynamic> toJson() => _$RFPTargetToJson(this);
@override
List<Object?> get hashParameters => [name, id, description, keywords];
}
@@ -0,0 +1,23 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'rfp_target.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
RFPTarget _$RFPTargetFromJson(Map<String, dynamic> json) => RFPTarget(
name: json['name'] as String,
id: (json['id'] as num).toInt(),
description: json['description'] as String?,
keywords: (json['keywords'] as List<dynamic>)
.map((e) => e as String)
.toList(),
);
Map<String, dynamic> _$RFPTargetToJson(RFPTarget instance) => <String, dynamic>{
'name': instance.name,
'id': instance.id,
'description': instance.description,
'keywords': instance.keywords,
};
@@ -0,0 +1,174 @@
import 'package:exceptions/exceptions.dart';
import 'package:fast_equatable/fast_equatable.dart';
class FingerprintOverrides with FastEquatable {
static final pattern = RegExp('([+-])([a-zA-Z_][a-zA-Z0-9_]{1,64})');
final bool? allTargets;
final Map<String, bool> targets;
FingerprintOverrides(this.allTargets, this.targets);
//Monitor https://searchfox.org/firefox-main/source/toolkit/components/resistfingerprinting/RFPTargetsDefault.inc
FingerprintOverrides.defaults()
: this(false, {
'CanvasRandomization': true,
'EfficientCanvasRandomization': true,
'FontVisibilityLangPack': true,
'JSMathFdlibm': true,
'ScreenAvailToResolution': true,
'NavigatorHWConcurrencyTiered': true,
'MaxTouchPointsCollapse': true,
});
FingerprintOverrides.hardenedDefaults()
: this(false, {
'TouchEvents': true,
'PointerEvents': true,
'KeyboardEvents': true,
'ScreenOrientation': true,
'SpeechSynthesis': true,
'CSSPrefersReducedMotion': true,
'CSSPrefersContrast': true,
'CanvasRandomization': true,
'CanvasExtractionFromThirdPartiesIsBlocked': true,
'JSLocale': true,
'NavigatorAppVersion': true,
'NavigatorBuildID': true,
'NavigatorHWConcurrency': true,
'NavigatorOscpu': true,
'NavigatorPlatform': true,
'NavigatorUserAgent': true,
'PointerId': true,
'StreamVideoFacingMode': true,
'JSDateTimeUTC': true,
'JSMathFdlibm': true,
'Gamepad': true,
'HttpUserAgent': true,
'WindowOuterSize': true,
'WindowScreenXY': true,
'WindowInnerScreenXY': true,
'ScreenPixelDepth': true,
'ScreenRect': true,
'ScreenAvailRect': true,
'VideoElementMozFrames': true,
'VideoElementMozFrameDelay': true,
'VideoElementPlaybackQuality': true,
'ReduceTimerPrecision': true,
'WidgetEvents': true,
'MediaDevices': true,
'MediaCapabilities': true,
'AudioSampleRate': true,
'NetworkConnection': true,
'WindowDevicePixelRatio': true,
'MouseEventScreenPoint': true,
'FontVisibilityBaseSystem': true,
'FontVisibilityLangPack': true,
'DeviceSensors': true,
'RoundWindowSize': true,
'UseStandinsForNativeColors': true,
'AudioContext': true,
'MediaError': true,
'DOMStyleOsxFontSmoothing': true,
'CSSDeviceSize': true,
'CSSColorInfo': true,
'CSSResolution': true,
'CSSPrefersReducedTransparency': true,
'CSSInvertedColors': true,
'CSSVideoDynamicRange': true,
'CSSPointerCapabilities': true,
'WebGLRenderCapability': true,
'WebGLRenderInfo': true,
'SiteSpecificZoom': true,
'FontVisibilityRestrictGenerics': true,
'WebVTT': true,
'WebGPULimits': true,
'WebGPUIsFallbackAdapter': true,
'WebGPUSubgroupSizes': true,
'JSLocalePrompt': true,
'ScreenAvailToResolution': true,
'UseHardcodedFontSubstitutes': true,
'DiskStorageLimit': true,
'WebCodecs': true,
'MaxTouchPoints': true,
'MaxTouchPointsCollapse': true,
'NavigatorHWConcurrencyTiered': true,
});
static Result<FingerprintOverrides> parse(
String input,
Set<String> availableTargets,
) {
final cleaned = input.replaceAll(RegExp(r'\s'), '');
if (cleaned.isEmpty) return Result.success(FingerprintOverrides(null, {}));
bool? allTargets;
final targets = <String, bool>{};
for (final word in cleaned.split(',')) {
final match = pattern.firstMatch(word);
if (match == null) {
return Result.failure(
const ErrorMessage(source: 'FpParser', message: 'Invalid Override'),
);
}
final enabled = match.group(1) != '-';
final name = match.group(2)!;
if (name == 'AllTargets') {
allTargets = enabled;
continue;
}
if (!availableTargets.contains(name)) {
return Result.failure(
const ErrorMessage(
source: 'FpParser',
message: 'Invalid target name',
),
);
}
targets[name] = enabled;
}
return Result.success(FingerprintOverrides(allTargets, targets));
}
@override
String toString() {
final sb = StringBuffer();
if (allTargets != null) {
sb.write('${allTargets! ? '+' : '-'}AllTargets');
if (targets.isNotEmpty) {
sb.write(',');
}
}
sb.write(
targets.entries.map((e) => (e.value ? '+' : '-') + e.key).join(','),
);
return sb.toString();
}
FingerprintOverrides copyWithAllTargetsEnabled(bool value) {
return FingerprintOverrides(
value,
Map.fromEntries(targets.entries.where((e) => e.value != value)),
);
}
FingerprintOverrides copyWithTarget(String name, bool value) {
if (value && allTargets == true) {
return this;
}
return FingerprintOverrides(allTargets, {...targets, name: value});
}
@override
List<Object?> get hashParameters => [allTargets, targets];
}
@@ -17,11 +17,16 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import 'package:exceptions/exceptions.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:nullability/nullability.dart';
import 'package:riverpod/riverpod.dart'; import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/user/data/providers.dart'; import 'package:weblibre/features/user/data/providers.dart';
import 'package:weblibre/features/user/domain/entities/fingerprint_overrides.dart';
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/features/user/domain/services/fingerprinting.dart';
part 'providers.g.dart'; part 'providers.g.dart';
@@ -47,3 +52,24 @@ bool incognitoModeEnabled(Ref ref) {
), ),
); );
} }
@Riverpod()
Future<Result<FingerprintOverrides>> fingerprintOverrideSettings(
Ref ref,
) async {
final fingerprintTargets = await ref.watch(fingerprintTargetsProvider.future);
final fingerprintTargetSet = fingerprintTargets.map((e) => e.name).toSet();
final overrides = ref.watch(
engineSettingsWithDefaultsProvider.select(
(settings) =>
settings.fingerprintingProtectionOverrides.mapNotNull(
(settings) =>
FingerprintOverrides.parse(settings, fingerprintTargetSet),
) ??
Result.success(FingerprintOverrides.defaults()),
),
);
return overrides;
}
@@ -117,3 +117,46 @@ final class IncognitoModeEnabledProvider
String _$incognitoModeEnabledHash() => String _$incognitoModeEnabledHash() =>
r'36957b70a5261f9d3ad228e07cc8dd5c8f616082'; r'36957b70a5261f9d3ad228e07cc8dd5c8f616082';
@ProviderFor(fingerprintOverrideSettings)
const fingerprintOverrideSettingsProvider =
FingerprintOverrideSettingsProvider._();
final class FingerprintOverrideSettingsProvider
extends
$FunctionalProvider<
AsyncValue<Result<FingerprintOverrides>>,
Result<FingerprintOverrides>,
FutureOr<Result<FingerprintOverrides>>
>
with
$FutureModifier<Result<FingerprintOverrides>>,
$FutureProvider<Result<FingerprintOverrides>> {
const FingerprintOverrideSettingsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'fingerprintOverrideSettingsProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$fingerprintOverrideSettingsHash();
@$internal
@override
$FutureProviderElement<Result<FingerprintOverrides>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<Result<FingerprintOverrides>> create(Ref ref) {
return fingerprintOverrideSettings(ref);
}
}
String _$fingerprintOverrideSettingsHash() =>
r'd4d40ec425098fb1f5a2f0c4944f058829a41a0a';
@@ -114,6 +114,11 @@ class EngineSettingsRepository extends _$EngineSettingsRepository {
DriftSqlType.string, DriftSqlType.string,
db.typeMapping, db.typeMapping,
), ),
'fingerprintingProtectionOverrides':
settings['fingerprintingProtectionOverrides']?.readAs(
DriftSqlType.string,
db.typeMapping,
),
}); });
} }
@@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider
} }
String _$engineSettingsRepositoryHash() => String _$engineSettingsRepositoryHash() =>
r'5e403757036edb4ef964b01cdcf96cbcbdc4d809'; r'33acd89e783d8d91b9b56e86411fdfcab24a2c54';
abstract class _$EngineSettingsRepository abstract class _$EngineSettingsRepository
extends $StreamNotifier<EngineSettings> { extends $StreamNotifier<EngineSettings> {
@@ -0,0 +1,20 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/user/data/models/rfp_target.dart';
part 'fingerprinting.g.dart';
@Riverpod(keepAlive: true)
Future<List<RFPTarget>> fingerprintTargets(Ref ref) async {
final json =
await rootBundle
.loadString('assets/preferences/rfp_targets.json')
.then(jsonDecode)
as List<dynamic>;
return json
.map((e) => RFPTarget.fromJson(e as Map<String, dynamic>))
.toList();
}
@@ -0,0 +1,50 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'fingerprinting.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(fingerprintTargets)
const fingerprintTargetsProvider = FingerprintTargetsProvider._();
final class FingerprintTargetsProvider
extends
$FunctionalProvider<
AsyncValue<List<RFPTarget>>,
List<RFPTarget>,
FutureOr<List<RFPTarget>>
>
with $FutureModifier<List<RFPTarget>>, $FutureProvider<List<RFPTarget>> {
const FingerprintTargetsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'fingerprintTargetsProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$fingerprintTargetsHash();
@$internal
@override
$FutureProviderElement<List<RFPTarget>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<List<RFPTarget>> create(Ref ref) {
return fingerprintTargets(ref);
}
}
String _$fingerprintTargetsHash() =>
r'1ec5933a82941b84fdad2130ccf1ba2156ddc33a';
+6 -6
View File
@@ -33,7 +33,7 @@ dependencies:
google_fonts: ^6.3.2 google_fonts: ^6.3.2
graphview: ^1.5.0 graphview: ^1.5.0
home_widget: ^0.8.0 home_widget: ^0.8.0
hooks_riverpod: ^3.0.2 hooks_riverpod: ^3.0.3
html: ^0.15.6 html: ^0.15.6
http: ^1.5.0 http: ^1.5.0
http_parser: ^4.1.2 http_parser: ^4.1.2
@@ -55,8 +55,8 @@ dependencies:
path: ../packages/pluggable_transports_proxy path: ../packages/pluggable_transports_proxy
pretty_qr_code: ^3.5.0 pretty_qr_code: ^3.5.0
quick_actions: ^1.1.0 quick_actions: ^1.1.0
riverpod: ^3.0.2 riverpod: ^3.0.3
riverpod_annotation: ^3.0.2 riverpod_annotation: ^3.0.3
rss_dart: ^1.0.13 rss_dart: ^1.0.13
rxdart: ^0.28.0 rxdart: ^0.28.0
share_plus: ^12.0.0 share_plus: ^12.0.0
@@ -69,7 +69,7 @@ dependencies:
speech_to_text_google_dialog: speech_to_text_google_dialog:
git: git:
url: https://github.com/FaFre/speech_to_text_google_dialog.git url: https://github.com/FaFre/speech_to_text_google_dialog.git
sqlite3: ^2.9.1 sqlite3: ^2.9.2
sqlite3_flutter_libs: ^0.5.40 sqlite3_flutter_libs: ^0.5.40
synchronized: ^3.4.0 synchronized: ^3.4.0
text_scroll: ^0.2.1 text_scroll: ^0.2.1
@@ -96,8 +96,8 @@ dev_dependencies:
go_router_builder: ^4.1.0 go_router_builder: ^4.1.0
json_serializable: ^6.11.1 json_serializable: ^6.11.1
lint: ^2.8.0 lint: ^2.8.0
riverpod_generator: ^3.0.2 riverpod_generator: ^3.0.3
riverpod_lint: ^3.0.2 riverpod_lint: ^3.0.3
flutter: flutter:
uses-material-design: true uses-material-design: true
@@ -144,6 +144,9 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
components.core.engineSettings.dohDefaultProviderUrl = settings.dohSettings.dohDefaultProviderUrl components.core.engineSettings.dohDefaultProviderUrl = settings.dohSettings.dohDefaultProviderUrl
components.core.engineSettings.dohExceptionsList = settings.dohSettings.dohExceptionsList components.core.engineSettings.dohExceptionsList = settings.dohSettings.dohExceptionsList
} }
if(settings.fingerprintingProtectionOverrides != null) {
components.core.engineSettings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides
}
} }
override fun updateRuntimeSettings(settings: GeckoEngineSettings) { override fun updateRuntimeSettings(settings: GeckoEngineSettings) {
@@ -227,6 +230,9 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
components.core.engine.settings.dohDefaultProviderUrl = settings.dohSettings.dohDefaultProviderUrl components.core.engine.settings.dohDefaultProviderUrl = settings.dohSettings.dohDefaultProviderUrl
components.core.engine.settings.dohExceptionsList = settings.dohSettings.dohExceptionsList components.core.engine.settings.dohExceptionsList = settings.dohSettings.dohExceptionsList
} }
if(settings.fingerprintingProtectionOverrides != null) {
components.core.engine.settings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides
}
if(reloadSession) { if(reloadSession) {
components.useCases.sessionUseCases.reload() components.useCases.sessionUseCases.reload()
@@ -1677,7 +1677,8 @@ data class GeckoEngineSettings (
val userAgent: String? = null, val userAgent: String? = null,
val contentBlocking: ContentBlocking? = null, val contentBlocking: ContentBlocking? = null,
val enterpriseRootsEnabled: Boolean? = null, val enterpriseRootsEnabled: Boolean? = null,
val dohSettings: DohSettings? = null val dohSettings: DohSettings? = null,
val fingerprintingProtectionOverrides: String? = null
) )
{ {
companion object { companion object {
@@ -1696,7 +1697,8 @@ data class GeckoEngineSettings (
val contentBlocking = pigeonVar_list[11] as ContentBlocking? val contentBlocking = pigeonVar_list[11] as ContentBlocking?
val enterpriseRootsEnabled = pigeonVar_list[12] as Boolean? val enterpriseRootsEnabled = pigeonVar_list[12] as Boolean?
val dohSettings = pigeonVar_list[13] as DohSettings? val dohSettings = pigeonVar_list[13] as DohSettings?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings) val fingerprintingProtectionOverrides = pigeonVar_list[14] as String?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings, fingerprintingProtectionOverrides)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
@@ -1715,6 +1717,7 @@ data class GeckoEngineSettings (
contentBlocking, contentBlocking,
enterpriseRootsEnabled, enterpriseRootsEnabled,
dohSettings, dohSettings,
fingerprintingProtectionOverrides,
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -94,4 +94,10 @@ class GeckoEngineSettingsService {
Future<void> dohSettings(DohSettings state) { Future<void> dohSettings(DohSettings state) {
return _api.updateRuntimeSettings(GeckoEngineSettings(dohSettings: state)); return _api.updateRuntimeSettings(GeckoEngineSettings(dohSettings: state));
} }
Future<void> fingerprintingProtectionOverrides(String? state) {
return _api.updateRuntimeSettings(
GeckoEngineSettings(fingerprintingProtectionOverrides: state),
);
}
} }
@@ -2043,6 +2043,7 @@ class GeckoEngineSettings {
this.contentBlocking, this.contentBlocking,
this.enterpriseRootsEnabled, this.enterpriseRootsEnabled,
this.dohSettings, this.dohSettings,
this.fingerprintingProtectionOverrides,
}); });
bool? javascriptEnabled; bool? javascriptEnabled;
@@ -2073,6 +2074,8 @@ class GeckoEngineSettings {
DohSettings? dohSettings; DohSettings? dohSettings;
String? fingerprintingProtectionOverrides;
List<Object?> _toList() { List<Object?> _toList() {
return <Object?>[ return <Object?>[
javascriptEnabled, javascriptEnabled,
@@ -2089,6 +2092,7 @@ class GeckoEngineSettings {
contentBlocking, contentBlocking,
enterpriseRootsEnabled, enterpriseRootsEnabled,
dohSettings, dohSettings,
fingerprintingProtectionOverrides,
]; ];
} }
@@ -2112,6 +2116,7 @@ class GeckoEngineSettings {
contentBlocking: result[11] as ContentBlocking?, contentBlocking: result[11] as ContentBlocking?,
enterpriseRootsEnabled: result[12] as bool?, enterpriseRootsEnabled: result[12] as bool?,
dohSettings: result[13] as DohSettings?, dohSettings: result[13] as DohSettings?,
fingerprintingProtectionOverrides: result[14] as String?,
); );
} }
@@ -666,6 +666,7 @@ class GeckoEngineSettings {
final ContentBlocking? contentBlocking; final ContentBlocking? contentBlocking;
final bool? enterpriseRootsEnabled; final bool? enterpriseRootsEnabled;
final DohSettings? dohSettings; final DohSettings? dohSettings;
final String? fingerprintingProtectionOverrides;
GeckoEngineSettings( GeckoEngineSettings(
this.javascriptEnabled, this.javascriptEnabled,
@@ -682,6 +683,7 @@ class GeckoEngineSettings {
this.contentBlocking, this.contentBlocking,
this.enterpriseRootsEnabled, this.enterpriseRootsEnabled,
this.dohSettings, this.dohSettings,
this.fingerprintingProtectionOverrides,
); );
} }