diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..f8c7bde
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,28 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: talsec-app
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+If applicable, steps to reproduce the behavior.
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Please complete the following information:**
+ - Device: [e.g. Samsung Galaxy A50]
+ - OS version: [e.g. Android 12]
+ - Version of freeRASP: [e.g. 6.0.0]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..a153e1f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: talsec-app
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..20ad538
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,36 @@
+# .freeMalwareDetection
+
+
+
+## Pre-release checklist
+- [ ] ๐ Malware detection works
+- [ ] ๐ Whitelisting works
+- [ ] ๐ Logging works
+- [ ] ๐ Changelog updated
+- [ ] โก๏ธ `pubspec.yaml` version updated
+
+
+## Post-release checklist
+- [ ] ๐ GitHub release
+
+## Type of Changes
+
+- [ ] โจ New feature (non-breaking change which adds functionality)
+- [ ] ๐ ๏ธ Bug fix (non-breaking change which fixes an issue)
+- [ ] โ Breaking change (fix or feature that would cause existing functionality to change)
+- [ ] ๐งน Code refactor
+- [ ] โ
Build configuration change
+- [ ] ๐ Documentation
+- [ ] ๐๏ธ Chore
+
+## Description
+
+
diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml
new file mode 100644
index 0000000..8c1a379
--- /dev/null
+++ b/.github/workflows/flutter-ci.yml
@@ -0,0 +1,57 @@
+name: Flutter CI
+
+on:
+ push:
+ branches:
+ - main
+ paths-ignore:
+ - '**/*.md'
+ - '.github/**'
+ pull_request:
+ branches:
+ - main
+ paths-ignore:
+ - '**/*.md'
+ - '.github/**'
+
+ workflow_dispatch:
+
+env:
+ FLUTTER_VERSION: 3.24.0
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - name: ๐ Git Checkout
+ uses: actions/checkout@v4
+
+ - name: ๐ฆ Setup Flutter
+ uses: subosito/flutter-action@v2.16.0
+ with:
+ channel: stable
+ flutter-version: ${{ env.FLUTTER_VERSION }}
+ cache: true
+
+ - name: โ Format Code
+ run: dart format --set-exit-if-changed .
+
+ - name: ๐ Analyze Code
+ run: flutter analyze --fatal-infos --fatal-warnings .
+
+ build-android:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: ๐ Git Checkout
+ uses: actions/checkout@v4
+
+ - name: ๐ฆ Setup Flutter
+ uses: subosito/flutter-action@v2.16.0
+ with:
+ channel: stable
+ flutter-version: ${{ env.FLUTTER_VERSION }}
+ cache: true
+
+ - name: ๐ค Build Android app
+ run: flutter build apk --release
diff --git a/.github/workflows/issue_watcher.yaml b/.github/workflows/issue_watcher.yaml
new file mode 100644
index 0000000..61f8c6d
--- /dev/null
+++ b/.github/workflows/issue_watcher.yaml
@@ -0,0 +1,11 @@
+name: Issue watcher
+on:
+ workflow_dispatch:
+ schedule:
+ # Date follows the cron syntax: https://en.wikipedia.org/wiki/Cron
+ # Every Monday at 6:30 AM
+ - cron: '30 6 * * 1'
+
+jobs:
+ check_issues:
+ uses: talsec/github-workflows/.github/workflows/issue_watcher.yml@master
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..29a3a50
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/.metadata b/.metadata
new file mode 100644
index 0000000..8d5d37e
--- /dev/null
+++ b/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "a14f74ff3a1cbd521163c5f03d68113d50af93d3"
+ channel: "stable"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
+ base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
+ - platform: android
+ create_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
+ base_revision: a14f74ff3a1cbd521163c5f03d68113d50af93d3
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..571a38d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.0.0] - 2024-11-08
+- Initial release
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..4653834
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Talsec
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index e965047..ab78dc4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,46 @@
-Hello
+
+
+# freeMalwareDetection for Flutter
+
+Enhance the security of your Android application with Free Malware Detection, a powerful feature designed to scan for malicious or suspicious apps. Leveraging various blacklists and security policies, this tool ensures your application remains protected from known threats, providing a secure environment for your users.
+
+## Overview
+
+freeMalwareDetection is an easy-to-integrate SDK that operates asynchronously to safeguard your application without compromising performance. It runs scans in the background, ensuring that your app's performance remains unaffected while delivering comprehensive security checks. This SDK is freely available and designed for seamless integration with the freeRASP SDK, delivering a comprehensive security solution.
+
+## Key Advantages
+
+- **Asynchronous Scanning**: Perform malware scans in the background without impacting app performance.
+- **Threat Analysis**: Receive information about detected threats to better inform and protect your users.
+- **Simple Integration**: Easy-to-follow integration process with extensive documentation and support.
+- **Versatile Blacklisting**: Supports hash-based, package name-based, and permission-based blacklists for comprehensive threat detection.
+- **Seamless Operation**: Integrates smoothly with freeRASP SDK to provide an all-in-one security solution.
+
+## ๐ฏ Features
+
+Free Malware Detection provides protection against potentially dangerous applications and behaviors, including:
+
+โ๏ธ Detection of apps with suspicious package names or hashes.
+
+โ๏ธ Blocking of apps with disallowed permissions.
+
+โ๏ธ Whitelisting of trusted installation sources to prevent unauthorized app installations.
+
+Visit our wiki to learn more about the specific checks performed and their impact on app security.
+
+## ๐ Discover Official Documentation
+
+Explore our [GitBook page](https://docs.talsec.app/freemalwaredetection) for detailed guides, tutorials, and technical documentation.
+
+## ๐ Integration Guide
+
+To integrate Free Malware Detection, follow our step-by-step [Integration Guide](https://docs.talsec.app/freemalwaredetection/integration-guide/requirements). This guide provides all the details you need for a smooth setup process on any platform.
+
+## โจ Enhancements
+
+If you have any suggestions for improvement or notice anything that could be clarified in the new documentation, please open an issue. Your feedback helps us maintain high-quality resources for all users.
+
+You can check out the [project board](https://github.com/orgs/talsec/projects/3) here.
+
+## :page_facing_up: License
+This project is provided as freemium software, i.e. there is a fair usage policy that imposes some limitations on the free usage. The SDK software consists of open-source and binary parts, which is the property of Talsec. The open-source part is licensed under the MIT License - see the LICENSE file for details.
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..271239f
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,5 @@
+include: package:very_good_analysis/analysis_options.yaml
+
+linter:
+ rules:
+ public_member_api_docs: false
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 0000000..b69dfa6
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,56 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file("local.properties")
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader("UTF-8") { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
+if (flutterVersionCode == null) {
+ flutterVersionCode = "1"
+}
+
+def flutterVersionName = localProperties.getProperty("flutter.versionName")
+if (flutterVersionName == null) {
+ flutterVersionName = "1.0"
+}
+
+android {
+ namespace = "app.talsec.free_malware_detection"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ defaultConfig {
+ applicationId = "app.talsec.free_malware_detection"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+ minSdk = 23
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutterVersionCode.toInteger()
+ versionName = flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f1a6f7e
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/app/talsec/free_malware_detection/MainActivity.kt b/android/app/src/main/kotlin/app/talsec/free_malware_detection/MainActivity.kt
new file mode 100644
index 0000000..6ee3832
--- /dev/null
+++ b/android/app/src/main/kotlin/app/talsec/free_malware_detection/MainActivity.kt
@@ -0,0 +1,5 @@
+package app.talsec.free_malware_detection
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity()
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..d2ffbff
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,18 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = "../build"
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..3b5b324
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..e1ca574
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..536165d
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,25 @@
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.7.10" apply false
+}
+
+include ":app"
diff --git a/lib/app.dart b/lib/app.dart
new file mode 100644
index 0000000..90e580a
--- /dev/null
+++ b/lib/app.dart
@@ -0,0 +1,16 @@
+import 'package:flutter/material.dart';
+import 'package:free_malware_detection/home/views/views.dart';
+
+/// The root widget of the application
+class App extends StatelessWidget {
+ const App({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Demo',
+ theme: ThemeData(primarySwatch: Colors.blue),
+ home: const HomePage(),
+ );
+ }
+}
diff --git a/lib/home/providers/providers.dart b/lib/home/providers/providers.dart
new file mode 100644
index 0000000..da415da
--- /dev/null
+++ b/lib/home/providers/providers.dart
@@ -0,0 +1,2 @@
+export 'threat_notifier.dart';
+export 'threat_state.dart';
diff --git a/lib/home/providers/threat_notifier.dart b/lib/home/providers/threat_notifier.dart
new file mode 100644
index 0000000..f5f5c6b
--- /dev/null
+++ b/lib/home/providers/threat_notifier.dart
@@ -0,0 +1,29 @@
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:free_malware_detection/home/providers/threat_state.dart';
+import 'package:freerasp/freerasp.dart';
+
+/// Class responsible for setting up listeners to detected threats
+class ThreatNotifier extends AutoDisposeNotifier {
+ @override
+ ThreatState build() {
+ _init();
+ return ThreatState(detectedMalware: {}, isDetected: false);
+ }
+
+ void _init() {
+ final threatCallback = ThreatCallback(onMalware: _updateMalware);
+ Talsec.instance.attachListener(threatCallback);
+ }
+
+ void _updateMalware(List malware) {
+ state = state.copyWith(
+ detectedMalware: malware.nonNulls.toSet(),
+ hasDetectedMalware: true,
+ );
+ }
+
+ void removeMalware(SuspiciousAppInfo malware) {
+ final malwareSet = state.detectedMalware..remove(malware);
+ state = state.copyWith(detectedMalware: malwareSet);
+ }
+}
diff --git a/lib/home/providers/threat_state.dart b/lib/home/providers/threat_state.dart
new file mode 100644
index 0000000..f37d777
--- /dev/null
+++ b/lib/home/providers/threat_state.dart
@@ -0,0 +1,21 @@
+import 'package:freerasp/freerasp.dart';
+
+class ThreatState {
+ ThreatState({
+ required this.detectedMalware,
+ required this.isDetected,
+ });
+
+ final Set detectedMalware;
+ final bool isDetected;
+
+ ThreatState copyWith({
+ Set? detectedMalware,
+ bool? hasDetectedMalware,
+ }) {
+ return ThreatState(
+ detectedMalware: detectedMalware ?? this.detectedMalware,
+ isDetected: hasDetectedMalware ?? isDetected,
+ );
+ }
+}
diff --git a/lib/home/views/home_page.dart b/lib/home/views/home_page.dart
new file mode 100644
index 0000000..6dd07cf
--- /dev/null
+++ b/lib/home/views/home_page.dart
@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:free_malware_detection/home/views/views.dart';
+import 'package:free_malware_detection/home/widgets/widgets.dart';
+
+import 'package:free_malware_detection/main.dart';
+import 'package:freerasp/freerasp.dart';
+
+/// The home page that displays the threats and results
+class HomePage extends ConsumerWidget {
+ const HomePage({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final threatState = ref.watch(threatProvider);
+
+ return Scaffold(
+ appBar: AppBar(title: const Text('freeMalwareDetection Demo')),
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const StatusTile(
+ title: '.freeRASP',
+ subtitle: 'Up and running',
+ icon: Icons.check_circle_outline_rounded,
+ iconColor: Colors.green,
+ ),
+ StatusTile(
+ title: '.freeMalwareDetection',
+ subtitle: threatState.isDetected ? 'Scan done' : 'Scan running',
+ icon: threatState.isDetected
+ ? Icons.check_circle_outline_rounded
+ : Icons.sync_outlined,
+ iconColor:
+ threatState.isDetected ? Colors.green : Colors.orange,
+ isSpinning: !threatState.isDetected, // Spins if scan is running
+ ),
+ Visibility(
+ visible: threatState.isDetected,
+ child: OutlinedButton(
+ onPressed: () => _showMalwareBottomSheet(
+ context,
+ threatState.detectedMalware.toList(),
+ ref,
+ ),
+ child: Text(
+ 'Show Results'.toUpperCase(),
+ style: Theme.of(context).textTheme.labelLarge,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+void _showMalwareBottomSheet(
+ BuildContext context,
+ List suspiciousApps,
+ WidgetRef ref,
+) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ showModalBottomSheet(
+ context: context,
+ isDismissible: false,
+ enableDrag: false,
+ isScrollControlled: true,
+ builder: (BuildContext context) => MalwareBottomSheet(
+ suspiciousApps: suspiciousApps,
+ onDismiss: () => Navigator.pop(context),
+ ),
+ );
+ });
+}
diff --git a/lib/home/views/malware_bottom_sheet.dart b/lib/home/views/malware_bottom_sheet.dart
new file mode 100644
index 0000000..5b33eb3
--- /dev/null
+++ b/lib/home/views/malware_bottom_sheet.dart
@@ -0,0 +1,84 @@
+import 'package:android_intent_plus/android_intent.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:free_malware_detection/home/widgets/widgets.dart';
+import 'package:free_malware_detection/main.dart';
+import 'package:free_malware_detection/services/talsec_service.dart';
+import 'package:freerasp/freerasp.dart';
+
+/// Bottom sheet widget that displays malware information
+class MalwareBottomSheet extends ConsumerWidget {
+ /// Represents malware information in the example app
+ const MalwareBottomSheet({
+ required this.suspiciousApps,
+ required this.onDismiss,
+ super.key,
+ });
+
+ /// List of suspicious apps
+ final List suspiciousApps;
+ final void Function()? onDismiss;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final textTheme = Theme.of(context).textTheme;
+ final suspiciousApps = ref.watch(threatProvider).detectedMalware.toList();
+
+ return Container(
+ height: double.infinity,
+ padding: const EdgeInsets.all(8),
+ child: Column(
+ children: [
+ Text('Suspicious Apps', style: textTheme.titleMedium),
+ const SizedBox(height: 8),
+ Expanded(
+ child: ListView.builder(
+ itemCount: suspiciousApps.length,
+ itemBuilder: (_, index) {
+ final item = suspiciousApps[index];
+ return MalwareItem(
+ packageInfo: item.packageInfo,
+ reason: item.reason,
+ onWhitelist: () {
+ _removeItem(ref, item);
+ TalsecService.whitelistApp(item);
+ },
+ onUninstall: () => _launchUninstall(item),
+ onDismiss: () {
+ _whitelistApp(item);
+ _removeItem(ref, item);
+ },
+ );
+ },
+ ),
+ ),
+ const SizedBox(height: 16),
+ SizedBox(
+ width: double.infinity,
+ child: FilledButton(
+ onPressed: onDismiss,
+ child: const Text('Dismiss'),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ void _removeItem(WidgetRef ref, SuspiciousAppInfo item) {
+ ref.read(threatProvider.notifier).removeMalware(item);
+ }
+
+ Future _whitelistApp(SuspiciousAppInfo malware) async {
+ await TalsecService.whitelistApp(malware);
+ }
+
+ Future _launchUninstall(SuspiciousAppInfo item) async {
+ // Launch uninstall intent
+ final packageName = item.packageInfo.packageName;
+ await AndroidIntent(
+ action: 'action_application_details_settings',
+ data: 'package:$packageName',
+ ).launch();
+ }
+}
diff --git a/lib/home/views/views.dart b/lib/home/views/views.dart
new file mode 100644
index 0000000..62079ac
--- /dev/null
+++ b/lib/home/views/views.dart
@@ -0,0 +1,2 @@
+export 'home_page.dart';
+export 'malware_bottom_sheet.dart';
diff --git a/lib/home/widgets/app_icon.dart b/lib/home/widgets/app_icon.dart
new file mode 100644
index 0000000..7735f62
--- /dev/null
+++ b/lib/home/widgets/app_icon.dart
@@ -0,0 +1,22 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+
+/// Displays the app icon or a default warning icon if unavailable
+class AppIcon extends StatelessWidget {
+ const AppIcon({super.key, this.base64Icon});
+
+ final String? base64Icon;
+
+ @override
+ Widget build(BuildContext context) {
+ if (base64Icon == null) {
+ return const Icon(Icons.warning, color: Colors.red);
+ }
+
+ return Padding(
+ padding: const EdgeInsets.all(4),
+ child: Image.memory(base64.decode(base64Icon!)),
+ );
+ }
+}
diff --git a/lib/home/widgets/dismiss_background.dart b/lib/home/widgets/dismiss_background.dart
new file mode 100644
index 0000000..9dc1527
--- /dev/null
+++ b/lib/home/widgets/dismiss_background.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+
+/// Dismiss background widget with a customizable message
+class DismissBackground extends StatelessWidget {
+ const DismissBackground({required this.message, super.key});
+
+ final String message;
+
+ @override
+ Widget build(BuildContext context) {
+ final textTheme = Theme.of(context).textTheme;
+ return Container(
+ color: Colors.amber,
+ alignment: Alignment.centerLeft,
+ padding: const EdgeInsets.only(left: 16),
+ child: Row(
+ children: [
+ const Icon(Icons.close, color: Colors.white),
+ const SizedBox(width: 8),
+ Text(
+ message.toUpperCase(),
+ style: textTheme.labelLarge?.copyWith(color: Colors.white),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/home/widgets/malware_item.dart b/lib/home/widgets/malware_item.dart
new file mode 100644
index 0000000..16c5c17
--- /dev/null
+++ b/lib/home/widgets/malware_item.dart
@@ -0,0 +1,72 @@
+import 'package:flutter/material.dart';
+import 'package:free_malware_detection/home/widgets/widgets.dart';
+import 'package:freerasp/freerasp.dart';
+
+extension on PackageInfo {
+ String get validAppName => appName ?? packageName;
+}
+
+/// List tile widget that displays malware information
+class MalwareItem extends StatelessWidget {
+ /// Represents malware information in the example app
+ const MalwareItem({
+ required this.packageInfo,
+ required this.reason,
+ this.onWhitelist,
+ this.onUninstall,
+ this.onDismiss,
+ super.key,
+ });
+
+ final PackageInfo packageInfo;
+ final String reason;
+ final void Function()? onWhitelist;
+ final void Function()? onUninstall;
+ final void Function()? onDismiss;
+
+ @override
+ Widget build(BuildContext context) {
+ return Dismissible(
+ key: ValueKey(packageInfo.packageName),
+ onDismissed: (_) => onDismiss?.call(),
+ direction: DismissDirection.startToEnd,
+ background: const DismissBackground(message: 'Dismiss'),
+ child: ExpansionTile(
+ tilePadding: const EdgeInsets.all(8),
+ title: Text(packageInfo.validAppName),
+ leading: AppIcon(base64Icon: packageInfo.appIcon),
+ children: [
+ ListTile(
+ title: const Text('Package Name'),
+ subtitle: Text(packageInfo.packageName),
+ ),
+ ListTile(
+ title: const Text('Installation Source'),
+ subtitle: Text(packageInfo.installationSource ?? 'Unknown'),
+ ),
+ ListTile(
+ title: const Text('Version'),
+ subtitle: Text(packageInfo.version ?? 'Unknown'),
+ ),
+ ListTile(
+ title: const Text('Detection Reason'),
+ subtitle: Text(reason),
+ ),
+ OverflowBar(
+ alignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => onWhitelist?.call(),
+ child: const Text('Whitelist'),
+ ),
+ TextButton(
+ onPressed: () => onUninstall?.call(),
+ child: const Text('Uninstall'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/home/widgets/spinning_icon.dart b/lib/home/widgets/spinning_icon.dart
new file mode 100644
index 0000000..861dc1b
--- /dev/null
+++ b/lib/home/widgets/spinning_icon.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+
+class SpinningIcon extends StatefulWidget {
+ const SpinningIcon({
+ required this.icon,
+ required this.iconColor,
+ super.key,
+ });
+
+ final IconData icon;
+ final Color iconColor;
+
+ @override
+ State createState() => _SpinningIconState();
+}
+
+class _SpinningIconState extends State
+ with SingleTickerProviderStateMixin {
+ late final AnimationController _controller = AnimationController(
+ duration: const Duration(milliseconds: 1000),
+ vsync: this,
+ )..repeat();
+
+ late final Animation _curvedAnimation = CurvedAnimation(
+ parent: _controller,
+ curve: Curves.decelerate,
+ );
+
+ @override
+ void dispose() {
+ _controller.dispose(); // Dispose of the controller to free resources
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return RotationTransition(
+ turns: _curvedAnimation,
+ child: Icon(widget.icon, color: widget.iconColor),
+ );
+ }
+}
diff --git a/lib/home/widgets/status_tile.dart b/lib/home/widgets/status_tile.dart
new file mode 100644
index 0000000..e6032fe
--- /dev/null
+++ b/lib/home/widgets/status_tile.dart
@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+import 'package:free_malware_detection/home/widgets/widgets.dart';
+
+class StatusTile extends StatelessWidget {
+ const StatusTile({
+ required this.title,
+ required this.subtitle,
+ required this.icon,
+ required this.iconColor,
+ super.key,
+ this.isSpinning = false,
+ });
+
+ final String title;
+ final String subtitle;
+ final IconData icon;
+ final Color iconColor;
+ final bool isSpinning;
+
+ @override
+ Widget build(BuildContext context) {
+ return ListTile(
+ trailing: isSpinning
+ ? SpinningIcon(icon: icon, iconColor: iconColor)
+ : Icon(icon, color: iconColor),
+ title: Text(title),
+ subtitle: Text(subtitle),
+ );
+ }
+}
diff --git a/lib/home/widgets/widgets.dart b/lib/home/widgets/widgets.dart
new file mode 100644
index 0000000..4397a47
--- /dev/null
+++ b/lib/home/widgets/widgets.dart
@@ -0,0 +1,5 @@
+export 'app_icon.dart';
+export 'dismiss_background.dart';
+export 'malware_item.dart';
+export 'spinning_icon.dart';
+export 'status_tile.dart';
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..d649049
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,19 @@
+// ignore_for_file: public_member_api_docs, avoid_redundant_argument_values
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:free_malware_detection/app.dart';
+import 'package:free_malware_detection/home/providers/providers.dart';
+import 'package:free_malware_detection/services/talsec_service.dart';
+
+/// Represents current state of the threats detectable by freeRASP
+final threatProvider =
+ NotifierProvider.autoDispose(
+ ThreatNotifier.new,
+);
+
+Future main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await TalsecService.initialize();
+ runApp(const ProviderScope(child: App()));
+}
diff --git a/lib/services/services.dart b/lib/services/services.dart
new file mode 100644
index 0000000..6f05765
--- /dev/null
+++ b/lib/services/services.dart
@@ -0,0 +1 @@
+export 'talsec_service.dart';
diff --git a/lib/services/talsec_service.dart b/lib/services/talsec_service.dart
new file mode 100644
index 0000000..eefa7f4
--- /dev/null
+++ b/lib/services/talsec_service.dart
@@ -0,0 +1,42 @@
+// ignore_for_file: avoid_redundant_argument_values
+
+import 'package:freerasp/freerasp.dart';
+
+class TalsecService {
+ static Future initialize() async {
+ final config = TalsecConfig(
+ androidConfig: AndroidConfig(
+ // โ๏ธ Do not retrieve any package information dynamically using
+ // โ๏ธ package_info_plus or similar packages. These can return false
+ // โ๏ธ information when the app is spoofed.
+ packageName: 'app.talsec.free_malware_detection',
+ signingCertHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='],
+ supportedStores: ['com.sec.android.app.samsungapps'],
+ malwareConfig: MalwareConfig(
+ // Let app detect itself as malware
+ blacklistedPackageNames: ['app.talsec.free_malware_detection'],
+ // Apps CANNOT have these (groups) of permissions
+ suspiciousPermissions: [
+ [
+ 'android.permission.CAMERA',
+ ],
+ /* OR */
+ [
+ 'android.permission.READ_SMS',
+ /* AND */
+ 'android.permission.READ_CONTACTS',
+ ],
+ ],
+ ),
+ ),
+ watcherMail: 'your_mail@example.com',
+ isProd: true,
+ );
+
+ await Talsec.instance.start(config);
+ }
+
+ static Future whitelistApp(SuspiciousAppInfo malware) async {
+ await Talsec.instance.addToWhitelist(malware.packageInfo.packageName);
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
new file mode 100644
index 0000000..50c4d53
--- /dev/null
+++ b/pubspec.lock
@@ -0,0 +1,277 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ android_intent_plus:
+ dependency: "direct main"
+ description:
+ name: android_intent_plus
+ sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.3"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.22"
+ flutter_riverpod:
+ dependency: "direct main"
+ description:
+ name: flutter_riverpod
+ sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.1"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ freerasp:
+ dependency: "direct main"
+ description:
+ name: freerasp
+ sha256: f18b728a54b27d1dfc4666df27a12d04da4943072c65f3ad06a648f877979ba7
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.8.0"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.9.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.5"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.5"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.16+1"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.1"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.15.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.6"
+ riverpod:
+ dependency: transitive
+ description:
+ name: riverpod
+ sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.1"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.1"
+ state_notifier:
+ dependency: transitive
+ description:
+ name: state_notifier
+ sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.2"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ very_good_analysis:
+ dependency: "direct dev"
+ description:
+ name: very_good_analysis
+ sha256: "1fb637c0022034b1f19ea2acb42a3603cbd8314a470646a59a2fb01f5f3a8629"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.0"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.2.5"
+sdks:
+ dart: ">=3.4.1 <4.0.0"
+ flutter: ">=3.22.0"
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..55cfda0
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,22 @@
+name: free_malware_detection
+description: "Flutter malware detection library for Android. Protect your app against potential threats with Talsec."
+publish_to: 'none'
+version: 1.0.0
+
+environment:
+ sdk: '>=3.4.1 <4.0.0'
+
+dependencies:
+ android_intent_plus: 4.0.3
+ flutter:
+ sdk: flutter
+ flutter_riverpod: ^2.6.1
+ freerasp: 6.8.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ very_good_analysis: ^6.0.0
+
+flutter:
+ uses-material-design: true