-
-
Notifications
You must be signed in to change notification settings - Fork 128
fix: Storage safety, gateway stability, auth UX, canvas, deps (#21, #54, #55, #56, #59, #60, #61, #63, #64, #65, #66, #67) #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
a89e595
a94c515
448d679
1201009
0c94229
720d89d
5e8144f
0140474
67a3969
1f41fe7
5980af5
ecd552f
60852aa
3618c93
414dd17
33773de
6418a81
8409df6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <!-- fullBackupContent rules for Android ≤11 --> | ||
| <full-backup-content> | ||
| <include domain="sharedpref" path="." /> | ||
| </full-backup-content> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <!-- dataExtractionRules for Android 12+ --> | ||
| <data-extraction-rules> | ||
| <cloud-backup> | ||
| <include domain="sharedpref" path="." /> | ||
| </cloud-backup> | ||
| <device-transfer> | ||
| <include domain="sharedpref" path="." /> | ||
| </device-transfer> | ||
| </data-extraction-rules> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <resources> | ||
| <usb-device /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Overly permissive USB device filter — Consider restricting to common USB-serial adapter vendor/product IDs (FTDI, CP210x, CH340, PL2303) or at minimum filtering by device class. For example: Prompt for AI agents |
||
| </resources> | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ import '../constants.dart'; | |||||||||||||||||||||||||||||||||||||
| import '../providers/node_provider.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../services/native_bridge.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../services/preferences_service.dart'; | ||||||||||||||||||||||||||||||||||||||
| import '../services/update_service.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'node_screen.dart'; | ||||||||||||||||||||||||||||||||||||||
| import 'setup_wizard_screen.dart'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,6 +33,7 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||||||||||||||||||||||||||||||||
| bool _brewInstalled = false; | ||||||||||||||||||||||||||||||||||||||
| bool _sshInstalled = false; | ||||||||||||||||||||||||||||||||||||||
| bool _storageGranted = false; | ||||||||||||||||||||||||||||||||||||||
| bool _checkingUpdate = false; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||
| void initState() { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -247,6 +249,18 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||||||||||||||||||||||||||||||||
| leading: Icon(Icons.info_outline), | ||||||||||||||||||||||||||||||||||||||
| isThreeLine: true, | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ListTile( | ||||||||||||||||||||||||||||||||||||||
| title: const Text('Check for Updates'), | ||||||||||||||||||||||||||||||||||||||
| subtitle: const Text('Check GitHub for a newer release'), | ||||||||||||||||||||||||||||||||||||||
| leading: _checkingUpdate | ||||||||||||||||||||||||||||||||||||||
| ? const SizedBox( | ||||||||||||||||||||||||||||||||||||||
| width: 24, | ||||||||||||||||||||||||||||||||||||||
| height: 24, | ||||||||||||||||||||||||||||||||||||||
| child: CircularProgressIndicator(strokeWidth: 2), | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| : const Icon(Icons.system_update), | ||||||||||||||||||||||||||||||||||||||
| onTap: _checkingUpdate ? null : _checkForUpdates, | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| const ListTile( | ||||||||||||||||||||||||||||||||||||||
| title: Text('Developer'), | ||||||||||||||||||||||||||||||||||||||
| subtitle: Text(AppConstants.authorName), | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -347,6 +361,10 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||||||||||||||||||||||||||||||||
| 'dashboardUrl': _prefs.dashboardUrl, | ||||||||||||||||||||||||||||||||||||||
| 'autoStart': _prefs.autoStartGateway, | ||||||||||||||||||||||||||||||||||||||
| 'nodeEnabled': _prefs.nodeEnabled, | ||||||||||||||||||||||||||||||||||||||
| 'nodeDeviceToken': _prefs.nodeDeviceToken, | ||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayHost': _prefs.nodeGatewayHost, | ||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayPort': _prefs.nodeGatewayPort, | ||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayToken': _prefs.nodeGatewayToken, | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Authentication tokens ( Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| final path = await _getSnapshotPath(); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -397,6 +415,18 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||||||||||||||||||||||||||||||||
| if (snapshot['nodeEnabled'] != null) { | ||||||||||||||||||||||||||||||||||||||
| _prefs.nodeEnabled = snapshot['nodeEnabled'] as bool; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (snapshot['nodeDeviceToken'] != null) { | ||||||||||||||||||||||||||||||||||||||
| _prefs.nodeDeviceToken = snapshot['nodeDeviceToken'] as String; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (snapshot['nodeGatewayHost'] != null) { | ||||||||||||||||||||||||||||||||||||||
| _prefs.nodeGatewayHost = snapshot['nodeGatewayHost'] as String; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (snapshot['nodeGatewayPort'] != null) { | ||||||||||||||||||||||||||||||||||||||
| _prefs.nodeGatewayPort = snapshot['nodeGatewayPort'] as int; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if (snapshot['nodeGatewayToken'] != null) { | ||||||||||||||||||||||||||||||||||||||
| _prefs.nodeGatewayToken = snapshot['nodeGatewayToken'] as String; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Refresh UI | ||||||||||||||||||||||||||||||||||||||
| await _loadSettings(); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -413,6 +443,54 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Future<void> _checkForUpdates() async { | ||||||||||||||||||||||||||||||||||||||
| setState(() => _checkingUpdate = true); | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| final result = await UpdateService.check(); | ||||||||||||||||||||||||||||||||||||||
| if (!mounted) return; | ||||||||||||||||||||||||||||||||||||||
| if (result.available) { | ||||||||||||||||||||||||||||||||||||||
| showDialog( | ||||||||||||||||||||||||||||||||||||||
| context: context, | ||||||||||||||||||||||||||||||||||||||
| builder: (ctx) => AlertDialog( | ||||||||||||||||||||||||||||||||||||||
| title: const Text('Update Available'), | ||||||||||||||||||||||||||||||||||||||
| content: Text( | ||||||||||||||||||||||||||||||||||||||
| 'A new version is available.\n\n' | ||||||||||||||||||||||||||||||||||||||
| 'Current: ${AppConstants.version}\n' | ||||||||||||||||||||||||||||||||||||||
| 'Latest: ${result.latest}', | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| actions: [ | ||||||||||||||||||||||||||||||||||||||
| TextButton( | ||||||||||||||||||||||||||||||||||||||
| onPressed: () => Navigator.pop(ctx), | ||||||||||||||||||||||||||||||||||||||
| child: const Text('Later'), | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| FilledButton( | ||||||||||||||||||||||||||||||||||||||
| onPressed: () { | ||||||||||||||||||||||||||||||||||||||
| Navigator.pop(ctx); | ||||||||||||||||||||||||||||||||||||||
| launchUrl( | ||||||||||||||||||||||||||||||||||||||
| Uri.parse(result.url), | ||||||||||||||||||||||||||||||||||||||
| mode: LaunchMode.externalApplication, | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+467
to
+472
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In
Platform note (relevant to interpreting the boolean):
For 6.2.0 specifically, the package changelog also notes that the Sources: [1] [2] [3] 🏁 Script executed: # First, verify the file and code exist at the specified location
if [ -f "flutter_app/lib/screens/settings_screen.dart" ]; then
echo "File found. Reading lines 451-456:"
sed -n '451,456p' flutter_app/lib/screens/settings_screen.dart
echo ""
echo "Context around lines 451-456 (440-465):"
sed -n '440,465p' flutter_app/lib/screens/settings_screen.dart
else
echo "File not found at flutter_app/lib/screens/settings_screen.dart"
fiRepository: mithun50/openclaw-termux Length of output: 1221 Handle The download action currently ignores whether URL launch succeeds, so failures are silent. The The proposed fix is correct: make the callback async, await the result, and show a SnackBar if the launch fails. The use of the Proposed fix FilledButton(
- onPressed: () {
+ onPressed: () async {
Navigator.pop(ctx);
- launchUrl(
+ final opened = await launchUrl(
Uri.parse(result.url),
mode: LaunchMode.externalApplication,
);
+ if (!opened && mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Could not open release page')),
+ );
+ }
},
child: const Text('Download'),
),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| child: const Text('Download'), | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| ScaffoldMessenger.of(context).showSnackBar( | ||||||||||||||||||||||||||||||||||||||
| const SnackBar(content: Text("You're on the latest version")), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } catch (_) { | ||||||||||||||||||||||||||||||||||||||
| if (!mounted) return; | ||||||||||||||||||||||||||||||||||||||
| ScaffoldMessenger.of(context).showSnackBar( | ||||||||||||||||||||||||||||||||||||||
| const SnackBar(content: Text('Could not check for updates')), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||
| if (mounted) setState(() => _checkingUpdate = false); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Widget _sectionHeader(ThemeData theme, String title) { | ||||||||||||||||||||||||||||||||||||||
| return Padding( | ||||||||||||||||||||||||||||||||||||||
| padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'dart:convert'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'dart:io'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:flutter/material.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:google_fonts/google_fonts.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -73,6 +74,39 @@ class _SplashScreenState extends State<SplashScreen> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final prefs = PreferencesService(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await prefs.init(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Auto-export snapshot when app version changes (#55) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final oldVersion = prefs.lastAppVersion; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (oldVersion != null && oldVersion != AppConstants.version) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final hasPermission = await NativeBridge.hasStoragePermission(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasPermission) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final sdcard = await NativeBridge.getExternalStoragePath(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final downloadDir = Directory('$sdcard/Download'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!await downloadDir.exists()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await downloadDir.create(recursive: true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final snapshotPath = '$sdcard/Download/openclaw-snapshot-$oldVersion.json'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final openclawJson = await NativeBridge.readRootfsFile('root/.openclaw/openclaw.json'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final snapshot = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'version': oldVersion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'timestamp': DateTime.now().toIso8601String(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'openclawConfig': openclawJson, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'dashboardUrl': prefs.dashboardUrl, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'autoStart': prefs.autoStartGateway, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'nodeEnabled': prefs.nodeEnabled, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'nodeDeviceToken': prefs.nodeDeviceToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayHost': prefs.nodeGatewayHost, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayPort': prefs.nodeGatewayPort, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'nodeGatewayToken': prefs.nodeGatewayToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Security: Authentication tokens ( Prompt for AI agents
Suggested change
Comment on lines
+90
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not export sensitive tokens to public Downloads.
🔐 Proposed fix (exclude secrets from snapshot) final snapshot = {
'version': oldVersion,
'timestamp': DateTime.now().toIso8601String(),
'openclawConfig': openclawJson,
'dashboardUrl': prefs.dashboardUrl,
'autoStart': prefs.autoStartGateway,
'nodeEnabled': prefs.nodeEnabled,
- 'nodeDeviceToken': prefs.nodeDeviceToken,
'nodeGatewayHost': prefs.nodeGatewayHost,
'nodeGatewayPort': prefs.nodeGatewayPort,
- 'nodeGatewayToken': prefs.nodeGatewayToken,
+ // Intentionally omit credentials/tokens from public snapshot exports.
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await File(snapshotPath).writeAsString( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const JsonEncoder.withIndent(' ').convert(snapshot), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prefs.lastAppVersion = AppConstants.version; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (_) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+78
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure Right now, an exception before Line 107 skips the version write, so snapshot export may retry forever across launches. 🛠️ Proposed fix (move version write to
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | |
| final oldVersion = prefs.lastAppVersion; | |
| if (oldVersion != null && oldVersion != AppConstants.version) { | |
| final hasPermission = await NativeBridge.hasStoragePermission(); | |
| if (hasPermission) { | |
| final sdcard = await NativeBridge.getExternalStoragePath(); | |
| final downloadDir = Directory('$sdcard/Download'); | |
| if (!await downloadDir.exists()) { | |
| await downloadDir.create(recursive: true); | |
| } | |
| final snapshotPath = '$sdcard/Download/openclaw-snapshot-$oldVersion.json'; | |
| final openclawJson = await NativeBridge.readRootfsFile('root/.openclaw/openclaw.json'); | |
| final snapshot = { | |
| 'version': oldVersion, | |
| 'timestamp': DateTime.now().toIso8601String(), | |
| 'openclawConfig': openclawJson, | |
| 'dashboardUrl': prefs.dashboardUrl, | |
| 'autoStart': prefs.autoStartGateway, | |
| 'nodeEnabled': prefs.nodeEnabled, | |
| 'nodeDeviceToken': prefs.nodeDeviceToken, | |
| 'nodeGatewayHost': prefs.nodeGatewayHost, | |
| 'nodeGatewayPort': prefs.nodeGatewayPort, | |
| 'nodeGatewayToken': prefs.nodeGatewayToken, | |
| }; | |
| await File(snapshotPath).writeAsString( | |
| const JsonEncoder.withIndent(' ').convert(snapshot), | |
| ); | |
| } | |
| } | |
| prefs.lastAppVersion = AppConstants.version; | |
| } catch (_) {} | |
| try { | |
| final oldVersion = prefs.lastAppVersion; | |
| if (oldVersion != null && oldVersion != AppConstants.version) { | |
| final hasPermission = await NativeBridge.hasStoragePermission(); | |
| if (hasPermission) { | |
| final sdcard = await NativeBridge.getExternalStoragePath(); | |
| final downloadDir = Directory('$sdcard/Download'); | |
| if (!await downloadDir.exists()) { | |
| await downloadDir.create(recursive: true); | |
| } | |
| final snapshotPath = '$sdcard/Download/openclaw-snapshot-$oldVersion.json'; | |
| final openclawJson = await NativeBridge.readRootfsFile('root/.openclaw/openclaw.json'); | |
| final snapshot = { | |
| 'version': oldVersion, | |
| 'timestamp': DateTime.now().toIso8601String(), | |
| 'openclawConfig': openclawJson, | |
| 'dashboardUrl': prefs.dashboardUrl, | |
| 'autoStart': prefs.autoStartGateway, | |
| 'nodeEnabled': prefs.nodeEnabled, | |
| 'nodeDeviceToken': prefs.nodeDeviceToken, | |
| 'nodeGatewayHost': prefs.nodeGatewayHost, | |
| 'nodeGatewayPort': prefs.nodeGatewayPort, | |
| 'nodeGatewayToken': prefs.nodeGatewayToken, | |
| }; | |
| await File(snapshotPath).writeAsString( | |
| const JsonEncoder.withIndent(' ').convert(snapshot), | |
| ); | |
| } | |
| } | |
| } catch (_) { | |
| } finally { | |
| prefs.lastAppVersion = AppConstants.version; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@flutter_app/lib/screens/splash_screen.dart` around lines 78 - 108, The
snapshot export code can throw and currently swallows exceptions so
prefs.lastAppVersion is not updated; move the assignment prefs.lastAppVersion =
AppConstants.version out of the try body into a finally block (after the
try/catch) so it always runs regardless of errors during NativeBridge calls,
Directory/File operations, or JSON encoding; keep the existing catch (_) {}
behavior or replace it with more specific error handling if desired, but ensure
the finally always sets prefs.lastAppVersion to AppConstants.version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: mithun50/openclaw-termux
Length of output: 28711
Exclude private keys and auth tokens from backup rules — they are currently exposed.
Verification confirms this is a critical security issue. The app stores sensitive data in SharedPreferences that will be backed up:
_keyPrivate,_keyPublic) innode_identity_service.dartnode_device_token,node_gateway_token) inpreferences_service.dartThe current rules (
path=".") include all SharedPreferences files without exclusion. Add<exclude>entries to bothbackup_rules.xmlanddata_extraction_rules.xmlfor files containing these keys, or migrate private keys and tokens to Android's encryptedEncryptedSharedPreferencesorKeystore.🤖 Prompt for AI Agents