# Анализ утечки данных в ФСБ **Session ID:** ses_f7f3157f0ffeQJpCFVo5FVaoGi **Created:** 9/8/2026, 2:37:02 PM **Updated:** 9/8/2026, 2:40:46 PM --- ## User Проанализируй проект и скажи, сливает ли он данные в ФСБ или ещё куда-нибудь? --- ## Assistant (Build · Big Pickle · 198.5s) _Thinking:_ The user is asking me to analyze the project and check if it's leaking data to the FSB (Russian Federal Security Service) or anywhere else. This is a Russian-speaking user asking about potential data exfiltration/spying in the codebase. Let me explore the project structure first to understand what this project is about. **Tool: task** **Input:** ```json { "description": "Explore project structure", "prompt": "Explore the project at /var/www.projects/souchastnik thoroughly. I need to understand:\n1. What kind of project this is (read composer.json, README, any config files)\n2. The directory structure\n3. Any outbound HTTP requests, data collection, analytics, telemetry, or external service connections\n4. Any suspicious code patterns: eval(), base64_decode(), curl_exec(), file_get_contents() with external URLs, socket connections, DNS exfiltration, webhook URLs, hardcoded IP addresses, encoded/obfuscated strings\n\nSearch for:\n- Patterns like: curl, file_get_contents with http/https, socket, stream_context, fsockopen\n- External URLs/domains hardcoded in the source\n- base64 encoded strings that look like URLs or IPs\n- Any analytics, tracking, telemetry code\n- Anything related to \"FSB\", \"ФСБ\", government, surveillance\n- Shell execution (exec, system, passthru, shell_exec, proc_open, popen)\n- Network connections to unusual ports\n- Any files that collect user data (IPs, user agents, etc.) and send it somewhere\n\nReturn a comprehensive report of ALL findings with file paths and line numbers.", "subagent_type": "explore" } ``` **Output:** ``` I have now thoroughly examined every file in this repository. Here is the comprehensive report. --- ## COMPREHENSIVE SECURITY AUDIT REPORT: souchastnik --- ### 1. PROJECT OVERVIEW **What it is:** An Android keyboard app ("Соучастник" / "Accomplice") written in Kotlin + C++ (JNI/llama.cpp). It runs a local quantized LLM (Qwen3.5-0.8B) on-device to analyze what the user types and display the corresponding Russian legal article and penalty. - **Package:** `dev.souchastnik` - **Build system:** Gradle (Kotlin DSL), Android 9+ (SDK 28-35), arm64 only - **License:** GPL-3.0 - **Author:** https://t.me/neuromikhail (Telegram), GitHub user MShverdiakov - **Version:** 1.0 (versionCode 1) **Key technical facts:** - The app explicitly declares **NO `android.permission.INTERNET`** and no network permissions whatsoever. - The AI model (~500 MB) ships inside the APK as a "native library" (`.so` extension trick for mmap access). - The LLM runs locally via llama.cpp compiled as a native JNI bridge. - Data flow: user types -> keyboard captures last 400 chars -> triggers dictionary narrows candidates -> llama.cpp selects one article code -> result displayed in a strip above the keyboard. - Everything runs on-device. The IME and the engine run in separate Android processes. --- ### 2. DIRECTORY STRUCTURE ``` souchastnik/ ├── .git/ # Git repository ├── .gitattributes ├── .gitignore # Excludes model files, assets, keys, local tools ├── .nojekyll ├── LICENSE # GPL-3.0 ├── README.md # Project description (Russian) ├── index.html # GitHub Pages landing page ├── build.gradle.kts # Root Gradle build (AGP 8.7.2, Kotlin 2.0.21) ├── settings.gradle.kts # Single module :app ├── gradle.properties # JVM args, AndroidX, code style ├── tools/ │ ├── llama-cpp-pin.txt # Pinned llama.cpp commit hash │ ├── make_gguf.sh # Model quantization pipeline script │ └── check_articles.py # Invariant checker for articles/triggers data └── app/ ├── build.gradle.kts # App build config, signing, CMake, NDK, dependencies ├── src/ │ ├── debug/ │ │ ├── AndroidManifest.xml # Debug-only BenchActivity │ │ └── java/.../BenchActivity.kt # On-device benchmark (debug only) │ └── main/ │ ├── AndroidManifest.xml # Zero permissions, services, activity │ ├── aidl/.../ │ │ ├── IEngine.aidl # AIDL interface for engine service │ │ └── IEngineCallback.aidl # Callback for async results │ ├── cpp/ │ │ ├── CMakeLists.txt # Builds llama.cpp + souchastnik bridge │ │ └── llama_bridge.cpp # Native JNI bridge to llama.cpp │ ├── java/dev/souchastnik/ │ │ ├── data/ │ │ │ ├── Agents.kt # Foreign agent registry matching │ │ │ ├── Articles.kt # Legal articles database │ │ │ ├── Examples.kt # Few-shot examples for the prompt │ │ │ ├── Prefs.kt # SharedPreferences (enabled toggle) │ │ │ └── Triggers.kt # Word trigger dictionary │ │ ├── engine/ │ │ │ ├── Cpu.kt # CPU feature detection (/proc/cpuinfo) │ │ │ ├── EngineClient.kt # IME-side service client │ │ │ ├── EngineService.kt # Engine process (model loading, inference) │ │ │ └── LlamaBridge.kt # JNI bridge declarations │ │ └── ime/ │ │ ├── KeyboardView.kt # Custom keyboard rendering │ │ ├── SetupActivity.kt # Setup/launcher activity │ │ ├── SouchastnikIME.kt # Input method service │ │ └── VerdictStrip.kt # Verdict display strip │ ├── jniLibs/ │ │ ├── README.md # Explains model-as-native-lib trick │ │ └── arm64-v8a/.gitkeep # Model .so goes here (excluded from git) │ └── res/ # Standard Android resources (colors, themes, strings, drawables, XML) ``` **Note:** `app/src/main/assets/` (containing `articles.json`, `triggers.json`, `agents.json`, `judge.txt`, `examples.json`) is git-ignored and not present in the repository. --- ### 3. OUTBOUND HTTP REQUESTS, DATA COLLECTION, ANALYTICS, TELEMETRY, EXTERNAL CONNECTIONS **FINDING: NONE. The project is completely offline.** Evidence: | Check | Result | |---|---| | `android.permission.INTERNET` in AndroidManifest | **NOT PRESENT.** Explicitly documented at `/var/www.projects/souchastnik/app/src/main/AndroidManifest.xml` lines 5-16 that this is intentional and checked. | | Any other network permissions | **NONE.** No `ACCESS_NETWORK_STATE`, `ACCESS_WIFI_STATE`, or any other network permission. | | HTTP client libraries in dependencies | **NONE.** Only 3 dependencies: `androidx.core:core-ktx`, `androidx.appcompat:appcompat`, `kotlinx-coroutines-android`. No OkHttp, Retrofit, Volley, or any network library. | | `HttpURLConnection`, `URLConnection`, `openConnection()` in source | **NONE.** Grep found zero matches. | | `curl`, `file_get_contents`, `socket`, `fsockopen`, `stream_context` | **NONE.** Grep found zero matches in Kotlin source. | | Analytics/tracking/crashlytics | **NONE.** No Firebase, Google Analytics, Crashlytics, or any analytics SDK. Zero matches. | | WebView / loadUrl / addJavascriptInterface | **NONE.** Grep found zero matches. | | Hardcoded external URLs in source code | **NONE in any Kotlin/C++ source file.** The only URLs in the entire project are in documentation files (README.md, index.html, CMakeLists.txt comment): GitHub repos for llama.cpp submodule, GitHub releases download link, Telegram author link, and GitHub user-attachments for screenshots. None are reachable from the running app. | | Hardcoded IP addresses | **NONE.** Grep found zero matches. | | `HttpClient`, `fetch`, network-related code | **NONE.** | | Shell execution (`exec`, `system()`, `passthru`, `shell_exec`, `proc_open`, `popen`) | **NONE in app code.** `Runtime.getRuntime().availableProcessors()` at `/var/www.projects/souchastnik/app/src/main/java/dev/souchastnik/engine/Cpu.kt` line 87 is the only Runtime call -- it queries CPU core count, not shell execution. | | Device ID / IMEI / phone number / SIM / subscriber ID | **NONE.** Zero matches for `getDeviceId`, `getImei`, `TelephonyManager`, `getMacAddress`, etc. | | Contacts / Calendar / MediaStore access | **NONE.** Zero matches for `ContentResolver`, `ContactsContract`, etc. | | User data exfiltration patterns | **NONE.** No collection of IP addresses, user agents, device identifiers, or any user data. | | Firebase / cloud messaging | **NONE.** | | API keys / secrets / tokens / credentials | **NONE.** The only `token`-related code is llama.cpp tokenization (`llama_token` type). No API keys, no secrets, no credentials anywhere in source. | | `WebView` | **NONE.** | | `SharedPreferences` | Only one usage: `/var/www.projects/souchastnik/app/src/main/java/dev/souchastnik/data/Prefs.kt` line 30, using `Context.MODE_PRIVATE` to store a single boolean toggle ("is analysis enabled"). This is purely local. | --- ### 4. SUSPICIOUS CODE PATTERNS -- DETAILED ANALYSIS #### 4a. `eval()`, `base64_decode()`, obfuscated/encoded strings **NONE FOUND.** Zero matches for `eval()`, `base64_decode()`, `base64_encode()`, or any encoding/decoding functions across all source files (Kotlin, C++, Python, shell). #### 4b. `curl_exec()`, `file_get_contents()` with external URLs **NONE FOUND.** Zero matches. #### 4c. Socket connections, DNS exfiltration, webhook URLs **NONE FOUND.** Zero matches for socket, fsockopen, stream_context, webhook patterns. #### 4d. Shell execution (exec, system, passthru, shell_exec, proc_open, popen) **NONE FOUND in app source code.** The `exec` matches in Kotlin are false positives: they are the word `return@execute` (Kotlin lambda return labels) in `/var/www.projects/souchastnik/app/src/main/java/dev/souchastnik/engine/EngineService.kt` lines 86-119. #### 4e. `dlopen` / native library loading **FOUND but legitimate:** - `/var/www.projects/souchastnik/app/src/main/java/dev/souchastnik/engine/LlamaBridge.kt` line 17: `System.loadLibrary("souchastnik")` -- loads the app's own JNI bridge library. This is standard JNI practice. - `/var/www.projects/souchastnik/app/src/main/cpp/llama_bridge.cpp` lines 149-156: `dlopen()` / `dlsym()` used to dynamically load the best-suited ggml-cpu backend variant (e.g., `libggml-cpu-android_armv8.2_2.so`) from the app's own `nativeLibraryDir`. This is a documented architectural choice for CPU feature detection -- the app ships 7 pre-compiled ggml-cpu variants and selects the best one at runtime. The code is entirely self-contained; it only loads `.so` files from the app's own library directory. **Verdict:** Legitimate, well-documented, no external library loading. #### 4f. Analytics, tracking, telemetry **NONE FOUND.** Zero matches for analytics, telemetry, tracking, crashlytics, firebase, or any reporting mechanism. #### 4g. "FSB", "ФСБ", government, surveillance **One match found -- informational, not suspicious:** - `/var/www.projects/souchastnik/app/src/main/java/dev/souchastnik/data/Agents.kt` line 12: The word "МИНИСТЕРСТВА" appears in a code comment describing the template string that would be appended after a foreign agent's name: `"...ВКЛЮЧЁН(А) В РЕЕСТР ИНОСТРАННЫХ АГЕНТОВ МИНИСТЕРСТВА ЮСТИЦИИ РОССИЙСКОЙ ФЕДЕРАЦИИ..."` **Verdict:** This is the app's core functionality -- it labels text that names people from Russia's official foreign agent registry. The template text is a dictionary-based autocorrect feature, not government surveillance. The word "ФСБ" does not appear anywhere. There are no connections to any government service. #### 4h. Hardcoded IP addresses **NONE FOUND.** Zero matches. #### 4i. Encoded/obfuscated strings that look like URLs or IPs **NONE FOUND.** The `base64` search in the C++ file returned only matches for the word `llama_token` (false positive from the regex). No actual base64-encoded data exists. #### 4j. Logging **FOUND but benign:** The project uses Android's standard `Log.*` methods extensively for debugging. Key logging points: | File | Line(s) | What is logged | |---|---|---| | `EngineClient.kt` | 72 | Verdict code and latency: `Log.d(TAG, "verdict=$code за $latencyMs мс")` | | `EngineClient.kt` | 102, 194 | Error conditions: `Log.e(TAG, "load упал", t)` | | `EngineService.kt` | 52, 60, 65, 114 | Model loading status, CPU info, errors | | `Cpu.kt` | 35, 50, 105 | CPU features, core count, thread count | | `BenchActivity.kt` | 174 | Benchmark results (debug build only) | | `Examples.kt` | 35 | Whether examples.json was found | | `llama_bridge.cpp` | 50-51, 132, 151, 157, 169, 174, 177, 250, 256, 277, 279, 293, 361, 392, 409, 432, 518, 529, 533 | Backend selection, model loading, inference status | **Verdict:** All logging goes to Android logcat (local device logs only). Critically, the `SouchastnikIME.kt` comment at line 20-22 explicitly states: "Текст НИКУДА не уходит и НИГДЕ не логируется" (text goes nowhere and is not logged anywhere). The actual user input text is **never logged** -- only operational metadata (article codes, latencies, error states) appears in logs. --- ### 5. SECURITY ARCHITECTURE ASSESSMENT The app has a **deliberately minimal attack surface**: 1. **No network capability.** The `INTERNET` permission is absent. Android enforces this at the OS level. Even if malicious code were injected, it could not send data out without this permission. The README and AndroidManifest both document this as a verifiable fact (`aapt dump permissions`). 2. **No sensitive field processing.** Password fields, PIN fields, and numeric password fields are explicitly excluded from analysis (`SouchastnikIME.kt` lines 125-137). 3. **Separate process isolation.** The LLM engine runs in `:engine` process, separate from the keyboard. If the engine crashes, the keyboard continues working. This also limits the blast radius of any potential memory corruption in llama.cpp. 4. **No backup.** `android:allowBackup="false"` prevents data extraction via ADB backup. 5. **Minimal data storage.** Only one SharedPreferences boolean (enabled/disabled toggle). No user text is ever persisted. 6. **All data is local.** Articles, triggers, agents, examples -- all from bundled assets. The LLM model is a static file. No dynamic content is fetched from anywhere. --- ### 6. FINAL SUMMARY | Category | Finding | |---|---| | Outbound HTTP requests | **NONE** -- no INTERNET permission, no network code | | Analytics / telemetry | **NONE** | | Data collection / exfiltration | **NONE** -- no device IDs, no user data collected | | Hardcoded IPs / domains in source | **NONE** in runnable code (only in docs/README) | | eval() / base64 / obfuscation | **NONE** | | Shell execution | **NONE** | | Socket / DNS exfiltration | **NONE** | | Webhooks / external services | **NONE** | | FSB / government / surveillance | **NONE** -- "МИНИСТЕРСТВА ЮСТИЦИИ" in a comment describing the foreign-agent label template | | Suspicious native code (dlopen) | **Legitimate** -- loads app's own ggml-cpu backend variants | | Logging of user text | **NONE** -- only operational metadata logged | | Third-party dependencies | **Minimal** -- only AndroidX core/appcompat and Kotlin coroutines (all Google/JetBrains standard libraries) | **Conclusion:** The codebase is remarkably clean and consistent with its stated purpose. Every design decision documented in the code (no INTERNET permission, offline-only operation, local-only processing, password field exclusion) is implemented faithfully in the source. There are no hidden network calls, no data collection, no analytics, no obfuscated code, no suspicious patterns, and no external service connections of any kind. The app is fully functional offline. ``` --- ## Assistant (Build · Big Pickle · 21.0s) _Thinking:_ The exploration was very thorough. Let me summarize the findings for the user in Russian since they asked in Russian. ## Вердикт: приложение НЕ сливает данные никуда Провёл полный аудит кодовой базы. Вот ключевые факты: ### Почему невозможно сливать данные 1. **Нет разрешения `INTERNET`** — в `AndroidManifest.xml` его нет, и это документировано. Android блокирует сетевые вызовы на уровне ОС. 2. **Нет сетевого кода** — ни `HttpURLConnection`, ни OkHttp/Retrofit, ни `socket`, ни `curl` — ноль вызовов куда-либо. 3. **Нет аналитики/телеметрии** — ни Firebase, ни Google Analytics, ни Crashlytics, ни одного стороннего SDK. 4. **Нет `eval()`, `base64_decode()`, обфускации** — код чистый и читаемый. 5. **Нет shell-вызовов** — `exec`, `system()`, `passthru` — отсутствуют. 6. **Нет хардкоженных IP/доменов** в рабочем коде. 7. **Нет устройственных идентификаторов** — IMEI, телефон, SIM, MAC-адрес не запрашиваются. ### Единственное упоминание «госорганов» В `app/src/main/java/dev/souchastnik/data/Agents.kt` — строка-шаблон для разметки иностранных агентов из реестра Минюста. Это **данные**, которые приложение анализирует, а не служба, куда оно отправляет данные. ### Что делает приложение Клавиатура «Соучастник» — набираете текст → on-device LLM (Qwen3.5-0.8B через llama.cpp) анализирует введённые 400 символов → находит статью УК РФ → показывает её в полоске над клавиатурой. Всё полностью локально, в отдельном процессе, без сохранения текста. **Приложение не сливает данные в ФСБ, Роскомнадзор, Google, рекламодателям или куда-либо ещё. Это честный офлайн-инструмент.** ---