Conversation
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: qodo-free-for-open-source-projects[bot] <189517486+qodo-free-for-open-source-projects[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* Fix icon caching * Fix * style: auto-fix linting and formatting issues * Quality * style: auto-fix linting and formatting issues * Optimize speed * style: auto-fix linting and formatting issues * Revert "style: auto-fix linting and formatting issues" This reverts commit ab9727a. * Revert "Optimize speed" This reverts commit ea53327. * Update nightly.yml * Update README.md * style: auto-fix linting and formatting issues * Unify Flutter icon cache * style: auto-fix linting and formatting issues * Fix icon cache * Migrate to built-in Flutter's design components * Update Flutter * style: auto-fix linting and formatting issues * Fix builds * Fixed icons * style: auto-fix linting and formatting issues * Fix build? * Change app ID to io.github.omeritzics.updatium * style: auto-fix linting and formatting issues * Update README.md * Update README.md * Update README.md * Attempt to fix Material You colors breaking design * Move filter icon to the top * style: auto-fix linting and formatting issues * Move the grid view button to the top * UI improvments * style: auto-fix linting and formatting issues * Update readme * Update readme * Update readme * Fix build * style: auto-fix linting and formatting issues * Fix overflow * style: auto-fix linting and formatting issues * Fix signing problem(?) * Fix build? * Fix * Release build fix * Update Nightly builds * Update Nightly builds * Revert "Release build fix" This reverts commit 257307d. * Revert "Update Nightly builds" This reverts commit 91b8f47. * Fix release build? * Update release.yml * Update release.yml * Update README.md * Update release.yml * UI fix * Update Hebrew * Bump SDK version * Update Hebrew * Fix Waydroid * style: auto-fix linting and formatting issues * style: auto-fix linting and formatting issues * Revert "Fix Waydroid" This reverts commit d879ba1. * Add nightly-signed.yml * Update nightly-signed.yml * Update workflows * Fix * Should fix signed builds * style: auto-fix linting and formatting issues * Update pubspec.lock * Replace flutter_keyboard_visibility * style: auto-fix linting and formatting issues * Replace shared_storage with docman * style: auto-fix linting and formatting issues * Fix typos * style: auto-fix linting and formatting issues * Another fix * style: auto-fix linting and formatting issues * Update dependencies * Fix * Delete .github/workflows/qama-unsigned.yml * Fix? * style: auto-fix linting and formatting issues * Fix errors? * Now it should fix the signing * Another fix * Another fix attempt * Fix nightly.yml * Try to add a different icon for the Nighly builds. #165 * Nightly new branding * Sign Nightly builds by default * Prepare for the new release * A message about unofficial sources * Fix the nightly build * Some bug fixes * style: auto-fix linting and formatting issues * Revert "Fix the nightly build" This reverts commit 9a1d9cd. * Revert "Fix nightly.yml" This reverts commit 0a68335. * Revert "Nightly new branding" This reverts commit 3e141c6. * Revert "Try to add a different icon for the Nighly builds. #165" This reverts commit 02dd713. * Some more fixes * Improve pure black theme * Attempt to fix grid view bug * Material You bug fix * style: auto-fix linting and formatting issues * Update badge style for GitHub release link * Bug fixes * style: auto-fix linting and formatting issues * Selection fix * UX/UI fixes * style: auto-fix linting and formatting issues * Update supported app sources in README * Fix build * style: auto-fix linting and formatting issues * Selection fix #2 * style: auto-fix linting and formatting issues * Fix spacing * Revert "Fix spacing" This reverts commit 1289296. * Add consistent spacing constants to app pages * style: auto-fix linting and formatting issues * Fix duplicate spacing constants compilation errors * Fix height16 scope issue in showChangeLogDialog * style: auto-fix linting and formatting issues * Add Fastlane supply metadata validation step Added a step to validate Fastlane supply metadata in the lint workflow. * Add Fastlane Supply Metadata validation job Added a new job to validate Fastlane Supply Metadata in the lint workflow. * Remove 'go' job from lint workflow Removed the 'go' job and its associated steps from the lint workflow. * Enhance nightly workflow with linting and formatting Add steps to auto-fix linting issues and format code in nightly workflow. * Enhance CI workflow with linting and formatting steps Added steps to auto-fix linting issues and format code in CI workflow. * Add auto-fix linting and formatting steps to workflow Added steps to auto-fix linting issues and format code before committing changes. * Delete .github/workflows/lint.yml * Delete .github/workflows/dependency-review.yml * Fix updateAppIcon method parameter reference error * Fix #168 * Fix icons in the app view page * style: auto-fix linting and formatting issues * style: auto-fix linting and formatting issues * Remove grid view from the code * Re-add grid view from Qama v26.1 * style: auto-fix linting and formatting issues * Fix typo * Fix formatting in localization section of README * Fix grid * Fix design inconsistency * style: auto-fix linting and formatting issues * Fix app.dart * Delete renovate.json * style: auto-fix linting and formatting issues * fix export error * style: auto-fix linting and formatting issues * Quick bug fix * style: auto-fix linting and formatting issues * Add renovate.json * chore(deps): update google/osv-scanner-action action to v2.3.3 * chore(deps): update gradle to v9.3.1 * chore(deps): update plugin org.jetbrains.kotlin.android to v2.3.10 * chore(deps): update actions/github-script action to v8 * chore(deps): update actions/upload-artifact action to v7 * chore(deps): update stefanzweifel/git-auto-commit-action action to v7 * Another bugfix * Update lib/providers/apps_provider.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update BUTTON_MIGRATION_GUIDE.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update lib/examples/refactored_icon_pipeline_example.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update lib/examples/refactored_icon_pipeline_example.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update assets/translations/README.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore(deps): update actions/checkout action to v6 * chore(deps): update actions/github-script action to v8 * style: auto-fix linting and formatting issues * Fix workflows * chore(deps): update dependency node to v24 * chore(deps): update actions/setup-node action to v6 * Update translations.yml * chore(deps): update dependency node to v24 * Migrate some design components to the official Flutter ones * 🌐 Add missing translation keys for export functionality * Fix workflow * chore(deps): update actions/checkout action to v6 * style: auto-fix linting and formatting issues * add about (#196) * M3 Expressive update (#194) * M3 Expressive update * Update lib/main.dart Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * commit --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * chore(deps): update stefanzweifel/git-auto-commit-action action to v7 (#198) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps): update peter-evans/create-pull-request action to v8 (#197) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis pull request introduces a YARA-based malware scanning system with security settings management, integrating APK scanning before installation, adding multi-language security UI labels across ten locales, updating dropdown theme styling, removing deep link handling, eliminating legacy dependencies, and restructuring project version requirements. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant App as Flutter App
participant AppsProvider as AppsProvider
participant SecurityProvider as SecuritySettingsProvider
participant YARAScanner as YARAScanner
participant FileSystem as File System
participant Installer as Package Installer
User->>App: Initiate APK Installation
App->>AppsProvider: installApk(apkPath)
AppsProvider->>SecurityProvider: scanAPK(apkPath)
SecurityProvider->>YARAScanner: scanFile(apkPath)
YARAScanner->>FileSystem: Read APK bytes
FileSystem-->>YARAScanner: APK data
YARAScanner->>YARAScanner: Evaluate rules & matches
YARAScanner-->>SecurityProvider: YARAScanResult
alt Malware Detected & Quarantine Enabled
SecurityProvider->>FileSystem: Create quarantine dir
SecurityProvider->>FileSystem: Move infected file
SecurityProvider->>FileSystem: Write quarantine report
SecurityProvider-->>AppsProvider: Exception (infected)
else No Threat Detected
SecurityProvider-->>AppsProvider: Clean result
AppsProvider->>Installer: Proceed with installation
Installer-->>User: Installation success
else Auto-Scan Disabled
SecurityProvider-->>AppsProvider: Non-infected result
AppsProvider->>Installer: Proceed with installation
Installer-->>User: Installation success
end
alt Scan Failed
AppsProvider->>FileSystem: Cleanup downloaded APK
AppsProvider-->>App: securityScanBlocked error
App-->>User: Installation blocked
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the application's security posture by integrating a robust in-app malware scanning system using YARA rules. This new capability allows for the automatic detection and handling of malicious APKs, providing users with a safer experience. Alongside this major feature, the project's dependency landscape has been streamlined through the removal of several external packages and updates to existing ones, contributing to a more maintainable and efficient codebase. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a significant new feature: in-app malware scanning powered by YARA. It also includes a substantial refactoring that removes several dependencies and cleans up the codebase. While the new security feature is a great addition, the current implementation of the YARA scanner is fundamentally flawed and does not provide the expected level of security. Additionally, the refactoring appears to have broken core application functionality, such as installing APKs. There are also concerns about resource management and performance that should be addressed.
| Future<List<PackageInfo>> getAllInstalledInfo() async { | ||
| return await pm.getInstalledPackages(flags: packageInfoFlags) ?? []; | ||
| return []; | ||
| } |
There was a problem hiding this comment.
This function, along with getInstalledInfo and installApk, has been stubbed out and no longer interacts with the Android package manager. This effectively removes the application's core functionality of installing and managing apps. For example, installApk now hardcodes code = 'not_implemented', preventing any installations.
This is a critical regression. If this is an intentional part of a larger refactoring, it should be clearly documented. Otherwise, this needs to be fixed before merging.
| } | ||
|
|
||
| /// Main YARA Scanner Class | ||
| class YARAScanner { |
There was a problem hiding this comment.
The current implementation of YARAScanner is not a true YARA engine. It appears to be a custom, simplified string and hex pattern matcher that parses YARA rule files. This is fundamentally different from how the actual YARA engine works, which involves compiling rules into a more efficient format and using advanced pattern matching algorithms.
This implementation is misleading as the feature is advertised as being 'powered by YARA', and it is likely to be ineffective at detecting malware compared to a real YARA engine. This could create a false sense of security for users.
Recommendation: Instead of a custom implementation, please integrate the actual YARA library. This can be done using Flutter's FFI (Foreign Function Interface) to call the native YARA C library. There might also be existing packages on pub.dev that provide these bindings.
| void dispose() { | ||
| // Cancel auto-update timer to prevent memory leaks | ||
| // Note: We don't dispose the singleton here as it might be used by other instances | ||
| // Instead, callers should call YARAScanner.disposeInstance() when the app is shutting down | ||
| } |
There was a problem hiding this comment.
The dispose method does not clean up the Timer created in the YARAScanner singleton for auto-updates. The comment indicates this is intentional, but there is no corresponding call to YARAScanner.disposeInstance() when the application is terminated. This will result in a resource leak, with the timer continuing to run in the background even when it's no longer needed.
Recommendation: The lifecycle of the YARAScanner singleton should be managed properly. The dispose method here should call a method on the scanner to cancel the timer, or there should be a clear mechanism to dispose of the singleton when the app is destroyed.
| } | ||
|
|
||
| /// Scan APK for malware before installation | ||
| Future<bool> _scanAPKForMalware(String apkPath, {List<String>? additionalApkPaths}) async { |
There was a problem hiding this comment.
The _scanAPKForMalware method creates a new instance of SecuritySettingsProvider every time it's called. The provider's creation (SecuritySettingsProvider.create()) involves asynchronous operations and I/O (reading SharedPreferences and getting application directories), making this approach inefficient, especially if scans are frequent.
Recommendation: To improve performance, SecuritySettingsProvider should be managed as a longer-lived object. Consider providing it as a singleton or using a dependency injection framework like provider to manage its lifecycle, so it's created only once.
There was a problem hiding this comment.
🚫 CI Build Failed
The automated build process failed. Please review the build logs and fix the issues before requesting another review.
Next steps:
- Check the build logs for specific errors
- Fix the identified issues
- Push your fixes to this branch
- The CI will automatically re-run
Once the build passes, this review will be dismissed automatically.
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
lib/providers/settings_provider.dart (1)
421-439:⚠️ Potential issue | 🟠 MajorExport directory selection is effectively broken now.
pickExportDir()no longer persists a new directory, andgetExportDir()now returns whatever URI was last stored without checking that it is still usable.lib/providers/apps_provider.dartandlib/pages/import_export.dartstill depend on both behaviors, so the picker/export UI can appear to work while writes keep using a stale location or fail later.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/providers/settings_provider.dart` around lines 421 - 439, getExportDir currently just parses the stored 'exportDir' string without validating it, and pickExportDir no longer persists a newly picked directory (nor clears it on remove), causing stale or unusable export locations; fix by updating pickExportDir to (a) invoke the platform picker/code-path that obtains a new Uri, persist its string into prefs under 'exportDir' (and clear prefs when remove==true), and return/update any in-memory state, and (b) update getExportDir to validate the stored Uri (e.g., check the Uri parses, and verify existence/writeability or access via the same storage API used for exporting) and if validation fails return null (and clear the stale prefs entry), so callers in apps_provider.dart and import_export.dart always receive a current usable directory via getExportDir and pickExportDir.lib/pages/home.dart (1)
147-259:⚠️ Potential issue | 🟠 MajorThis removes all working
updatium://deep-link handling.
initDeepLinks()no longer reads the initial intent or subscribes to link events, whileandroid/app/src/main/AndroidManifest.xmlstill exposes theupdatiumscheme. Share/import links will now open the app and then be ignored.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/pages/home.dart` around lines 147 - 259, initDeepLinks currently defines interpretLink/goToAddApp/goToExistingApp but never hooks them up to platform intents, so updatium:// links are ignored; restore initial intent reading and a link-event subscription that invokes interpretLink(Uri) for incoming links, and ensure the subscription is cancelled on widget dispose. Specifically, inside initDeepLinks add code to (1) read the initial link/intent and call interpretLink(Uri.parse(...)) if present, (2) subscribe to the link stream (or AppLinks/uni_links equivalent) and call interpretLink for each event, and (3) save and cancel the StreamSubscription on dispose; keep the existing helper functions (goToAddApp, goToExistingApp, interpretLink) and reuse them when handling both initial and subsequent links.pubspec.yaml (1)
22-76:⚠️ Potential issue | 🔴 CriticalIncompatible API usage detected against declared minimums—build will fail.
The lowered minimums are incompatible with the codebase:
- Flutter 3.19.0: Does not include
Color.withValues(added 3.27.0),WidgetStateProperty(3.22.0),PredictiveBackPageTransitionsBuilder(3.22.0), oryear2023theme flags (3.29.0). The repo uses all of these acrosslib/main.dart,lib/pages/settings.dart,lib/pages/app.dart, and elsewhere.- share_plus 7.0.0: Does not support
fileNameOverridesparameter inShare.shareXFiles(...)(added in 10.0.0). Used inlib/pages/apps.dart:1364.- flutter_foreground_task 8.0.0: Does not include
TaskStartercallback orForegroundServiceTypes(both added in 8.9.0). Used inlib/main.dart:119andlib/main.dart:250.Require minimum versions: Flutter ≥3.29.0, share_plus ≥10.0.0, flutter_foreground_task ≥8.9.0.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pubspec.yaml` around lines 22 - 76, Update the package minimums in pubspec.yaml to match APIs used: set the Dart/Flutter SDK constraint to require Flutter ≥3.29.0 (replace the current sdk/flutter lines so Flutter is ">=3.29.0"), bump share_plus to at least ^10.0.0 to get the fileNameOverrides parameter for Share.shareXFiles, and bump flutter_foreground_task to at least ^8.9.0 to get TaskStarter and ForegroundServiceTypes; this aligns the declared minimums with usages of Color.withValues, WidgetStateProperty, PredictiveBackPageTransitionsBuilder, the year2023 theme flags, Share.shareXFiles(fileNameOverrides: ...), TaskStarter, and ForegroundServiceTypes.lib/pages/settings.dart (1)
828-838:⚠️ Potential issue | 🟠 MajorRemove or disable the dead Shizuku toggle.
Both branches force
settingsProvider.useShizuku = false, so this switch can never stay enabled.lib/providers/apps_provider.dartalso rejects the Shizuku path now, which leaves an interactive control with no working behavior behind it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/pages/settings.dart` around lines 828 - 838, The Switch for Shizuku is dead (both branches set settingsProvider.useShizuku = false) and should be removed or disabled: remove or replace the Switch widget (the Switch and its onChanged handler around Flexible/Text(tr('useShizuku')) and the references to settingsProvider.useShizuku) or make it clearly disabled by setting value: false and onChanged: null and optionally add a subtitle/tooltip explaining it’s unsupported; also remove any dead branching logic that attempted to toggle Shizuku so no unused code referencing settingsProvider.useShizuku remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@android/AndroidManifest.xml`:
- Around line 1-4: The top-level android/AndroidManifest.xml is dead config and
duplicates the flutterEmbedding meta-data; remove this duplicate file or move
the <meta-data android:name="flutterEmbedding" android:value="2" /> entry into
the real app manifest used by Gradle (the manifest merged from
android/app/src/main/AndroidManifest.xml under the <application> element) so
only the canonical manifest contains the flutterEmbedding meta-data.
In `@assets/translations/de.json`:
- Around line 31-64: New YARA/security translation keys (e.g.,
"yaraMalwareScanner", "yaraScannerDescription", "enableAutoScan",
"enableAutoScanDescription", "enableAutoUpdate", "enableAutoUpdateDescription",
"updateInterval", "updateIntervalDescription", "threatLevelFilter",
"quarantineInfected", "quarantineInfectedDescription", etc.) were only added to
de.json causing the Translation Management CI to fail; add the same keys to
every locale file under assets/translations that lib/main.dart declares as
supported (not just en/de/fr) — either with proper localized strings or safe
placeholders (English or the de values) so no locale is left missing these keys
— and ensure the JSON structure and commas are valid in each file.
In `@assets/translations/en.json`:
- Around line 31-61: Add the new translation keys introduced in this diff (for
example: "yaraMalwareScanner", "yaraScannerDescription", "securitySettings",
"enableAutoScan", "enableAutoScanDescription", "enableAutoUpdate",
"enableAutoUpdateDescription", "updateInterval", "updateIntervalDescription",
"hours", "threatLevelFilter", "threatLevelFilterDescription", "level1",
"lowThreat", "level2", "mediumThreat", "level3", "highThreat",
"quarantineSettings", "quarantineInfected", "quarantineInfectedDescription",
"viewQuarantine", "viewQuarantineDescription", "databaseInformation",
"lastUpdate", "rulesVersion", "updateNow", "updating",
"rulesUpdatedSuccessfully", "rulesUpdateFailed", "quarantineViewComingSoon")
into every other locale file so the translation-management job no longer reports
missing keys; for each locale add the exact key names with appropriate
translated strings (or sensible placeholder values such as the English text) so
all locales mirror the en.json keys before merging.
- Around line 31-32: The translation copy overstates the
implementation—lib/security/yara_scanner.dart implements a basic custom matcher,
not a full YARA engine—so update the strings for "yaraMalwareScanner" and
"yaraScannerDescription" to remove "Powered by YARA" and "industry-standard"
phrasing and instead describe it as a basic YARA-inspired/custom rule matcher
that provides additional scanning but does not guarantee full YARA compatibility
or 100% detection; make the language accurate and include a short user-facing
disclaimer about limitations.
In `@lib/pages/settings.dart`:
- Around line 916-923: The DropdownMenuItem labels hardcode "hours" causing "1
hours" — update the label generation in the DropdownButton<int> /
DropdownMenuItem<int> map so it uses the correct singular/plural translation
(e.g., call the app's pluralization API or choose tr('hour') when hours == 1 and
tr('hours') otherwise) when building the child Text for each hours value; keep
the existing use of _securityProviderInitialized and
_securityProvider.getUpdateInterval() unchanged while only changing the label
expression.
In `@lib/providers/apps_provider.dart`:
- Around line 500-516: The placeholder implementations of getAllInstalledInfo
and getInstalledInfo break app-detection; replace them with real package-manager
queries: implement getAllInstalledInfo to return the list of installed
PackageInfo objects from the platform package manager and implement
getInstalledInfo(String? packageName, {bool printErr = true}) to look up and
return the PackageInfo for the given packageName (or null if not found),
preserving the existing try/catch and honoring printErr when logging errors;
ensure you use the same PackageInfo type expected downstream and do not return
[]/null unconditionally so loadApps(), version reconciliation, downgrade checks,
and silent-install logic receive real data.
- Around line 2353-2355: The export feature has been short-circuited:
DocMan-related export paths return null (silently no-op) so neither auto-export
nor manual export persist data; restore the original persistence instead of
returning null by removing the early returns in the DocMan export handlers and
invoking the existing persistence/save routine used by the export flow (the same
code path used by import/export persistence), ensuring both the auto-export and
manual export functions call that persistence method and return its result
rather than null.
- Around line 1058-1065: The variable code is declared as int? but is being
assigned the string 'not_implemented'; replace those string assignments by
throwing immediately when installation is unsupported: inside the branches that
reference AndroidPackageInstaller and ShizukuApkInstaller (the blocks guarded by
settingsProvider.useShizuku), throw a descriptive exception (e.g.,
UnsupportedError or InstallError) instead of assigning a sentinel string so
callers never receive a non-int; leave any remaining integer-returning paths and
the subsequent comparisons (code != 0, code != 3) and InstallError(code)
expecting an int intact.
In `@lib/providers/native_provider.dart`:
- Around line 13-17: The loadSystemFont() function currently sets
_systemFontLoaded = true without actually adding or loading any font, causing
'SystemFont' to be undefined and preventing retries; update loadSystemFont()
(and related FontLoader usage) to either 1) call fontLoader.addFont(...) with
the correct asset/ByteData for the system font and then await fontLoader.load(),
or 2) if the system font is no longer supported, remove the fake success path
and clear the useSystemFont flow (avoid setting _systemFontLoaded = true) so
main.dart's theme fallback to 'SystemFont' doesn't silently fail; reference
loadSystemFont, _systemFontLoaded, and the FontLoader('SystemFont') instance
when making the change.
In `@lib/security/security_settings_provider.dart`:
- Around line 78-80: The scanAPK logic never applies the configured threshold
from getThreatLevelFilter(), so result.matches must be filtered by threat level
before deciding to block/quarantine; update the scanAPK function to read int
threshold = getThreatLevelFilter() and compute filteredMatches =
result.matches.where(match => match.severity >= threshold) (or use the correct
property name on the match object), then use filteredMatches for all downstream
decisions and reporting (e.g., blocking/quarantining, notifications, and
returned results) so the threshold setting actually controls which detections
trigger actions; apply the same filter logic to both places where scanAPK
evaluates result.matches.
- Around line 54-76: The setters setAutoUpdateEnabled and setUpdateInterval
currently only replace the config via YARAScanner.getInstance(_config) but do
not reconfigure the running scanner’s timers; after updating _config and
swapping the instance you should reinitialize the scanner so its auto-update
timer is started/stopped with the new settings—call the scanner reconfiguration
routine (e.g., await _scanner.initialize() or the scanner's restart/reconfigure
method) after assigning _scanner in both setAutoUpdateEnabled and
setUpdateInterval so the running schedule reflects the new config.
In `@lib/security/yara_scanner.dart`:
- Around line 116-144: YARARule.fromString currently collapses an entire .yar
file into one synthetic YARARule, merging metadata across multiple rule blocks;
change the API to parse and return all rules found: implement a parser in
YARARule.fromString (or add YARARules.fromString/parseMultiple) that iterates
the file, detects each "rule <name>" block boundary, collects metadata (name,
author, description, tags, content) per rule, and produces a List<YARARule> (or
an Iterable) rather than a single instance; update callers of
YARARule.fromString to handle multiple rules (or provide a convenience wrapper
for single-rule usage) and ensure the rule content for each YARARule contains
only that block's text so scanning/reporting yields one match object per rule.
- Around line 355-390: The scanner currently only extracts `{ ... }` definitions
and always utf8.encodes the extracted text so quoted strings are skipped and hex
falls back never triggers; update the parsing in the loop that builds strings
(referencing ruleLines and strings) and the later block that finds the match
(stringPattern, stringMatch, searchString, searchBytes, rule.content) to
distinguish three cases: 1) quoted strings (starts with " or ') — strip quotes
and use utf8.encode(searchString), 2) brace or token hex sequences (e.g. "{6A
40}" or "6A 40 68") — detect via a regex like r'^[0-9A-Fa-f\s]+' or presence of
braces and parse hex pairs into bytes, and 3) default text — utf8.encode; ensure
the initial extraction also captures quoted string definitions (e.g.
r'\$(\w+)\s*=\s*"([^"]+)"' and similar) so stringPattern/stringMatch will locate
the correct content in rule.content before converting to searchBytes.
- Around line 183-191: initialize() currently only loads rules from disk and
starts the timer which means fresh installs scan with zero rules; after await
_loadRules() check whether any rules were actually loaded (e.g., rules directory
or in-memory rule list is empty) and if so trigger the same immediate update
routine that _startAutoUpdate schedules (invoke the function the timer would
call — the existing rule-fetch/update method used inside _startAutoUpdate)
before returning; keep cancelling _updateTimer and still start the periodic
timer when config.enableAutoUpdate is true so bootstrapped rules are available
for the first scan.
---
Outside diff comments:
In `@lib/pages/home.dart`:
- Around line 147-259: initDeepLinks currently defines
interpretLink/goToAddApp/goToExistingApp but never hooks them up to platform
intents, so updatium:// links are ignored; restore initial intent reading and a
link-event subscription that invokes interpretLink(Uri) for incoming links, and
ensure the subscription is cancelled on widget dispose. Specifically, inside
initDeepLinks add code to (1) read the initial link/intent and call
interpretLink(Uri.parse(...)) if present, (2) subscribe to the link stream (or
AppLinks/uni_links equivalent) and call interpretLink for each event, and (3)
save and cancel the StreamSubscription on dispose; keep the existing helper
functions (goToAddApp, goToExistingApp, interpretLink) and reuse them when
handling both initial and subsequent links.
In `@lib/pages/settings.dart`:
- Around line 828-838: The Switch for Shizuku is dead (both branches set
settingsProvider.useShizuku = false) and should be removed or disabled: remove
or replace the Switch widget (the Switch and its onChanged handler around
Flexible/Text(tr('useShizuku')) and the references to
settingsProvider.useShizuku) or make it clearly disabled by setting value: false
and onChanged: null and optionally add a subtitle/tooltip explaining it’s
unsupported; also remove any dead branching logic that attempted to toggle
Shizuku so no unused code referencing settingsProvider.useShizuku remains.
In `@lib/providers/settings_provider.dart`:
- Around line 421-439: getExportDir currently just parses the stored 'exportDir'
string without validating it, and pickExportDir no longer persists a newly
picked directory (nor clears it on remove), causing stale or unusable export
locations; fix by updating pickExportDir to (a) invoke the platform
picker/code-path that obtains a new Uri, persist its string into prefs under
'exportDir' (and clear prefs when remove==true), and return/update any in-memory
state, and (b) update getExportDir to validate the stored Uri (e.g., check the
Uri parses, and verify existence/writeability or access via the same storage API
used for exporting) and if validation fails return null (and clear the stale
prefs entry), so callers in apps_provider.dart and import_export.dart always
receive a current usable directory via getExportDir and pickExportDir.
In `@pubspec.yaml`:
- Around line 22-76: Update the package minimums in pubspec.yaml to match APIs
used: set the Dart/Flutter SDK constraint to require Flutter ≥3.29.0 (replace
the current sdk/flutter lines so Flutter is ">=3.29.0"), bump share_plus to at
least ^10.0.0 to get the fileNameOverrides parameter for Share.shareXFiles, and
bump flutter_foreground_task to at least ^8.9.0 to get TaskStarter and
ForegroundServiceTypes; this aligns the declared minimums with usages of
Color.withValues, WidgetStateProperty, PredictiveBackPageTransitionsBuilder, the
year2023 theme flags, Share.shareXFiles(fileNameOverrides: ...), TaskStarter,
and ForegroundServiceTypes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 29b0bea5-5302-4408-9eaf-d47daeb09029
⛔ Files ignored due to path filters (1)
pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (16)
android/AndroidManifest.xmlassets/translations/de.jsonassets/translations/en.jsonassets/translations/fr.jsonlib/custom_errors.dartlib/main.dartlib/pages/app.dartlib/pages/apps.dartlib/pages/home.dartlib/pages/settings.dartlib/providers/apps_provider.dartlib/providers/native_provider.dartlib/providers/settings_provider.dartlib/security/security_settings_provider.dartlib/security/yara_scanner.dartpubspec.yaml
💤 Files with no reviewable changes (3)
- lib/pages/apps.dart
- lib/pages/app.dart
- lib/custom_errors.dart
| "yaraMalwareScanner": "Automatische Malware-Erkennung (Powered by YARA)", | ||
| "yaraScannerDescription": "Branchenstandardmäßige Malware-Erkennung mit YARA-kompatiblen Regeln für umfassenden Schutz vor Viren, Trojanern und anderen Bedrohungen. Kein Scanner kann 100% Erkennung garantieren, bietet aber starken Schutz vor bekannter Malware. Die Verantwortung für die sichere App-Nutzung liegt letztlich beim Benutzer.", | ||
| "securitySettings": "Sicherheitseinstellungen", | ||
| "xIsTrackOnly": "{} ist nur zur Nachverfolgung", | ||
| "source": "Quelle", | ||
| "app": "App", | ||
| "enableAutoScan": "Automatische Scan aktivieren", | ||
| "enableAutoScanDescription": "Heruntergeladene APKs automatisch auf Malware scannen, vor der Installation", | ||
| "enableAutoUpdate": "Automatische Updates aktivieren", | ||
| "enableAutoUpdateDescription": "Malware-Definitions-Datenbank automatisch aktualisieren", | ||
| "updateInterval": "Update-Intervall", | ||
| "updateIntervalDescription": "Wie oft nach neuen Malware-Definitionen gesucht werden soll", | ||
| "hours": "Stunden", | ||
| "threatLevelFilter": "Bedrohungsstufen-Filter", | ||
| "threatLevelFilterDescription": "Mindestbedrohungsstufe für Warnungen", | ||
| "level1": "Stufe 1", | ||
| "lowThreat": "Niedrige Bedrohung", | ||
| "level2": "Stufe 2", | ||
| "mediumThreat": "Mittlere Bedrohung", | ||
| "level3": "Stufe 3", | ||
| "highThreat": "Hohe Bedrohung", | ||
| "quarantineSettings": "Quarantäne-Einstellungen", | ||
| "quarantineInfected": "Infizierte Dateien quarantänen", | ||
| "quarantineInfectedDescription": "Erkannte Malware zur Sicherheit in Quarantäne verschieben", | ||
| "viewQuarantine": "Quarantäne anzeigen", | ||
| "viewQuarantineDescription": "Quarantänierte Dateien verwalten", | ||
| "databaseInformation": "Datenbank-Informationen", | ||
| "lastUpdate": "Letzte Aktualisierung", | ||
| "rulesVersion": "Regeln-Version", | ||
| "updateNow": "Jetzt aktualisieren", | ||
| "updating": "Aktualisiere...", | ||
| "rulesUpdatedSuccessfully": "Malware-Definitionen erfolgreich aktualisiert", | ||
| "rulesUpdateFailed": "Fehler beim Aktualisieren der Malware-Definitionen", | ||
| "quarantineViewComingSoon": "Quarantäne-Ansicht kommt bald", |
There was a problem hiding this comment.
These new YARA keys still need to land in the rest of assets/translations/.
The Translation Management job is already failing, and lib/main.dart declares many supported locales beyond en/de/fr. Adding the new security keys in only a subset of locale files keeps CI red and leaves the remaining locales on fallback/missing strings.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@assets/translations/de.json` around lines 31 - 64, New YARA/security
translation keys (e.g., "yaraMalwareScanner", "yaraScannerDescription",
"enableAutoScan", "enableAutoScanDescription", "enableAutoUpdate",
"enableAutoUpdateDescription", "updateInterval", "updateIntervalDescription",
"threatLevelFilter", "quarantineInfected", "quarantineInfectedDescription",
etc.) were only added to de.json causing the Translation Management CI to fail;
add the same keys to every locale file under assets/translations that
lib/main.dart declares as supported (not just en/de/fr) — either with proper
localized strings or safe placeholders (English or the de values) so no locale
is left missing these keys — and ensure the JSON structure and commas are valid
in each file.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
lib/providers/apps_provider.dart (3)
500-516:⚠️ Potential issue | 🔴 CriticalRestore real installed-package queries (placeholders break app state).
getAllInstalledInfo()andgetInstalledInfo()now return[]/nullunconditionally, so installed apps are treated as uninstalled. That breaks install-state reconciliation, downgrade checks, and silent-install eligibility.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/providers/apps_provider.dart` around lines 500 - 516, The placeholders in getAllInstalledInfo() and getInstalledInfo() are breaking app state; restore their real implementations by replacing the empty returns with the actual platform queries: have getAllInstalledInfo() asynchronously fetch and map installed packages into List<PackageInfo> (e.g., using the platform package API originally used in this file) and have getInstalledInfo(String? packageName, {bool printErr = true}) query for a single package by name, returning a PackageInfo or null; keep the existing try/catch behavior, ensure errors are logged when printErr is true, and return the proper types instead of []/null so install-state reconciliation, downgrade checks, and silent-install eligibility work correctly.
1058-1074:⚠️ Potential issue | 🔴 CriticalInstallation path has a hard type error (
int?assignedString).
codeisint?, but both branches assign'not_implemented'(Line 1061, Line 1064). Subsequent integer comparisons andInstallError(code)are incompatible and break this path.#!/bin/bash set -euo pipefail # Verify the mismatched assignments in installApk() sed -n '1056,1076p' lib/providers/apps_provider.dart # Verify InstallError usage/signature context expects numeric code rg -n -C3 'class InstallError|InstallError\(' libSuggested fix
- int? code; - if (!settingsProvider.useShizuku) { - // AndroidPackageInstaller functionality removed - code = 'not_implemented'; - } else { - // ShizukuApkInstaller functionality removed - code = 'not_implemented'; - } + throw UpdatiumError(tr('functionNotImplemented'));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/providers/apps_provider.dart` around lines 1058 - 1074, The variable code is declared as int? but is being assigned the String 'not_implemented'; change those assignments to an integer sentinel (e.g., define a constant like kNotImplemented = -2 and assign that) so the comparisons (code != 0 && code != 3) and the InstallError(code) call remain type-correct; update any related conditional logic in the same block (the branches that previously set 'not_implemented') to use the integer constant and ensure InstallError is invoked with an int.
2353-2355:⚠️ Potential issue | 🟠 MajorExport is currently a silent no-op in both auto and manual paths.
Both branches return
nullbefore persistence, so export appears available but does nothing.Also applies to: 2373-2374
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/providers/apps_provider.dart`:
- Around line 939-982: The helper _scanAPKForMalware currently returns false for
both infections and scanner failures, losing distinction; change it to return a
structured result (e.g., an enum ScanOutcome { clean, infected, scannerError }
or a small class ScanResult { ScanOutcome outcome; String? message;
List<String>? matches; }) instead of Future<bool>, populate outcome and
diagnostic fields at each exit (primary scan error -> scannerError with error
message, primary scan infected -> infected with match names, additional APK
outcomes set similarly, final successful path -> clean), and update all callers
of _scanAPKForMalware to handle the new structured result so installation flow
and telemetry can differentiate infected vs scanner-unavailable cases.
- Around line 1361-1363: The code throws
UpdatiumError(tr('shizukuNotSupported')) after download/processing has started,
causing wasted network/disk work; move the Shizuku support check earlier into
the preflight path (e.g., add a check in preflightChecks or the function that
runs before downloads) or change the silent-install eligibility function
(isSilentInstallEligible / similar) to return false when Shizuku is unsupported
so the flow never begins the download/processing; update references to
ShizukuApkInstaller removal accordingly and ensure the early check throws or
prevents silent installs before any expensive work starts.
- Around line 1021-1036: Replace the hard-coded translation lookup tr('Security
scan detected malware. Installation blocked for safety.') with a stable i18n key
(for example tr('securityScanBlocked')) in the block that throws UpdatiumError
when _scanAPKForMalware returns false; update the locales/translation resource
files to include the new key mapping to the full sentence for all supported
locales and ensure any tests or callers expecting the old literal are updated to
use the new key.
---
Duplicate comments:
In `@lib/providers/apps_provider.dart`:
- Around line 500-516: The placeholders in getAllInstalledInfo() and
getInstalledInfo() are breaking app state; restore their real implementations by
replacing the empty returns with the actual platform queries: have
getAllInstalledInfo() asynchronously fetch and map installed packages into
List<PackageInfo> (e.g., using the platform package API originally used in this
file) and have getInstalledInfo(String? packageName, {bool printErr = true})
query for a single package by name, returning a PackageInfo or null; keep the
existing try/catch behavior, ensure errors are logged when printErr is true, and
return the proper types instead of []/null so install-state reconciliation,
downgrade checks, and silent-install eligibility work correctly.
- Around line 1058-1074: The variable code is declared as int? but is being
assigned the String 'not_implemented'; change those assignments to an integer
sentinel (e.g., define a constant like kNotImplemented = -2 and assign that) so
the comparisons (code != 0 && code != 3) and the InstallError(code) call remain
type-correct; update any related conditional logic in the same block (the
branches that previously set 'not_implemented') to use the integer constant and
ensure InstallError is invoked with an int.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0ea4faac-9e2a-4049-b368-28e46aaa3113
📒 Files selected for processing (3)
lib/main.dartlib/pages/apps.dartlib/providers/apps_provider.dart
💤 Files with no reviewable changes (1)
- lib/pages/apps.dart
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/main.dart
| /// Scan APK for malware before installation | ||
| Future<bool> _scanAPKForMalware(String apkPath, {List<String>? additionalApkPaths}) async { | ||
| SecuritySettingsProvider? securityProvider; | ||
| try { | ||
| securityProvider = await SecuritySettingsProvider.create(); | ||
| await securityProvider.initialize(); | ||
| final scanResult = await securityProvider.scanAPK(apkPath); | ||
|
|
||
| if (scanResult.error != null) { | ||
| logs.add('Security scan error for primary APK: ${scanResult.error}'); | ||
| return false; | ||
| } | ||
|
|
||
| if (scanResult.isInfected) { | ||
| logs.add( | ||
| 'Security scan detected malware in APK: ${scanResult.matches.map((m) => m.ruleName).join(', ')}', | ||
| ); | ||
| return false; | ||
| } | ||
|
|
||
| if (additionalApkPaths != null) { | ||
| for (final additionalApkPath in additionalApkPaths) { | ||
| final additionalScanResult = await securityProvider.scanAPK(additionalApkPath); | ||
|
|
||
| if (additionalScanResult.error != null) { | ||
| logs.add('Security scan error for additional APK: ${additionalScanResult.error}'); | ||
| return false; | ||
| } | ||
|
|
||
| if (additionalScanResult.isInfected) { | ||
| logs.add('Security scan detected malware in additional APK: ${additionalScanResult.matches.map((m) => m.ruleName).join(', ')}'); | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } catch (e) { | ||
| logs.add('Security scan failed: $e'); | ||
| return false; // Block installation on scan failure | ||
| } finally { | ||
| securityProvider?.dispose(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Don’t collapse malware and scan-failure into a single boolean.
This helper returns false for both infection and scanner errors (Line 947 and Line 976), so callers can’t distinguish “infected APK” from “scanner unavailable/error”. Return a structured status so install flow can show correct user messaging and telemetry.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/providers/apps_provider.dart` around lines 939 - 982, The helper
_scanAPKForMalware currently returns false for both infections and scanner
failures, losing distinction; change it to return a structured result (e.g., an
enum ScanOutcome { clean, infected, scannerError } or a small class ScanResult {
ScanOutcome outcome; String? message; List<String>? matches; }) instead of
Future<bool>, populate outcome and diagnostic fields at each exit (primary scan
error -> scannerError with error message, primary scan infected -> infected with
match names, additional APK outcomes set similarly, final successful path ->
clean), and update all callers of _scanAPKForMalware to handle the new
structured result so installation flow and telemetry can differentiate infected
vs scanner-unavailable cases.
lib/providers/apps_provider.dart
Outdated
| // ShizukuApkInstaller functionality removed | ||
| throw UpdatiumError(tr('shizukuNotSupported')); | ||
| } |
There was a problem hiding this comment.
Shizuku unsupported is detected too late (after expensive work).
Throwing shizukuNotSupported here happens after download/processing has already started, causing avoidable network/disk work and noisy failures. Gate this earlier in the preflight path (or make silent-install eligibility return false when Shizuku install is unsupported).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/providers/apps_provider.dart` around lines 1361 - 1363, The code throws
UpdatiumError(tr('shizukuNotSupported')) after download/processing has started,
causing wasted network/disk work; move the Shizuku support check earlier into
the preflight path (e.g., add a check in preflightChecks or the function that
runs before downloads) or change the silent-install eligibility function
(isSilentInstallEligible / similar) to return false when Shizuku is unsupported
so the flow never begins the download/processing; update references to
ShizukuApkInstaller removal accordingly and ensure the early check throws or
prevents silent installs before any expensive work starts.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/pages/home.dart (1)
163-283:⚠️ Potential issue | 🟠 MajorDeep-link processing is now fully disabled.
Line 164 and Lines 279/282 remove all AppLinks entry points, so
interpretLink(...)is never invoked and link-based navigation/import flows no longer execute. If this wasn’t intentional, this is a functional regression.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/pages/home.dart` around lines 163 - 283, initDeepLinks removed the AppLinks entry points so interpretLink (and its helpers goToAddApp/goToExistingApp) never run; restore the missing initial-link and on-link handlers inside initDeepLinks so interpretLink is invoked for both cold-start and warm-state links. Re-add the logic that checks the initial/deferred link and the stream subscription/handler for incoming links (or rewire whatever platform/link service was removed) to call interpretLink(Uri) when a link is received, ensuring the existing goToAddApp and goToExistingApp helpers are used for navigation/import flows.pubspec.yaml (1)
51-63:⚠️ Potential issue | 🔴 CriticalRemove duplicate
cryptodependency (lines 51 and 63).The
cryptopackage is declared twice with different versions (^3.0.3and^3.0.7), creating an invalid dependency map. Keep only the entry at line 63 with version^3.0.7.Fix
- crypto: ^3.0.3 device_info_plus: ^10.0.0Remove the duplicate at line 51; keep line 63 with
crypto: ^3.0.7.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pubspec.yaml` around lines 51 - 63, The pubspec lists the crypto dependency twice causing a conflicting dependency map; remove the earlier duplicate entry "crypto: ^3.0.3" and keep only the later "crypto: ^3.0.7" entry so the dependency appears once (retain the existing "crypto: ^3.0.7" line and delete the "crypto: ^3.0.3" line).
🧹 Nitpick comments (1)
lib/pages/settings.dart (1)
163-180: Consider logging initialization failures for debuggability.The catch block silently swallows all exceptions, leaving no trace of why security features are unavailable. This makes debugging difficult when users report that the YARA scanner section is disabled.
♻️ Suggested improvement
} catch (e) { // Handle initialization error + debugPrint('SecuritySettingsProvider initialization failed: $e'); if (mounted) { setState(() { _securityProviderInitialized = false; }); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/pages/settings.dart` around lines 163 - 180, The catch in _initializeSecurityProvider silently swallows errors which hides why SecuritySettingsProvider.create()/initialize() failed; modify the catch to capture the exception and log it (including stack trace) before updating _securityProviderInitialized and calling setState (use whatever app logger or debugPrint available), e.g., reference _initializeSecurityProvider, SecuritySettingsProvider.create, _securityProvider, _securityProviderInitialized and ensure the log provides clear context like "Failed to initialize security provider" plus the error and stack trace.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.kt`:
- Around line 95-98: The generic catch in getAppInfo is swallowing all
exceptions (catch (e: Exception) { null }), hiding real failures and preventing
the ERROR branch from running; change the error handling so only
PackageManager.NameNotFoundException returns null and any other Exception is
either logged and rethrown or allowed to propagate (i.e., remove or replace the
broad catch (e: Exception) in getAppInfo), referencing the existing catch for
PackageManager.NameNotFoundException and the getAppInfo function so the ERROR
branch can handle real failures.
- Around line 70-72: The catch block in MainActivity that currently swallows
exceptions (the "catch (e: Exception) { // Skip apps... }" block) must log
failures instead of being silent; update that catch to call Android logging
(e.g., Log.w or Log.e) including the package identifier (e.g., packageName or
the variable used when iterating packages) and the exception object so
stack/causes are recorded, or use your existing logger instance if present
(include MainActivity TAG or MainActivity::class.simpleName for context); ensure
the log message clearly states "Failed to access package <packageName>" and pass
the exception so diagnostics capture the root cause.
- Around line 15-23: The MethodChannel handler currently calls blocking
PackageManager APIs (getInstalledPackages, getPackageInfo,
getLaunchIntentForPackage) inside the UI thread via getInstalledAppsList; move
those package queries to a background thread (e.g., use
CoroutineScope(Dispatchers.IO) or an Executor) inside the MethodChannel
setMethodCallHandler, perform all PackageManager calls in getInstalledAppsList
on that IO thread, then post the result back to the main thread before invoking
result.success or result.error (e.g., with withContext(Dispatchers.Main) or
runOnUiThread) to avoid ANRs while keeping MethodChannel callbacks on the UI
thread.
In `@assets/translations/es.json`:
- Line 31: The Spanish translation for the key "yaraMalwareScanner" contains a
typo ("Reglos"); open assets/translations/es.json, locate the
"yaraMalwareScanner" entry and correct the value string by replacing "Reglos"
with "Reglas" so it reads "Escaneo Automático de Malware (Reglas Estilo YARA)".
In `@assets/translations/ru.json`:
- Line 42: The translation for the key threatLevelFilterDescription contains the
English word "triggering"; update the Russian string to remove the English term
(e.g., change "Минимальный уровень угрозы для triggering оповещений" to
"Минимальный уровень угрозы для срабатывания оповещений" or similar) so the UI
text is fully Russian and consistent.
In `@lib/pages/settings.dart`:
- Around line 980-986: The async onChanged handler for the dropdown (guarded by
_securityProviderInitialized) awaits _securityProvider.setUpdateInterval and
then calls setState without verifying the widget is still mounted; modify the
handler in the onChanged callback to check mounted before calling setState
(e.g., return early or wrap setState with if (!mounted) return; or if (mounted)
setState(...)) so you don't call setState on a disposed widget after awaiting
_securityProvider.setUpdateInterval.
- Around line 960-968: The onChanged callback for the SwitchListTile should
guard the setState call with an if (mounted) check to match the earlier pattern;
update the async callback that calls
_securityProvider.setAutoUpdateEnabled(value) so that after awaiting it you only
call setState() when mounted (i.e., wrap setState() inside if (mounted) {
setState(() {}); }); reference the SwitchListTile onChanged,
_securityProvider.setAutoUpdateEnabled, and setState to locate the change.
- Around line 157-161: The SecuritySettingsProvider instance created in
initState() (_securityProvider) is not being disposed; update the widget's
dispose() to call _securityProvider.dispose() before calling super.dispose() so
that SecuritySettingsProvider.dispose() runs and resources are cleaned up.
In `@lib/providers/apps_provider.dart`:
- Around line 501-558: getAllInstalledInfo and getInstalledInfo currently
construct and return package_info_plus.PackageInfo which lacks Android-specific
fields used elsewhere; change these methods to return (or convert to) the
Android-specific model used elsewhere (or create a new DTO e.g.,
AndroidPackageInfo/ExtendedPackageInfo) and populate all required fields
(applicationInfo with targetSdkVersion and helper methods
getAppIcon/getAppLabel, signingInfo fields like
hasMultipleSigners/apkContentSigners/signingCertificateHistory,
versionCode/versionName, etc.) instead of PackageInfo so downstream accesses
(e.g., applicationInfo?.targetSdkVersion, signingInfo?.hasMultipleSigners,
versionCode/versionName) are valid; update the return types and constructor
usage in getAllInstalledInfo and getInstalledInfo to build and return that
extended model from the platform channel data.
In `@lib/security/yara_scanner.dart`:
- Around line 274-284: _loadRules() currently clears and repopulates the shared
_rules list while scanFile() may iterate it, causing ConcurrentModificationError
or partial rule sets; fix by building a new local List<YARARule> (e.g.,
newRules) inside _loadRules(), add parsed rules to newRules, then atomically
replace the shared reference (_rules = newRules) once loading completes, and
likewise ensure any other updater code around lines referenced (399-404) uses
the same replace-not-mutate pattern; alternatively, have scanFile() take a
snapshot (final rules = List.from(_rules)) before iterating to avoid concurrent
mutations.
- Around line 22-26: The ruleSources array (this.ruleSources) currently points
at mutable branch URLs; change it to use raw URLs pinned to specific commit SHAs
or tags (replace each URL in this.ruleSources with its corresponding
raw.githubusercontent.com/.../COMMIT_HASH/path.yar) and add a verification step
before loading: fetch a signed manifest or accompanying checksum, validate the
manifest signature and compare file hashes, and refuse to load rules if
verification fails; implement the verification logic in the YaraScanner
initialization path (where this.ruleSources is consumed) so only pinned,
validated rule files are accepted.
- Around line 140-150: The parser currently only detects rules starting with
"rule " and reads tags from a nonstandard "tags =" meta, missing qualified
headers like "private/global rule" and standard tags declared after a colon;
update the rule-start detection (the block using currentRuleStart,
currentRuleName and trimmedLine) to match headers with an optional qualifier and
optional tag list (e.g. regex like
^\s*(?:private|global)?\s*rule\s+([^\s{:]+)(?:\s*:\s*([^{]+))?), extract
currentRuleName from group 1 and, if group 2 exists, split by whitespace to
populate tags (in addition to keeping the existing "tags =" meta parsing), and
apply the same change to the other occurrence around lines 182-193 so
_parseSingleRule receives the rule name and combined tags correctly.
- Around line 418-523: The matcher treats empty patterns as matches and ignores
rule condition logic; update _checkRule to (1) parse the rule's "condition:"
line to determine whether it requires "any of" or "all of" named strings (or
fall back to requiring at least one match if condition is missing), (2) collect
identifier names (the $vars) and evaluate the condition against which
identifiers were actually found instead of returning on the first found string,
and (3) ensure _containsBytes returns false for empty patterns (do not treat
empty List<int> as a match) so empty or stripped patterns cannot trigger false
positives; locate logic in _checkRule (string extraction,
quotedPattern/hexPattern/defaultPattern, YARAMatch return) and the helper
_containsBytes to implement these checks.
---
Outside diff comments:
In `@lib/pages/home.dart`:
- Around line 163-283: initDeepLinks removed the AppLinks entry points so
interpretLink (and its helpers goToAddApp/goToExistingApp) never run; restore
the missing initial-link and on-link handlers inside initDeepLinks so
interpretLink is invoked for both cold-start and warm-state links. Re-add the
logic that checks the initial/deferred link and the stream subscription/handler
for incoming links (or rewire whatever platform/link service was removed) to
call interpretLink(Uri) when a link is received, ensuring the existing
goToAddApp and goToExistingApp helpers are used for navigation/import flows.
In `@pubspec.yaml`:
- Around line 51-63: The pubspec lists the crypto dependency twice causing a
conflicting dependency map; remove the earlier duplicate entry "crypto: ^3.0.3"
and keep only the later "crypto: ^3.0.7" entry so the dependency appears once
(retain the existing "crypto: ^3.0.7" line and delete the "crypto: ^3.0.3"
line).
---
Nitpick comments:
In `@lib/pages/settings.dart`:
- Around line 163-180: The catch in _initializeSecurityProvider silently
swallows errors which hides why SecuritySettingsProvider.create()/initialize()
failed; modify the catch to capture the exception and log it (including stack
trace) before updating _securityProviderInitialized and calling setState (use
whatever app logger or debugPrint available), e.g., reference
_initializeSecurityProvider, SecuritySettingsProvider.create, _securityProvider,
_securityProviderInitialized and ensure the log provides clear context like
"Failed to initialize security provider" plus the error and stack trace.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a123772c-2964-4f79-afef-50719e58cd7d
⛔ Files ignored due to path filters (1)
pubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
.github/workflows/translations.ymlandroid/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.ktassets/translations/ar.jsonassets/translations/de.jsonassets/translations/en.jsonassets/translations/es.jsonassets/translations/fr.jsonassets/translations/it.jsonassets/translations/ja.jsonassets/translations/pt.jsonassets/translations/ru.jsonassets/translations/zh.jsonlib/main.dartlib/pages/home.dartlib/pages/settings.dartlib/providers/apps_provider.dartlib/security/security_settings_provider.dartlib/security/yara_scanner.dartpubspec.yaml
💤 Files with no reviewable changes (1)
- .github/workflows/translations.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/main.dart
| MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> | ||
| when (call.method) { | ||
| "getInstalledApps" -> { | ||
| try { | ||
| val installedApps = getInstalledAppsList() | ||
| result.success(installedApps) | ||
| } catch (e: Exception) { | ||
| result.error("ERROR", "Failed to get installed apps", e.message) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify that heavy package-manager calls are currently executed directly in the handler.
rg -n -C3 'setMethodCallHandler|getInstalledAppsList\(|getAppInfo\(' android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.ktRepository: omeritzics/Updatium
Length of output: 1724
🏁 Script executed:
# Check for other threading patterns and executor usage in the project
rg -i 'executor|coroutine|launch|async|thread|runOnUiThread' android/app/src/main/kotlin --type kt | head -30Repository: omeritzics/Updatium
Length of output: 89
🏁 Script executed:
# Check build.gradle for Kotlin version and dependencies
cat android/app/build.gradle 2>/dev/null || cat android/app/build.gradle.kts 2>/dev/null | grep -A 5 -B 5 'kotlin\|coroutine' | head -40Repository: omeritzics/Updatium
Length of output: 685
🏁 Script executed:
# Search for other MethodChannel handlers to see if there's a pattern for threading
rg -n 'setMethodCallHandler|ioScope\|Dispatchers' android/app/src/main/kotlin --type ktRepository: omeritzics/Updatium
Length of output: 89
🏁 Script executed:
# Search for threading patterns without -type flag
rg -i 'executor|coroutine|launch|async|thread|runOnUiThread' android/app/src/main/kotlin | head -30Repository: omeritzics/Updatium
Length of output: 590
🏁 Script executed:
# Look for other MethodChannel handlers and any existing async patterns
rg -n 'setMethodCallHandler|launch\(|async\(' android/app/src/main/kotlinRepository: omeritzics/Updatium
Length of output: 253
🏁 Script executed:
# Check full MainActivity.kt file length to see if suggested fix scope is accurate
wc -l android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.ktRepository: omeritzics/Updatium
Length of output: 140
🏁 Script executed:
# Read the complete MainActivity.kt file to verify all context
cat -n android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.ktRepository: omeritzics/Updatium
Length of output: 5061
Move package queries off the main thread.
Lines 19 and 29 execute blocking PackageManager operations (getInstalledPackages, getPackageInfo, getLaunchIntentForPackage) directly inside the MethodChannel handler, which runs on the main thread. This can cause Application Not Responding (ANR) errors, especially on devices with many installed packages.
Suggested fix
+import java.util.concurrent.Executors
+
class MainActivity : FlutterActivity() {
private val CHANNEL = "updatium/package_manager"
+ private val ioExecutor = Executors.newSingleThreadExecutor()
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"getInstalledApps" -> {
- try {
- val installedApps = getInstalledAppsList()
- result.success(installedApps)
- } catch (e: Exception) {
- result.error("ERROR", "Failed to get installed apps", e.message)
- }
+ ioExecutor.execute {
+ try {
+ val installedApps = getInstalledAppsList()
+ runOnUiThread { result.success(installedApps) }
+ } catch (e: Exception) {
+ runOnUiThread { result.error("ERROR", "Failed to get installed apps", e.message) }
+ }
+ }
}
"getAppInfo" -> {
- try {
- val packageName = call.argument<String>("packageName")
- if (packageName != null) {
- val appInfo = getAppInfo(packageName)
- result.success(appInfo)
- } else {
- result.error("INVALID_ARGUMENT", "Package name is required", null)
- }
- } catch (e: Exception) {
- result.error("ERROR", "Failed to get app info", e.message)
- }
+ ioExecutor.execute {
+ try {
+ val packageName = call.argument<String>("packageName")
+ if (packageName != null) {
+ val appInfo = getAppInfo(packageName)
+ runOnUiThread { result.success(appInfo) }
+ } else {
+ runOnUiThread { result.error("INVALID_ARGUMENT", "Package name is required", null) }
+ }
+ } catch (e: Exception) {
+ runOnUiThread { result.error("ERROR", "Failed to get app info", e.message) }
+ }
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.kt`
around lines 15 - 23, The MethodChannel handler currently calls blocking
PackageManager APIs (getInstalledPackages, getPackageInfo,
getLaunchIntentForPackage) inside the UI thread via getInstalledAppsList; move
those package queries to a background thread (e.g., use
CoroutineScope(Dispatchers.IO) or an Executor) inside the MethodChannel
setMethodCallHandler, perform all PackageManager calls in getInstalledAppsList
on that IO thread, then post the result back to the main thread before invoking
result.success or result.error (e.g., with withContext(Dispatchers.Main) or
runOnUiThread) to avoid ANRs while keeping MethodChannel callbacks on the UI
thread.
android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.kt
Outdated
Show resolved
Hide resolved
android/app/src/main/kotlin/io/github/omeritzics/updatium/MainActivity.kt
Outdated
Show resolved
Hide resolved
| "updateIntervalDescription": "Как часто проверять новые определения вредоносного ПО", | ||
| "hours": "часов", | ||
| "threatLevelFilter": "Фильтр уровня угрозы", | ||
| "threatLevelFilterDescription": "Минимальный уровень угрозы для triggering оповещений", |
There was a problem hiding this comment.
Translate the stray English term in the Russian copy.
Line 42 still contains triggering, so this screen will ship mixed-language UI.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@assets/translations/ru.json` at line 42, The translation for the key
threatLevelFilterDescription contains the English word "triggering"; update the
Russian string to remove the English term (e.g., change "Минимальный уровень
угрозы для triggering оповещений" to "Минимальный уровень угрозы для
срабатывания оповещений" or similar) so the UI text is fully Russian and
consistent.
| Future<List<PackageInfo>> getAllInstalledInfo() async { | ||
| return await pm.getInstalledPackages(flags: packageInfoFlags) ?? []; | ||
| try { | ||
| // Use platform channel to get all installed apps | ||
| const platform = MethodChannel('updatium/package_manager'); | ||
| final List<dynamic> installedApps = await platform.invokeMethod('getInstalledApps'); | ||
|
|
||
| List<PackageInfo> packageInfoList = []; | ||
| for (var appData in installedApps) { | ||
| try { | ||
| // Create PackageInfo objects from the platform data | ||
| final packageInfo = PackageInfo( | ||
| appName: appData['appName'] ?? '', | ||
| packageName: appData['packageName'] ?? '', | ||
| version: appData['version'] ?? '', | ||
| buildNumber: appData['buildNumber'] ?? '', | ||
| ); | ||
| packageInfoList.add(packageInfo); | ||
| } catch (e) { | ||
| // Skip individual app errors but continue processing others | ||
| print('Error parsing app data: $e'); // OK | ||
| } | ||
| } | ||
| return packageInfoList; | ||
| } catch (e) { | ||
| print('Error getting installed apps: $e'); // OK | ||
| return []; | ||
| } | ||
| } | ||
|
|
||
| Future<PackageInfo?> getInstalledInfo( | ||
| String? packageName, { | ||
| bool printErr = true, | ||
| }) async { | ||
| if (packageName != null) { | ||
| try { | ||
| return await pm.getPackageInfo( | ||
| packageName: packageName, | ||
| flags: packageInfoFlags, | ||
| ); | ||
| } catch (e) { | ||
| if (printErr) { | ||
| print(e); // OK | ||
| } | ||
| if (packageName == null || packageName.isEmpty) { | ||
| return null; | ||
| } | ||
|
|
||
| try { | ||
| // Use platform channel to get specific app info | ||
| const platform = MethodChannel('updatium/package_manager'); | ||
| final Map<String, dynamic>? appData = await platform.invokeMethod('getAppInfo', {'packageName': packageName}); | ||
|
|
||
| if (appData == null) { | ||
| return null; | ||
| } | ||
|
|
||
| return PackageInfo( | ||
| appName: appData['appName'] ?? '', | ||
| packageName: appData['packageName'] ?? '', | ||
| version: appData['version'] ?? '', | ||
| buildNumber: appData['buildNumber'] ?? '', | ||
| ); | ||
| } catch (e) { | ||
| if (printErr) { | ||
| print(e); // OK | ||
| } | ||
| return null; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check the file structure and locate the relevant code
wc -l lib/providers/apps_provider.dartRepository: omeritzics/Updatium
Length of output: 100
🏁 Script executed:
# Search for usages of the methods getAllInstalledInfo and getInstalledInfo
rg "(getAllInstalledInfo|getInstalledInfo)" lib/providers/apps_provider.dart -A 5 -B 2Repository: omeritzics/Updatium
Length of output: 3178
🏁 Script executed:
# Search for accesses to Android-specific fields mentioned in the review
rg "(applicationInfo|signingInfo|versionCode|versionName)" lib/providers/apps_provider.dart -B 2 -A 2Repository: omeritzics/Updatium
Length of output: 3163
🏁 Script executed:
# Check what PackageInfo type is imported
rg "import|PackageInfo" lib/providers/apps_provider.dart | head -20Repository: omeritzics/Updatium
Length of output: 870
🏁 Script executed:
# Search for PackageInfo class definition or custom implementation
rg "class PackageInfo" lib/providers/apps_provider.dart -B 3 -A 10Repository: omeritzics/Updatium
Length of output: 45
🏁 Script executed:
# Check for package_info_plus import or any PackageInfo import
rg "import.*PackageInfo|from.*package_info" lib/providers/apps_provider.dartRepository: omeritzics/Updatium
Length of output: 45
🏁 Script executed:
# Search for where PackageInfo is defined - might be in models
fd "PackageInfo" lib --type fRepository: omeritzics/Updatium
Length of output: 45
🏁 Script executed:
# Check what properties the returned PackageInfo objects have in the methods
sed -n '501,558p' lib/providers/apps_provider.dartRepository: omeritzics/Updatium
Length of output: 1832
🏁 Script executed:
# Search for PackageInfo definition more broadly across all lib files
rg "class PackageInfo|PackageInfo\s*{" lib/ -B 2 -A 15Repository: omeritzics/Updatium
Length of output: 192
🏁 Script executed:
# Check the models directory or any file that might define PackageInfo
find lib -name "*.dart" -type f | xargs grep -l "class PackageInfo" 2>/dev/nullRepository: omeritzics/Updatium
Length of output: 47
🏁 Script executed:
# Search for PackageInfo import across all Dart files
find lib -name "*.dart" -type f | xargs grep -h "^import.*PackageInfo\|^import.*package_info" 2>/dev/null | sort -uRepository: omeritzics/Updatium
Length of output: 121
🏁 Script executed:
# Check pubspec.yaml for package_info_plus dependency
cat pubspec.yaml | grep -A 2 -B 2 "package_info"Repository: omeritzics/Updatium
Length of output: 232
🏁 Script executed:
# Let me check the package_info_plus documentation to confirm what fields it actually has
# First, search for where the returned PackageInfo is actually used
rg "\.applicationInfo\?|\.signingInfo\?|\.versionCode|\.versionName" lib/providers/apps_provider.dart | wc -lRepository: omeritzics/Updatium
Length of output: 65
🏁 Script executed:
# Let's see the exact lines where these fields are accessed
rg "\.applicationInfo\?|\.signingInfo\?|\.versionCode|\.versionName" lib/providers/apps_provider.dart -n | head -15Repository: omeritzics/Updatium
Length of output: 1165
🏁 Script executed:
# Let me verify the exact signature of package_info_plus.PackageInfo by checking web documentation
# or checking if there's any extension on PackageInfo in the codebase
rg "extension.*PackageInfo|on PackageInfo" lib/ -B 2 -A 5Repository: omeritzics/Updatium
Length of output: 45
🏁 Script executed:
# Also check if PackageInfo is being extended or if there's type manipulation happening
rg "PackageInfo.*extension|PackageInfo\s*\(" lib/providers/apps_provider.dart -B 3 -A 3Repository: omeritzics/Updatium
Length of output: 548
getAllInstalledInfo() and getInstalledInfo() return an incompatible type—downstream code will fail.
You're returning package_info_plus.PackageInfo, which only carries basic metadata (appName, packageName, version, buildNumber). However, 16 places in this same file access Android-specific fields that don't exist on that type:
.applicationInfo?.targetSdkVersion(line 867).signingInfo?.hasMultipleSigners,.apkContentSigners,.signingCertificateHistory(lines 60, 66–67).versionCodeand.versionName(lines 1082, 1085, 1087, 1602, 1641, 2460).applicationInfo?.getAppIcon()and.getAppLabel()(lines 1917, 2051–2052)
Either keep the original Android-specific model or define a custom DTO that includes all required fields.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/providers/apps_provider.dart` around lines 501 - 558, getAllInstalledInfo
and getInstalledInfo currently construct and return
package_info_plus.PackageInfo which lacks Android-specific fields used
elsewhere; change these methods to return (or convert to) the Android-specific
model used elsewhere (or create a new DTO e.g.,
AndroidPackageInfo/ExtendedPackageInfo) and populate all required fields
(applicationInfo with targetSdkVersion and helper methods
getAppIcon/getAppLabel, signingInfo fields like
hasMultipleSigners/apkContentSigners/signingCertificateHistory,
versionCode/versionName, etc.) instead of PackageInfo so downstream accesses
(e.g., applicationInfo?.targetSdkVersion, signingInfo?.hasMultipleSigners,
versionCode/versionName) are valid; update the return types and constructor
usage in getAllInstalledInfo and getInstalledInfo to build and return that
extended model from the platform channel data.
| this.ruleSources = const [ | ||
| 'https://raw.githubusercontent.com/Yara-Rules/rules/master/index.yar', | ||
| 'https://raw.githubusercontent.com/Yara-Rules/rules/master/malware/MALW_YaraRule_APT.yar', | ||
| 'https://raw.githubusercontent.com/Yara-Rules/rules/master/malware/MALW_YaraRule_Mobile.yar', | ||
| ], |
There was a problem hiding this comment.
Pin rule sources to immutable revisions.
These URLs track live repository heads, so install-blocking behavior can change underneath users without an app release or integrity check. For a security gate, fetch from pinned commits/tags and verify a signed hash/manifest before loading.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/security/yara_scanner.dart` around lines 22 - 26, The ruleSources array
(this.ruleSources) currently points at mutable branch URLs; change it to use raw
URLs pinned to specific commit SHAs or tags (replace each URL in
this.ruleSources with its corresponding
raw.githubusercontent.com/.../COMMIT_HASH/path.yar) and add a verification step
before loading: fetch a signed manifest or accompanying checksum, validate the
manifest signature and compare file hashes, and refuse to load rules if
verification fails; implement the verification logic in the YaraScanner
initialization path (where this.ruleSources is consumed) so only pinned,
validated rule files are accepted.
| // Detect rule block start | ||
| if (trimmedLine.startsWith('rule ')) { | ||
| // If we were building a previous rule, finalize it first | ||
| if (currentRuleStart != -1 && currentRuleName != null) { | ||
| final ruleContent = currentRuleLines.join('\n'); | ||
| rules.add(_parseSingleRule(ruleContent, currentRuleName)); | ||
| } | ||
|
|
||
| // Start new rule | ||
| currentRuleStart = i; | ||
| currentRuleName = trimmedLine.substring(5).trim().split(' ').first; |
There was a problem hiding this comment.
Header parsing misses standard YARA tags and qualified rules.
Real YARA tags live on the rule declaration (rule Foo : trojan mobile), but this parser only looks for a non-standard tags = meta line and only recognizes declarations that start with rule . That skips private/global rule headers and leaves most loaded rules with empty tags, so threat-level filtering effectively collapses to level 1.
Also applies to: 182-193
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/security/yara_scanner.dart` around lines 140 - 150, The parser currently
only detects rules starting with "rule " and reads tags from a nonstandard "tags
=" meta, missing qualified headers like "private/global rule" and standard tags
declared after a colon; update the rule-start detection (the block using
currentRuleStart, currentRuleName and trimmedLine) to match headers with an
optional qualifier and optional tag list (e.g. regex like
^\s*(?:private|global)?\s*rule\s+([^\s{:]+)(?:\s*:\s*([^{]+))?), extract
currentRuleName from group 1 and, if group 2 exists, split by whitespace to
populate tags (in addition to keeping the existing "tags =" meta parsing), and
apply the same change to the other occurrence around lines 182-193 so
_parseSingleRule receives the rule name and combined tags correctly.
| _rules.clear(); | ||
| int loadedCount = 0; | ||
| int errorCount = 0; | ||
|
|
||
| await for (final entity in rulesDir.list()) { | ||
| if (entity is File && entity.path.endsWith('.yar')) { | ||
| try { | ||
| final content = await entity.readAsString(); | ||
| final rules = YARARule.parseMultiple(content); | ||
| _rules.addAll(rules); | ||
| loadedCount += rules.length; |
There was a problem hiding this comment.
Don’t mutate _rules in place while scans can run.
_loadRules() clears and repopulates the shared list, while scanFile() iterates it. If an auto-update lands during a scan, you'll either throw a ConcurrentModificationError or scan against a partially swapped rule set. Build a new list and swap it atomically, or snapshot _rules before iterating.
Also applies to: 399-404
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/security/yara_scanner.dart` around lines 274 - 284, _loadRules()
currently clears and repopulates the shared _rules list while scanFile() may
iterate it, causing ConcurrentModificationError or partial rule sets; fix by
building a new local List<YARARule> (e.g., newRules) inside _loadRules(), add
parsed rules to newRules, then atomically replace the shared reference (_rules =
newRules) once loading completes, and likewise ensure any other updater code
around lines referenced (399-404) uses the same replace-not-mutate pattern;
alternatively, have scanFile() take a snapshot (final rules = List.from(_rules))
before iterating to avoid concurrent mutations.
| Future<YARAMatch?> _checkRule( | ||
| YARARule rule, | ||
| List<int> fileBytes, | ||
| ) async { | ||
| // Simple string matching (basic implementation) | ||
| // In a real implementation, you'd want to use proper YARA parsing | ||
| final ruleLines = rule.content.split('\n'); | ||
| final strings = <String>[]; | ||
|
|
||
| for (final line in ruleLines) { | ||
| final trimmedLine = line.trim(); | ||
| if (trimmedLine.startsWith('condition:')) { | ||
| // condition = trimmedLine.substring(10).trim(); // Not used in basic implementation | ||
| } else if (trimmedLine.contains('\$') && trimmedLine.contains(' = ')) { | ||
| // Extract quoted strings: $name = "text" | ||
| final quotedMatch = RegExp(r'\$(\w+)\s*=\s*["\']([^"\']+)["\']').firstMatch(trimmedLine); | ||
| if (quotedMatch != null && quotedMatch.group(1) != null) { | ||
| strings.add(quotedMatch.group(1)!); | ||
| continue; | ||
| } | ||
|
|
||
| // Extract hex sequences: $name = {6A 40} or $name = 6A 40 68 | ||
| final hexMatch = RegExp(r'\$(\w+)\s*=\s*(?:{([^}]+)}|([0-9A-Fa-f\s]+))').firstMatch(trimmedLine); | ||
| if (hexMatch != null && hexMatch.group(1) != null) { | ||
| strings.add(hexMatch.group(1)!); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Check if any strings match in binary data | ||
| for (final string in strings) { | ||
| // Try quoted string pattern first | ||
| String? searchString; | ||
| List<int> searchBytes; | ||
|
|
||
| final quotedPattern = RegExp(r'\$' + string + r'\s*=\s*["\']([^"\']+)["\']'); | ||
| final quotedMatch = quotedPattern.firstMatch(rule.content); | ||
|
|
||
| if (quotedMatch != null) { | ||
| // Case 1: Quoted strings - strip quotes and use utf8.encode | ||
| searchString = quotedMatch.group(1)!; | ||
| searchBytes = utf8.encode(searchString); | ||
| } else { | ||
| // Try hex patterns (both braced and unbraced) | ||
| final hexPattern = RegExp(r'\$' + string + r'\s*=\s*(?:{([^}]+)}|([0-9A-Fa-f\s]+))'); | ||
| final hexMatch = hexPattern.firstMatch(rule.content); | ||
|
|
||
| if (hexMatch != null) { | ||
| // Case 2: Hex sequences - parse hex pairs into bytes | ||
| final hexContent = hexMatch.group(1) ?? hexMatch.group(2)!; | ||
| final cleanHex = hexContent.replaceAll(RegExp(r'\s+'), ''); | ||
| searchBytes = []; | ||
|
|
||
| for (int i = 0; i < cleanHex.length; i += 2) { | ||
| if (i + 1 < cleanHex.length) { | ||
| final byte = int.tryParse(cleanHex.substring(i, i + 2), radix: 16); | ||
| if (byte != null) { | ||
| searchBytes.add(byte); | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| // Case 3: Default text - try to find any remaining pattern and use utf8.encode | ||
| final defaultPattern = RegExp(r'\$' + string + r'\s*=\s*([^\n]+)'); | ||
| final defaultMatch = defaultPattern.firstMatch(rule.content); | ||
|
|
||
| if (defaultMatch != null) { | ||
| searchString = defaultMatch.group(1)!.trim().replaceAll(RegExp(r'^["\']|["\']$'), ''); | ||
| searchBytes = utf8.encode(searchString); | ||
| } else { | ||
| continue; // Skip if no pattern found | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Search for bytes in the file | ||
| if (_containsBytes(fileBytes, searchBytes)) { | ||
| return YARAMatch( | ||
| ruleName: rule.name, | ||
| description: rule.description ?? 'No description available', | ||
| author: rule.author, | ||
| tags: rule.tags, | ||
| threatLevel: _calculateThreatLevel(rule.tags), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /// Helper method to check if byte sequence contains another byte sequence | ||
| bool _containsBytes(List<int> data, List<int> pattern) { | ||
| if (pattern.isEmpty) return true; | ||
| if (data.length < pattern.length) return false; | ||
|
|
||
| for (int i = 0; i <= data.length - pattern.length; i++) { | ||
| bool match = true; | ||
| for (int j = 0; j < pattern.length; j++) { | ||
| if (data[i + j] != pattern[j]) { | ||
| match = false; | ||
| break; | ||
| } | ||
| } | ||
| if (match) return true; | ||
| } | ||
| return false; |
There was a problem hiding this comment.
This matcher still ignores real YARA semantics.
A rule is treated as matched as soon as any extracted byte/string appears. Upstream rules rely on condition: logic, modifiers, and hex wildcards/jumps; here unsupported tokens are stripped, and an emptied pattern will even satisfy _containsBytes() for every file. With auto-scan blocking installs, that will create both false positives and false negatives.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/security/yara_scanner.dart` around lines 418 - 523, The matcher treats
empty patterns as matches and ignores rule condition logic; update _checkRule to
(1) parse the rule's "condition:" line to determine whether it requires "any of"
or "all of" named strings (or fall back to requiring at least one match if
condition is missing), (2) collect identifier names (the $vars) and evaluate the
condition against which identifiers were actually found instead of returning on
the first found string, and (3) ensure _containsBytes returns false for empty
patterns (do not treat empty List<int> as a match) so empty or stripped patterns
cannot trigger false positives; locate logic in _checkRule (string extraction,
quotedPattern/hexPattern/defaultPattern, YARAMatch return) and the helper
_containsBytes to implement these checks.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…Activity.kt Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…Activity.kt Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Summary by CodeRabbit
Release Notes
New Features
Internationalization
Chores