Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mvt-project/mvt
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.5.4
Choose a base ref
...
head repository: mvt-project/mvt
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
Showing with 14,440 additions and 660 deletions.
  1. +0 −11 .github/workflows/black.yml
  2. +23 −0 .github/workflows/mypy.yml
  3. +61 −0 .github/workflows/publish-release-docker.yml
  4. +0 −52 .github/workflows/python-package.yml
  5. +10 −2 .github/workflows/ruff.yml
  6. +1 −1 .github/workflows/scripts/update-ios-releases.py
  7. +38 −0 .github/workflows/tests.yml
  8. +1 −0 .github/workflows/update-ios-data.yml
  9. +2 −0 .safety-policy.yml
  10. +54 −8 CONTRIBUTING.md
  11. +132 −52 Dockerfile
  12. +36 −0 Dockerfile.android
  13. +137 −0 Dockerfile.ios
  14. +30 −9 Makefile
  15. +1 −1 README.md
  16. +0 −14 dev/mvt-android
  17. +0 −14 dev/mvt-ios
  18. +1 −1 docs/android/download_apks.md
  19. +43 −0 docs/command_completion.md
  20. +19 −1 docs/docker.md
  21. +4 −0 docs/install.md
  22. +10 −0 docs/iocs.md
  23. +5 −5 docs/requirements.txt
  24. +2 −2 mkdocs.yml
  25. +0 −36 mvt/android/artifacts/artifact.py
  26. +0 −18 mvt/common/help.py
  27. +106 −0 pyproject.toml
  28. +0 −6 ruff.toml
  29. +0 −14 scripts/lint.sh
  30. +0 −96 setup.cfg
  31. +0 −8 setup.py
  32. 0 { → src}/mvt/__init__.py
  33. 0 { → src}/mvt/android/__init__.py
  34. 0 { → src}/mvt/android/artifacts/__init__.py
  35. +42 −0 src/mvt/android/artifacts/artifact.py
  36. +2 −1 { → src}/mvt/android/artifacts/dumpsys_accessibility.py
  37. +163 −0 src/mvt/android/artifacts/dumpsys_adb.py
  38. +40 −7 { → src}/mvt/android/artifacts/dumpsys_appops.py
  39. 0 { → src}/mvt/android/artifacts/dumpsys_battery_daily.py
  40. 0 { → src}/mvt/android/artifacts/dumpsys_battery_history.py
  41. 0 { → src}/mvt/android/artifacts/dumpsys_dbinfo.py
  42. 0 { → src}/mvt/android/artifacts/dumpsys_package_activities.py
  43. +1 −2 { → src}/mvt/android/artifacts/dumpsys_packages.py
  44. +42 −0 src/mvt/android/artifacts/dumpsys_platform_compat.py
  45. 0 { → src}/mvt/android/artifacts/dumpsys_receivers.py
  46. +43 −0 src/mvt/android/artifacts/file_timestamps.py
  47. +11 −0 { → src}/mvt/android/artifacts/getprop.py
  48. 0 { → src}/mvt/android/artifacts/processes.py
  49. +5 −0 { → src}/mvt/android/artifacts/settings.py
  50. +272 −0 src/mvt/android/artifacts/tombstone_crashes.py
  51. +32 −50 { → src}/mvt/android/cli.py
  52. 0 { → src}/mvt/android/cmd_check_adb.py
  53. 0 { → src}/mvt/android/cmd_check_androidqf.py
  54. 0 { → src}/mvt/android/cmd_check_backup.py
  55. 0 { → src}/mvt/android/cmd_check_bugreport.py
  56. +4 −2 { → src}/mvt/android/cmd_download_apks.py
  57. 0 { → src}/mvt/android/modules/__init__.py
  58. +2 −0 { → src}/mvt/android/modules/adb/__init__.py
  59. +3 −4 { → src}/mvt/android/modules/adb/base.py
  60. +2 −1 { → src}/mvt/android/modules/adb/chrome_history.py
  61. 0 { → src}/mvt/android/modules/adb/dumpsys_accessibility.py
  62. 0 { → src}/mvt/android/modules/adb/dumpsys_activities.py
  63. +45 −0 src/mvt/android/modules/adb/dumpsys_adbstate.py
  64. 0 { → src}/mvt/android/modules/adb/dumpsys_appops.py
  65. 0 { → src}/mvt/android/modules/adb/dumpsys_battery_daily.py
  66. 0 { → src}/mvt/android/modules/adb/dumpsys_battery_history.py
  67. 0 { → src}/mvt/android/modules/adb/dumpsys_dbinfo.py
  68. 0 { → src}/mvt/android/modules/adb/dumpsys_full.py
  69. 0 { → src}/mvt/android/modules/adb/dumpsys_receivers.py
  70. 0 { → src}/mvt/android/modules/adb/files.py
  71. 0 { → src}/mvt/android/modules/adb/getprop.py
  72. 0 { → src}/mvt/android/modules/adb/logcat.py
  73. +1 −2 { → src}/mvt/android/modules/adb/packages.py
  74. 0 { → src}/mvt/android/modules/adb/processes.py
  75. 0 { → src}/mvt/android/modules/adb/root_binaries.py
  76. 0 { → src}/mvt/android/modules/adb/selinux_status.py
  77. 0 { → src}/mvt/android/modules/adb/settings.py
  78. +7 −4 { → src}/mvt/android/modules/adb/sms.py
  79. +2 −1 { → src}/mvt/android/modules/adb/whatsapp.py
  80. +8 −0 { → src}/mvt/android/modules/androidqf/__init__.py
  81. +32 −0 { → src}/mvt/android/modules/androidqf/base.py
  82. 0 { → src}/mvt/android/modules/androidqf/dumpsys_accessibility.py
  83. 0 { → src}/mvt/android/modules/androidqf/dumpsys_activities.py
  84. +51 −0 src/mvt/android/modules/androidqf/dumpsys_adb.py
  85. 0 { → src}/mvt/android/modules/androidqf/dumpsys_appops.py
  86. 0 { → src}/mvt/android/modules/androidqf/dumpsys_battery_daily.py
  87. 0 { → src}/mvt/android/modules/androidqf/dumpsys_battery_history.py
  88. 0 { → src}/mvt/android/modules/androidqf/dumpsys_dbinfo.py
  89. 0 { → src}/mvt/android/modules/androidqf/dumpsys_packages.py
  90. +44 −0 src/mvt/android/modules/androidqf/dumpsys_platform_compat.py
  91. 0 { → src}/mvt/android/modules/androidqf/dumpsys_receivers.py
  92. +151 −0 src/mvt/android/modules/androidqf/files.py
  93. 0 { → src}/mvt/android/modules/androidqf/getprop.py
  94. +65 −0 src/mvt/android/modules/androidqf/logfile_timestamps.py
  95. +128 −0 src/mvt/android/modules/androidqf/packages.py
  96. 0 { → src}/mvt/android/modules/androidqf/processes.py
  97. 0 { → src}/mvt/android/modules/androidqf/settings.py
  98. 0 { → src}/mvt/android/modules/androidqf/sms.py
  99. 0 { → src}/mvt/android/modules/backup/__init__.py
  100. 0 { → src}/mvt/android/modules/backup/base.py
  101. +9 −8 { → src}/mvt/android/modules/backup/helpers.py
  102. +2 −1 { → src}/mvt/android/modules/backup/sms.py
  103. +8 −0 { → src}/mvt/android/modules/bugreport/__init__.py
  104. 0 { → src}/mvt/android/modules/bugreport/accessibility.py
  105. 0 { → src}/mvt/android/modules/bugreport/activities.py
  106. +54 −0 src/mvt/android/modules/bugreport/adb_state.py
  107. 0 { → src}/mvt/android/modules/bugreport/appops.py
  108. +9 −2 { → src}/mvt/android/modules/bugreport/base.py
  109. 0 { → src}/mvt/android/modules/bugreport/battery_daily.py
  110. 0 { → src}/mvt/android/modules/bugreport/battery_history.py
  111. 0 { → src}/mvt/android/modules/bugreport/dbinfo.py
  112. +55 −0 src/mvt/android/modules/bugreport/fs_timestamps.py
  113. 0 { → src}/mvt/android/modules/bugreport/getprop.py
  114. 0 { → src}/mvt/android/modules/bugreport/packages.py
  115. +48 −0 src/mvt/android/modules/bugreport/platform_compat.py
  116. 0 { → src}/mvt/android/modules/bugreport/receivers.py
  117. +64 −0 src/mvt/android/modules/bugreport/tombstones.py
  118. 0 { → src}/mvt/android/parsers/__init__.py
  119. +3 −1 { → src}/mvt/android/parsers/backup.py
  120. 0 src/mvt/android/parsers/proto/__init__.py
  121. +195 −0 src/mvt/android/parsers/proto/tombstone.proto
  122. +208 −0 src/mvt/android/parsers/proto/tombstone.py
  123. +12 −0 { → src}/mvt/android/utils.py
  124. 0 { → src}/mvt/common/__init__.py
  125. 0 { → src}/mvt/common/artifact.py
  126. 0 { → src}/mvt/common/cmd_check_iocs.py
  127. +15 −4 { → src}/mvt/common/command.py
  128. +105 −0 src/mvt/common/config.py
  129. +51 −0 src/mvt/common/help.py
  130. +179 −36 { → src}/mvt/common/indicators.py
  131. +19 −5 { → src}/mvt/common/logo.py
  132. +7 −2 { → src}/mvt/common/module.py
  133. 0 { → src}/mvt/common/options.py
  134. +16 −7 { → src}/mvt/common/updates.py
  135. +0 −4 { → src}/mvt/common/url.py
  136. +5 −4 { → src}/mvt/common/utils.py
  137. +1 −1 { → src}/mvt/common/version.py
  138. 0 { → src}/mvt/common/virustotal.py
  139. 0 { → src}/mvt/ios/__init__.py
  140. +34 −55 { → src}/mvt/ios/cli.py
  141. 0 { → src}/mvt/ios/cmd_check_backup.py
  142. 0 { → src}/mvt/ios/cmd_check_fs.py
  143. 0 { → src}/mvt/ios/data/ios_models.json
  144. +40 −0 { → src}/mvt/ios/data/ios_versions.json
  145. 0 { → src}/mvt/ios/decrypt.py
  146. 0 { → src}/mvt/ios/modules/__init__.py
  147. 0 { → src}/mvt/ios/modules/backup/__init__.py
  148. +1 −1 { → src}/mvt/ios/modules/backup/backup_info.py
  149. 0 { → src}/mvt/ios/modules/backup/configuration_profiles.py
  150. +7 −6 { → src}/mvt/ios/modules/backup/manifest.py
  151. 0 { → src}/mvt/ios/modules/backup/profile_events.py
  152. +6 −2 { → src}/mvt/ios/modules/base.py
  153. 0 { → src}/mvt/ios/modules/fs/__init__.py
  154. +2 −9 { → src}/mvt/ios/modules/fs/analytics.py
  155. 0 { → src}/mvt/ios/modules/fs/analytics_ios_versions.py
  156. +1 −1 { → src}/mvt/ios/modules/fs/cache_files.py
  157. 0 { → src}/mvt/ios/modules/fs/filesystem.py
  158. 0 { → src}/mvt/ios/modules/fs/net_netusage.py
  159. +2 −2 { → src}/mvt/ios/modules/fs/safari_favicon.py
  160. 0 { → src}/mvt/ios/modules/fs/shutdownlog.py
  161. 0 { → src}/mvt/ios/modules/fs/version_history.py
  162. +1 −1 { → src}/mvt/ios/modules/fs/webkit_base.py
  163. 0 { → src}/mvt/ios/modules/fs/webkit_indexeddb.py
  164. 0 { → src}/mvt/ios/modules/fs/webkit_localstorage.py
  165. 0 { → src}/mvt/ios/modules/fs/webkit_safariviewservice.py
  166. 0 { → src}/mvt/ios/modules/mixed/__init__.py
  167. +10 −6 { → src}/mvt/ios/modules/mixed/applications.py
  168. 0 { → src}/mvt/ios/modules/mixed/calendar.py
  169. 0 { → src}/mvt/ios/modules/mixed/calls.py
  170. +3 −3 { → src}/mvt/ios/modules/mixed/chrome_favicon.py
  171. +1 −1 { → src}/mvt/ios/modules/mixed/chrome_history.py
  172. 0 { → src}/mvt/ios/modules/mixed/contacts.py
  173. +2 −2 { → src}/mvt/ios/modules/mixed/firefox_favicon.py
  174. +1 −1 { → src}/mvt/ios/modules/mixed/firefox_history.py
  175. 0 { → src}/mvt/ios/modules/mixed/global_preferences.py
  176. 0 { → src}/mvt/ios/modules/mixed/idstatuscache.py
  177. 0 { → src}/mvt/ios/modules/mixed/interactionc.py
  178. 0 { → src}/mvt/ios/modules/mixed/locationd.py
  179. 0 { → src}/mvt/ios/modules/mixed/net_datausage.py
  180. 0 { → src}/mvt/ios/modules/mixed/osanalytics_addaily.py
  181. +2 −2 { → src}/mvt/ios/modules/mixed/safari_browserstate.py
  182. +1 −1 { → src}/mvt/ios/modules/mixed/safari_history.py
  183. +5 −4 { → src}/mvt/ios/modules/mixed/shortcuts.py
  184. +2 −2 { → src}/mvt/ios/modules/mixed/sms.py
  185. 0 { → src}/mvt/ios/modules/mixed/sms_attachments.py
  186. 0 { → src}/mvt/ios/modules/mixed/tcc.py
  187. +4 −2 { → src}/mvt/ios/modules/mixed/webkit_resource_load_statistics.py
  188. +2 −2 { → src}/mvt/ios/modules/mixed/webkit_session_resource_log.py
  189. +1 −1 { → src}/mvt/ios/modules/mixed/whatsapp.py
  190. +68 −42 { → src}/mvt/ios/modules/net_base.py
  191. 0 { → src}/mvt/ios/versions.py
  192. +9 −0 test-requirements.txt
  193. +56 −0 tests/android/test_artifact_dumpsys_adb.py
  194. +17 −1 tests/android/test_artifact_dumpsys_appops.py
  195. +40 −0 tests/android/test_artifact_dumpsys_platform_compat.py
  196. +67 −0 tests/android/test_artifact_tombstones.py
  197. +27 −0 tests/android_androidqf/test_dumpsys_adbstate.py
  198. +23 −0 tests/android_androidqf/test_dumpsys_platform_compat.py
  199. +6 −1 tests/android_androidqf/test_dumpsysappops.py
  200. +25 −0 tests/android_androidqf/test_files.py
  201. +133 −0 tests/android_androidqf/test_packages.py
  202. +6 −1 tests/android_bugreport/test_bugreport.py
  203. +18 −1 tests/artifacts/android_data/bugreport/dumpstate.txt
  204. BIN tests/artifacts/android_data/dumpsys_adb.txt
  205. +16 −0 tests/artifacts/android_data/dumpsys_adb_xml.txt
  206. +16 −0 tests/artifacts/android_data/dumpsys_platform_compat.txt
  207. BIN tests/artifacts/android_data/tombstone_process.pb
  208. +987 −0 tests/artifacts/android_data/tombstone_process.txt
  209. +30 −0 tests/artifacts/androidqf/dumpsys.txt
  210. +1 −0 tests/artifacts/androidqf/files.json
  211. +233 −0 tests/artifacts/androidqf/packages.json
  212. +40 −0 tests/artifacts/generate_stix.py
  213. +1,147 −0 tests/artifacts/stix2/638cd3ee5e5f019f84f9e0ea.json
  214. +8,248 −0 tests/artifacts/stix2/cytrox.stix2
  215. +76 −8 tests/common/test_indicators.py
  216. +4 −2 tests/common/test_utils.py
  217. +32 −0 tests/conftest.py
  218. +4 −0 tests/test_check_android_androidqf.py
  219. +4 −0 tests/test_check_android_backup.py
11 changes: 0 additions & 11 deletions .github/workflows/black.yml

This file was deleted.

23 changes: 23 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Mypy
on: workflow_dispatch

jobs:
mypy_py3:
name: Mypy check
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.9
cache: 'pip'
- name: Checkout
uses: actions/checkout@master
- name: Install Dependencies
run: |
pip install mypy
- name: mypy
run: |
make mypy
61 changes: 61 additions & 0 deletions .github/workflows/publish-release-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#
name: Create and publish a Docker image

# Configures this workflow to run every time a release is published.
on:
workflow_dispatch:
release:
types: [published]

# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
attestations: write
id-token: write
#
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

52 changes: 0 additions & 52 deletions .github/workflows/python-package.yml

This file was deleted.

12 changes: 10 additions & 2 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -4,16 +4,24 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
ruff_py3:
name: Ruff syntax check
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.9
cache: 'pip'
- name: Checkout
uses: actions/checkout@master
- name: Install Dependencies
run: |
pip install --user ruff
pip install ruff
- name: ruff
run: |
ruff check --output-format github .
make ruff
2 changes: 1 addition & 1 deletion .github/workflows/scripts/update-ios-releases.py
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ def parse_latest_ios_versions(rss_feed_text):


def update_mvt(mvt_checkout_path, latest_ios_versions):
version_path = os.path.join(mvt_checkout_path, "mvt/ios/data/ios_versions.json")
version_path = os.path.join(mvt_checkout_path, "src/mvt/ios/data/ios_versions.json")
with open(version_path, "r") as version_file:
current_versions = json.load(version_file)

38 changes: 38 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
name: Run Python Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10'] # , '3.11']

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies
run: |
make install
make test-requirements
- name: Test with pytest
run: |
set -o pipefail
make test-ci | tee pytest-coverage.txt
- name: Pytest coverage comment
continue-on-error: true # Workflows running on a fork can't post comments
uses: MishaKav/pytest-coverage-comment@main
if: github.event_name == 'pull_request'
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
1 change: 1 addition & 0 deletions .github/workflows/update-ios-data.yml
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ jobs:
title: '[auto] Update iOS releases and versions'
commit-message: Add new iOS versions and build numbers
branch: auto/add-new-ios-releases
draft: true
body: |
This is an automated pull request to update the iOS releases and version numbers.
add-paths: |
2 changes: 2 additions & 0 deletions .safety-policy.yml
Original file line number Diff line number Diff line change
@@ -6,4 +6,6 @@
security: # configuration for the `safety check` command
ignore-vulnerabilities: # Here you can list multiple specific vulnerabilities you want to ignore (optionally for a time period)
67599: # Example vulnerability ID
reason: disputed, inapplicable
70612:
reason: disputed, inapplicable
62 changes: 54 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,65 @@
# Contributing
# Contributing to Mobile Verification Toolkit (MVT)

Thank you for your interest in contributing to Mobile Verification Toolkit (MVT)! Your help is very much appreciated.
We greatly appreciate contributions to MVT!

Your involvement, whether through identifying issues, improving functionality, or enhancing documentation, is very much appreciated. To ensure smooth collaboration and a welcoming environment, we've outlined some key guidelines for contributing below.

## Where to start
## Getting started

Starting to contribute to a somewhat complex project like MVT might seem intimidating. Unless you have specific ideas of new functionality you would like to submit, some good starting points are searching for `TODO:` and `FIXME:` comments throughout the code. Alternatively you can check if any GitHub issues existed marked with the ["help wanted"](https://github.com/mvt-project/mvt/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) tag.
Contributing to an open-source project like MVT might seem overwhelming at first, but we're here to support you!

Whether you're a technologist, a frontline human rights defender, a field researcher, or someone new to consensual spyware forensics, there are many ways to make meaningful contributions.

Here's how you can get started:

1. **Explore the codebase:**
- Browse the repository to get familar with MVT. Many MVT modules are simple in functionality and easy to understand.
- Look for `TODO:` or `FIXME:` comments in the code for areas that need attention.

2. **Check Github issues:**
- Look for issues tagged with ["help wanted"](https://github.com/mvt-project/mvt/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or ["good first issue"](https://github.com/mvt-project/mvt/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to find tasks that are beginner-friendly or where input from the community would be helpful.

3. **Ask for guidance:**

- If you're unsure where to start, feel free to open a [discussion](https://github.com/mvt-project/mvt/discussions) or comment on an issue.

## How to contribute:

1. **Report issues:**

- Found a bug? Please check existing issues to see if it's already reported. If not, open a new issue. Mobile operating systems and databases are constantly evolving, an new errors may appear spontaniously in new app versions.

**Please provide as much information as possible about the prodblem including: any error messages, steps to reproduce the problem, and any logs or screenshots that can help.**


2. **Suggest features:**
- If you have an idea for new functionality, create a feature request issue and describe your proposal.

3. **Submit code:**
- Fork the repository and create a new branch for your changes.
- Ensure your changes align with the code style guidelines (see below).
- Open a pull request (PR) with a clear description of your changes and link it to any relevant issues.

4. **Documentation contributions:**
- Improving documentation is just as valuable as contributing code! If you notice gaps or inaccuracies in the documentation, feel free to submit changes or suggest updates.

## Code style
Please follow these code style guidelines for consistency and readability:

- **Indentation**: use 4 spaces per tab.
- **Quotes**: Use double quotes (`"`) by default. Use single quotes (`'`) for nested strings instead of escaping (`\"`), or when using f-formatting.
- **Maximum line length**:
- Aim for lines no longer than 80 characters.
- Exceptions are allowed for long log lines or strings, which may extend up to 100 characters.
- Wrap lines that exceed 100 characters.

Follow [PEP 8 guidelines](https://peps.python.org/pep-0008/) for indentation and overall Python code style. All MVT code is automatically linted with [Ruff](https://github.com/astral-sh/ruff) before merging.

Please check your code before opening a pull request by running `make ruff`

When contributing code to

- **Indentation**: we use 4-spaces tabs.
## Community and support

- **Quotes**: we use double quotes (`"`) as a default. Single quotes (`'`) can be favored with nested strings instead of escaping (`\"`), or when using f-formatting.
We aim to create a supportive and collaborative environment for all contributors. If you run into any challenges, feel free to reach out through the discussions or issues section of the repository.

- **Maximum line length**: we strongly encourage to respect a 80 characters long lines and to follow [PEP8 indentation guidelines](https://peps.python.org/pep-0008/#indentation) when having to wrap. However, if breaking at 80 is not possible or is detrimental to the readability of the code, exceptions are tolerated. For example, long log lines, or long strings can be extended to 100 characters long. Please hard wrap anything beyond 100 characters.
Your contributions, big or small, help improve MVT and are always appreciated.
Loading