mirror of
https://github.com/we-promise/sure.git
synced 2026-05-24 21:14:56 +00:00
* Add mobile custom proxy headers * Clear login placeholders on focus Email/password fields ship with example values pre-filled. Tapping the field now clears the placeholder so users don't have to delete it manually. Skips clearing if the user has already edited the value. * Push Configuration as a route from Sign in Opening Configuration from the Sign in screen now uses Navigator.push instead of toggling a state flag, so Android back returns to Sign in instead of quitting the app. Saving the URL auto-pops the route. * Address PR review on custom proxy headers - Test Connection no longer leaves global ApiConfig headers mutated; unsaved edits are restored in a finally block after the probe. - _loadSavedUrl / _loadCustomHeaders wrap storage reads in try/catch and always finish initialization with sensible defaults. - Sanitization is now a single CustomProxyHeader.sanitize() reused by ApiConfig.setCustomProxyHeaders and CustomProxyHeadersService. - Brief comment on redactedValue explaining the length-obscuring design. * Harden custom proxy header validation and load path - validateValue now rejects ASCII control characters (CR/LF/tab/etc.) to prevent header-injection via crafted values. - loadHeaders moves the secure-storage read inside the try block so platform exceptions are caught the same way JSON parse errors are.
48 lines
1.3 KiB
Dart
48 lines
1.3 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
|
|
import '../models/custom_proxy_header.dart';
|
|
|
|
class CustomProxyHeadersService {
|
|
static const String storageKey = 'custom_proxy_headers';
|
|
|
|
static CustomProxyHeadersService? _instance;
|
|
|
|
CustomProxyHeadersService._();
|
|
|
|
static CustomProxyHeadersService get instance {
|
|
_instance ??= CustomProxyHeadersService._();
|
|
return _instance!;
|
|
}
|
|
|
|
Future<List<CustomProxyHeader>> loadHeaders() async {
|
|
const storage = FlutterSecureStorage();
|
|
try {
|
|
final raw = await storage.read(key: storageKey);
|
|
if (raw == null || raw.isEmpty) return [];
|
|
|
|
final decoded = jsonDecode(raw);
|
|
if (decoded is! List) return [];
|
|
|
|
return CustomProxyHeader.sanitize(
|
|
decoded
|
|
.whereType<Map>()
|
|
.map((item) => CustomProxyHeader.fromJson(Map<String, dynamic>.from(item)))
|
|
.toList(),
|
|
);
|
|
} catch (_) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
Future<void> saveHeaders(List<CustomProxyHeader> headers) async {
|
|
const storage = FlutterSecureStorage();
|
|
final sanitized = CustomProxyHeader.sanitize(headers);
|
|
await storage.write(
|
|
key: storageKey,
|
|
value: jsonEncode(sanitized.map((header) => header.toJson()).toList()),
|
|
);
|
|
}
|
|
}
|