Add use orbot/invizible checkbox to tor settings screen
What changed, and why it matters
This commit adds a user-facing checkbox that lets mobile users choose to route the app's Tor traffic through the separate Orbot/InviZible app instead of a manually entered proxy port. It also changes the default external Tor port from 9150 to 9050 and makes a hardcoded error message translatable. There is no obvious security vulnerability in the diff; it is a feature/configuration improvement.
No immediate security action required. As a defensive review, verify that the actual proxy connection code (`getProxy()` and downstream SOCKS HTTP usage) correctly consumes the `useOrbot` setting and does not silently fall back to a different proxy when Orbot is unreachable. Also confirm that port 9050 is only used in external mode and does not conflict with built-in Tor.
Security signals we found
No injection or unsafe input handling observed
No changes to authentication, key storage, or wallet logic
Default external Tor port changed from 9150 to 9050 (Orbot convention)
New setting persisted via shared_preferences as a boolean
UI-only disabling of SOCKS port field when Orbot/InviZible is selected
Evidence from the diff
The patch introduces a persisted boolean setting torUseOrbot and exposes it on the Tor settings screen only when the app runs on Android or iOS and external Tor mode is selected. When enabled, the SOCKS port field is disabled and forced to 9050 (Orbot’s default). The default external SOCKS port in TorSettingsService is changed from 9150 to 9050. Localization strings are added for the new label and an existing disabled-Tor error message. Two unused imports are removed from wallet_model.dart. No networking, cryptographic, or authentication logic is altered.
Changed components
lib/services/tor_settings_service.dartlib/widgets/tor_settings_form.dartlib/services/shared_preferences_service.dartlib/screens/connection_setup.dartlib/l10n/*Inspect captured patch +75 / −9
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 29e6cc1..0404d1f 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -170,6 +170,7 @@
"torSettingsModeDisabled": "No Tor",
"torSettingsSocksPortLabel": "SOCKS Port",
"torSettingsSocksPortHint": "e.g. 9050",
+ "torSettingsUseOrbotLabel": "Use Orbot/InviZible",
"torSettingsSaveButton": "Save",
"torSettingsTestConnectionButton": "Test Connection",
"settingsTorSettingsLabel": "Tor Settings",
@@ -181,5 +182,6 @@
"type": "String"
}
}
- }
+ },
+ "connectionSetupTorDisabledError": "Tor is disabled. Please go back and enable it."
}
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index 50ac05d..6d19609 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -1071,6 +1071,12 @@ abstract class AppLocalizations {
/// **'e.g. 9050'**
String get torSettingsSocksPortHint;
+ /// No description provided for @torSettingsUseOrbotLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Use Orbot/InviZible'**
+ String get torSettingsUseOrbotLabel;
+
/// No description provided for @torSettingsSaveButton.
///
/// In en, this message translates to:
@@ -1100,6 +1106,12 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Using external Tor proxy at {address}'**
String connectionSetupUsingExternalTor(String address);
+
+ /// No description provided for @connectionSetupTorDisabledError.
+ ///
+ /// In en, this message translates to:
+ /// **'Tor is disabled. Please go back and enable it.'**
+ String get connectionSetupTorDisabledError;
}
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index 9704bc0..5da4ab0 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -517,6 +517,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get torSettingsSocksPortHint => 'e.g. 9050';
+ @override
+ String get torSettingsUseOrbotLabel => 'Use Orbot/InviZible';
+
@override
String get torSettingsSaveButton => 'Save';
@@ -533,4 +536,7 @@ class AppLocalizationsEn extends AppLocalizations {
String connectionSetupUsingExternalTor(String address) {
return 'Using external Tor proxy at $address';
}
+
+ @override
+ String get connectionSetupTorDisabledError => 'Tor is disabled. Please go back and enable it.';
}
diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart
index e7f315d..f85a412 100644
--- a/lib/l10n/app_localizations_pt.dart
+++ b/lib/l10n/app_localizations_pt.dart
@@ -517,6 +517,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get torSettingsSocksPortHint => 'ex: 9050';
+ @override
+ String get torSettingsUseOrbotLabel => 'Usar Orbot/InviZible';
+
@override
String get torSettingsSaveButton => 'Salvar';
@@ -533,4 +536,8 @@ class AppLocalizationsPt extends AppLocalizations {
String connectionSetupUsingExternalTor(String address) {
return 'Usando proxy Tor externo em $address';
}
+
+ @override
+ String get connectionSetupTorDisabledError =>
+ 'O Tor está desativado. Por favor, volte e ative-o.';
}
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 47a2df0..39605dc 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -170,6 +170,7 @@
"torSettingsModeDisabled": "Sem Tor",
"torSettingsSocksPortLabel": "Porta SOCKS",
"torSettingsSocksPortHint": "ex: 9050",
+ "torSettingsUseOrbotLabel": "Usar Orbot/InviZible",
"torSettingsSaveButton": "Salvar",
"torSettingsTestConnectionButton": "Testar Conexão",
"settingsTorSettingsLabel": "Configurações do Tor",
@@ -181,5 +182,6 @@
"type": "String"
}
}
- }
+ },
+ "connectionSetupTorDisabledError": "O Tor está desativado. Por favor, volte e ative-o."
}
diff --git a/lib/models/wallet_model.dart b/lib/models/wallet_model.dart
index d67ff5f..fcbfbcb 100644
--- a/lib/models/wallet_model.dart
+++ b/lib/models/wallet_model.dart
@@ -13,7 +13,6 @@ import 'package:monero/src/wallet2.dart';
import 'package:http/http.dart' as http;
import 'package:polyseed/polyseed.dart';
import 'package:bip39/bip39.dart' as bip39;
-import 'package:skylight_wallet/models/fiat_rate_model.dart';
import 'package:skylight_wallet/services/notifications_service.dart';
import 'package:skylight_wallet/services/shared_preferences_service.dart';
@@ -23,7 +22,6 @@ import 'package:skylight_wallet/util/bip39.dart';
import 'package:skylight_wallet/util/cacert.dart';
import 'package:skylight_wallet/util/formatting.dart';
import 'package:skylight_wallet/util/get_height_by_date.dart';
-import 'package:skylight_wallet/util/height.dart';
import 'package:skylight_wallet/util/logging.dart';
import 'package:skylight_wallet/util/socks_http.dart';
import 'package:skylight_wallet/util/wallet.dart';
diff --git a/lib/screens/connection_setup.dart b/lib/screens/connection_setup.dart
index d150625..7021470 100644
--- a/lib/screens/connection_setup.dart
+++ b/lib/screens/connection_setup.dart
@@ -155,6 +155,7 @@ class _ConnectionSetupScreenState extends State<ConnectionSetupScreen> {
}
Future _testConnection() async {
+ final i18n = AppLocalizations.of(context)!;
final proto = _useSsl ? 'https' : 'http';
final daemonAddress = cleanAddress(_addressController.text);
final customProxyPort = _customProxyPortController.text;
@@ -192,7 +193,7 @@ class _ConnectionSetupScreenState extends State<ConnectionSetupScreen> {
// show error toast
ScaffoldMessenger.of(
context,
- ).showSnackBar(SnackBar(content: Text("Tor is disabled. Please go back and enable it.")));
+ ).showSnackBar(SnackBar(content: Text(i18n.connectionSetupTorDisabledError)));
return;
}
diff --git a/lib/services/shared_preferences_service.dart b/lib/services/shared_preferences_service.dart
index ec2f65f..7241f13 100644
--- a/lib/services/shared_preferences_service.dart
+++ b/lib/services/shared_preferences_service.dart
@@ -21,6 +21,7 @@ class SharedPreferencesKeys {
static const String unusedSubaddressIndexIsSupported = 'unusedSubaddressIndexIsSupported';
static const String torMode = 'torMode';
static const String torSocksPort = 'torSocksPort';
+ static const String torUseOrbot = 'torUseOrbot';
}
class SharedPreferencesService {
diff --git a/lib/services/tor_settings_service.dart b/lib/services/tor_settings_service.dart
index 135d1bd..6d51c20 100644
--- a/lib/services/tor_settings_service.dart
+++ b/lib/services/tor_settings_service.dart
@@ -9,10 +9,12 @@ class TorSettingsService {
static final TorSettingsService sharedInstance = TorSettingsService._();
TorMode _torMode = TorMode.builtIn;
- String _socksPort = '9150';
+ String _socksPort = '9050';
+ bool _useOrbot = false;
TorMode get torMode => _torMode;
String get socksPort => _socksPort;
+ bool get useOrbot => _useOrbot;
TorSettingsService._();
@@ -23,6 +25,9 @@ class TorSettingsService {
final String? socksPortString = await SharedPreferencesService.get<String>(
SharedPreferencesKeys.torSocksPort,
);
+ final bool? useOrbotValue = await SharedPreferencesService.get<bool>(
+ SharedPreferencesKeys.torUseOrbot,
+ );
if (torModeString != null) {
_torMode = _torModeFromString(torModeString);
@@ -31,9 +36,13 @@ class TorSettingsService {
if (socksPortString != null) {
_socksPort = socksPortString;
}
+
+ if (useOrbotValue != null) {
+ _useOrbot = useOrbotValue;
+ }
}
- Future<void> save({required TorMode torMode, String? socksPort}) async {
+ Future<void> save({required TorMode torMode, String? socksPort, bool? useOrbot}) async {
_torMode = torMode;
await SharedPreferencesService.set<String>(
SharedPreferencesKeys.torMode,
@@ -44,6 +53,11 @@ class TorSettingsService {
_socksPort = socksPort;
await SharedPreferencesService.set<String>(SharedPreferencesKeys.torSocksPort, socksPort);
}
+
+ if (useOrbot != null) {
+ _useOrbot = useOrbot;
+ await SharedPreferencesService.set<bool>(SharedPreferencesKeys.torUseOrbot, useOrbot);
+ }
}
Future<({InternetAddress host, int port})?> getProxy() async {
diff --git a/lib/widgets/tor_settings_form.dart b/lib/widgets/tor_settings_form.dart
index 691605f..4ead5fe 100644
--- a/lib/widgets/tor_settings_form.dart
+++ b/lib/widgets/tor_settings_form.dart
@@ -20,6 +20,7 @@ class TorSettingsForm extends StatefulWidget {
class _TorSettingsFormState extends State<TorSettingsForm> {
TorMode _selectedMode = TorMode.builtIn;
final TextEditingController _socksPortController = TextEditingController();
+ bool _useOrbot = false;
bool _isTestingConnection = false;
bool _hasTested = false;
@@ -35,13 +36,18 @@ class _TorSettingsFormState extends State<TorSettingsForm> {
final torSettings = TorSettingsService.sharedInstance;
setState(() {
_selectedMode = torSettings.torMode;
- _socksPortController.text = torSettings.socksPort;
+ _useOrbot = torSettings.useOrbot;
+ _socksPortController.text = _useOrbot ? '9050' : torSettings.socksPort;
});
}
Future<void> _saveSettings() async {
final torSettings = TorSettingsService.sharedInstance;
- await torSettings.save(torMode: _selectedMode, socksPort: _socksPortController.text);
+ await torSettings.save(
+ torMode: _selectedMode,
+ socksPort: _socksPortController.text,
+ useOrbot: _useOrbot,
+ );
}
void _onSavePressed() async {
@@ -126,6 +132,7 @@ class _TorSettingsFormState extends State<TorSettingsForm> {
if (_selectedMode == TorMode.external)
TextFormField(
controller: _socksPortController,
+ enabled: !_useOrbot || !(Platform.isAndroid || Platform.isIOS),
decoration: InputDecoration(
labelText: i18n.torSettingsSocksPortLabel,
hintText: i18n.torSettingsSocksPortHint,
@@ -143,6 +150,22 @@ class _TorSettingsFormState extends State<TorSettingsForm> {
});
},
),
+ if (_selectedMode == TorMode.external && (Platform.isAndroid || Platform.isIOS))
+ CheckboxListTile(
+ title: Text(i18n.torSettingsUseOrbotLabel),
+ value: _useOrbot,
+ onChanged: (value) {
+ setState(() {
+ _useOrbot = value ?? false;
+ if (_useOrbot) {
+ _socksPortController.text = '9050';
+ }
+ _hasTested = false;
+ });
+ },
+ controlAffinity: ListTileControlAffinity.leading,
+ contentPadding: EdgeInsets.zero,
+ ),
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.