Документация: README.md, ARCHITECTURE.md, DEPLOY.md, .github/copilot-instructions.md
Deploy drhider / validate (push) Waiting to run
Deploy drhider / validate (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,120 @@
|
|||||||
|
# ⛔ ПРАВИЛА ДЛЯ COPILOT — ПРОЕКТ DRHIDER
|
||||||
|
|
||||||
|
Читать перед ЛЮБЫМ действием. Нарушение = зря потраченное время.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ ЗОЛОТОЕ ПРАВИЛО
|
||||||
|
|
||||||
|
**НИКОГДА НИЧЕГО НЕ ДЕЛАТЬ БЕЗ ЯВНОЙ КОМАНДЫ «ДЕЛАЙ».**
|
||||||
|
|
||||||
|
Любой вопрос, обсуждение, анализ — НЕ повод менять код.
|
||||||
|
Только после явного: «делай», «делайте», «давай», «приступай», «пушить», «commit», «push».
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ НЕ МЕНЯТЬ КОД БЕЗ «ДЕЛАЙ»
|
||||||
|
|
||||||
|
Даже если ошибка очевидна. Даже если «исправление в одну строку».
|
||||||
|
**Показать проблему → описать решение → ЖДАТЬ «делай».**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ НЕ СМОТРЕТЬ ЛОКАЛЬНЫЕ ПАПКИ CONTRACTS
|
||||||
|
|
||||||
|
Весь старый код drhider — ТОЛЬКО на ВМ (`5.172.178.213` через SSH).
|
||||||
|
**НЕ лезть** в `/home/naeel/nubes/contracts/`, `contracts-flask/` и т.д.
|
||||||
|
|
||||||
|
Единственное исключение: `/home/naeel/nubes/loadtest/` (образец managed Flask).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ НЕ СПЕШИТЬ
|
||||||
|
|
||||||
|
Сначала ВСЁ обдумать. Проверить. Перепроверить. Только потом отвечать.
|
||||||
|
Спешка → ошибки → проблемы.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ НЕ ПРЕДПОЛАГАТЬ, НЕ ДОГАДЫВАТЬСЯ
|
||||||
|
|
||||||
|
Есть сомнения — **ОСТАНОВИТЬСЯ И СПРОСИТЬ**.
|
||||||
|
Не надо самостоятельно «улучшать» рабочий код без прямого разрешения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔⛔⛔ КОМАНДЫ В ТЕРМИНАЛЕ — С ТАЙМАУТАМИ
|
||||||
|
|
||||||
|
- `curl`: `--max-time N`
|
||||||
|
- `ssh`: `-o ConnectTimeout=N`
|
||||||
|
- `grep` на больших файлах: `timeout N grep ...`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ ПОСЛЕ КАЖДОЙ ПРАВКИ
|
||||||
|
|
||||||
|
1. **Проверить синтаксис:**
|
||||||
|
```bash
|
||||||
|
python3 -c "import py_compile; py_compile.compile('файл.py', doraise=True)"
|
||||||
|
```
|
||||||
|
2. **Проверить импорт работает:**
|
||||||
|
```bash
|
||||||
|
cd /home/naeel/nubes/drhider && python3 -c "from drhider import obfuscate_files, LLMClient"
|
||||||
|
```
|
||||||
|
3. **Проверить соседние функции не затёрты** (после replace_string_in_file)
|
||||||
|
4. **СРАЗУ commit + push** с подробным сообщением
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ КОММИТИТЬ + ПУШИТЬ ПОСЛЕ КАЖДОЙ ПРАВКИ
|
||||||
|
|
||||||
|
Без исключений. Никаких «потом», никаких накоплений.
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "подробное описание что сделано"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ ДОКУМЕНТИРОВАТЬ ВСЁ В History/
|
||||||
|
|
||||||
|
Каждое изменение, план, ошибку — в `/home/naeel/nubes/drhider/History/`:
|
||||||
|
- Отдельный `.md` файл на каждую сессию/задачу
|
||||||
|
- Что планировалось → что сделано → в чём ошибся
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 ПРОЕКТ DRHIDER — КЛЮЧЕВЫЕ ФАКТЫ
|
||||||
|
|
||||||
|
| Факт | Значение |
|
||||||
|
|---|---|
|
||||||
|
| Платформа | **Штурвал** (Managed Flask на pythonk8s) |
|
||||||
|
| URL | https://drhider.pythonk8s.dev.nubes.ru/ |
|
||||||
|
| Репозиторий | https://gitea.services.ngcloud.ru/Nail/drhider |
|
||||||
|
| Точка входа | `site/app.py` → `app = create_app()` |
|
||||||
|
| БД | **НЕТ** (всё в памяти) |
|
||||||
|
| Dockerfile | **НЕТ** (Штурвал сам запускает Flask) |
|
||||||
|
| gunicorn | **НЕТ** (Штурвал сам) |
|
||||||
|
| Зависимости | `flask`, `python-docx`, `pdfplumber`, `httpx` |
|
||||||
|
| ВМ (legacy) | `5.172.178.213`, ssh-ключ `~/.ssh/naeel_vm_id_ed25519` |
|
||||||
|
|
||||||
|
### Переменные окружения
|
||||||
|
|
||||||
|
| Переменная | Значение |
|
||||||
|
|---|---|
|
||||||
|
| `LLM_API_KEY` | `sk-ucI5YvOticoOQ9Kuj5K9mQ` |
|
||||||
|
| `LLM_URL` | `https://api.aillm.ru/v1/chat/completions` |
|
||||||
|
| `LLM_MODEL` | `gpt-oss-120b` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔ ЗАПРЕЩЕНО
|
||||||
|
|
||||||
|
1. Менять код без «делай»
|
||||||
|
2. Смотреть локальные папки contracts/
|
||||||
|
3. Действовать по догадкам («я бы сделал так», «можно попробовать»)
|
||||||
|
4. Удалять файлы/папки без разрешения
|
||||||
|
5. Использовать sed
|
||||||
|
6. Откладывать commit/push «на потом»
|
||||||
|
7. Игнорировать проверку синтаксиса перед push
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
# Архитектура DrHider
|
||||||
|
|
||||||
|
## Общая схема
|
||||||
|
|
||||||
|
```
|
||||||
|
Пользователь (браузер)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
https://drhider.pythonk8s.dev.nubes.ru/
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Штурвал (Managed Flask на pythonk8s)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
site/app.py ──→ Flask (create_app)
|
||||||
|
│
|
||||||
|
├── GET / → main_bp → templates/index.html
|
||||||
|
├── GET /health → health_bp → {"ok": true}
|
||||||
|
└── POST /api/drhider → api_bp → drhider.obfuscate_files()
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
TwoPassObfuscator
|
||||||
|
│
|
||||||
|
┌────────────┼────────────┐
|
||||||
|
│ │ │
|
||||||
|
extractor scanner replacer
|
||||||
|
(текст) (regex+LLM) (замена)
|
||||||
|
│ │ │
|
||||||
|
└────────────┼────────────┘
|
||||||
|
│
|
||||||
|
builder
|
||||||
|
(ZIP+CSV)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
application/zip
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Двухпроходная обфускация
|
||||||
|
|
||||||
|
### Проход 1: сбор сущностей
|
||||||
|
|
||||||
|
```
|
||||||
|
Файлы (.docx, .pdf, .txt, .zip)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
extractor.expand_zips() ← распаковать ZIP
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
extractor.convert_pdfs_to_docx() ← PDF → DOCX
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
extractor.extract_text() ← извлечь текст из каждого файла
|
||||||
|
│
|
||||||
|
├──→ scanner.scan_regex() ← regex: телефоны, email, ИНН, ОГРН, КПП,
|
||||||
|
│ БИК, счета, паспорта, компании, ФИО
|
||||||
|
│
|
||||||
|
└──→ scanner.scan_llm_ner() ← LLM: имена, адреса, паспорта
|
||||||
|
(только если llm_client передан)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
mapping = {оригинал → фиктивное} ← глобальный словарь замен
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проход 2: замена
|
||||||
|
|
||||||
|
```
|
||||||
|
mapping + sorted_keys (по убыванию длины)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Для каждого файла:
|
||||||
|
│
|
||||||
|
├── .docx → replacer.replace_in_docx() ← склеить runs → заменить → сохранить
|
||||||
|
├── .doc → без изменений (бинарный)
|
||||||
|
└── .txt/.pdf → replacer.replace_in_text() ← простая строковая замена
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
builder.build_mapping_csv() ← mapping.csv
|
||||||
|
builder.build_zip() ← ZIP со всеми файлами + mapping.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Модули
|
||||||
|
|
||||||
|
### `drhider/config.py`
|
||||||
|
Константы уровня всего пакета. НЕ содержит логики — только данные.
|
||||||
|
- `ENTITY_PATTERNS` — 10 regex-паттернов
|
||||||
|
- `COMPANY_PATTERN`, `PERSON_PATTERN` — скомпилированные regex
|
||||||
|
- Словари: `RU_SURNAMES`, `RU_NAMES`, `RU_PATRONYMICS`, `RU_CITIES`, `RU_STREETS`, `FAKE_DOMAINS`
|
||||||
|
|
||||||
|
### `drhider/checksum.py`
|
||||||
|
Чистые функции для расчёта контрольных сумм. Без побочных эффектов.
|
||||||
|
- `checksum_inn10(inn)` → 1 цифра
|
||||||
|
- `checksum_inn12(inn)` → 2 цифры
|
||||||
|
- `checksum_ogrn(ogrn)` → 1 цифра
|
||||||
|
|
||||||
|
### `drhider/random_utils.py`
|
||||||
|
Утилиты генерации случайных строк.
|
||||||
|
- `random_digits(n)` → строка из n цифр
|
||||||
|
- `random_letters(n)` → строка из n строчных латинских букв
|
||||||
|
|
||||||
|
### `drhider/generators/`
|
||||||
|
Каждый генератор — независимая функция с сигнатурой `generate_xxx(original: str) -> str`.
|
||||||
|
Параметр `original` нужен для извлечения префикса (например, «ИНН » из «ИНН 1234567890»).
|
||||||
|
Генераторы НЕ общаются друг с другом — только импортируют из `config`, `checksum`, `random_utils`.
|
||||||
|
|
||||||
|
`__init__.py` содержит `ENTITY_GENERATORS` — словарь, связывающий `entity_type` из `ENTITY_PATTERNS` с функцией-генератором. Используется в `scanner.scan_regex()`.
|
||||||
|
|
||||||
|
### `drhider/llm_client.py`
|
||||||
|
HTTP-клиент к OpenAI-совместимому API. Читает переменные окружения.
|
||||||
|
Интерфейс: `.complete(prompt: str) -> str`.
|
||||||
|
|
||||||
|
### `drhider/extractor.py`
|
||||||
|
Три независимые функции (не класс):
|
||||||
|
- `extract_text(fname, content, ctype)` → `(text, docx_document_or_None)`
|
||||||
|
- `expand_zips(files)` → распакованный список
|
||||||
|
- `convert_pdfs_to_docx(files)` → PDF заменены на DOCX
|
||||||
|
|
||||||
|
### `drhider/scanner.py`
|
||||||
|
Две функции (не класс), мутируют переданный `mapping: Dict[str, str]`:
|
||||||
|
- `scan_regex(text, mapping)` — regex-поиск
|
||||||
|
- `scan_llm_ner(all_texts, mapping, llm_client)` — LLM NER
|
||||||
|
|
||||||
|
### `drhider/replacer.py`
|
||||||
|
Три чистые функции:
|
||||||
|
- `apply_replacements(text, mapping, sorted_keys)` → строка с заменами
|
||||||
|
- `replace_in_docx(doc, mapping, sorted_keys)` → bytes (изменённый DOCX)
|
||||||
|
- `replace_in_text(text, fname, mapping, sorted_keys)` → bytes
|
||||||
|
|
||||||
|
### `drhider/builder.py`
|
||||||
|
Две функции сборки результата:
|
||||||
|
- `build_zip(files, mapping_csv)` → bytes (ZIP-архив)
|
||||||
|
- `build_mapping_csv(mapping)` → str (CSV)
|
||||||
|
|
||||||
|
### `drhider/obfuscator.py`
|
||||||
|
`TwoPassObfuscator` — оркестратор. Единственный класс, который знает о顺序е вызовов.
|
||||||
|
Метод `.obfuscate(files)` вызывает модули в правильном порядке.
|
||||||
|
`obfuscate_files()` — удобная функция-обёртка.
|
||||||
|
|
||||||
|
### `site/app.py`
|
||||||
|
Точка входа для Штурвала. `create_app()` создаёт Flask, регистрирует blueprint'ы.
|
||||||
|
`app = create_app()` — глобальный экземпляр.
|
||||||
|
|
||||||
|
### `site/routes/`
|
||||||
|
Blueprint'ы — каждый в своём файле:
|
||||||
|
- `main_bp` — только `GET /`
|
||||||
|
- `health_bp` — только `GET /health`
|
||||||
|
- `api_bp` — только `POST /api/drhider`
|
||||||
|
|
||||||
|
`__init__.py` содержит `register_routes(app)` — единая точка регистрации.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Поток данных
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP POST /api/drhider (multipart/form-data)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
api_bp.drhider()
|
||||||
|
│ request.files.getlist("files")
|
||||||
|
│ → [(filename, bytes, mimetype), ...]
|
||||||
|
▼
|
||||||
|
obfuscate_files(files, llm_client=LLMClient())
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
TwoPassObfuscator.obfuscate(files)
|
||||||
|
│
|
||||||
|
├── expand_zips → convert_pdfs_to_docx → extract_text
|
||||||
|
├── scan_regex + scan_llm_ner → mapping
|
||||||
|
├── replace_in_docx / replace_in_text → обфусцированные файлы
|
||||||
|
└── build_mapping_csv + build_zip → (zip_bytes, csv_str)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
send_file(BytesIO(zip_data), mimetype="application/zip")
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
HTTP 200 + ZIP-архив
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Принципы
|
||||||
|
|
||||||
|
1. **Модули — чистые функции.** Где возможно — без классов, без состояния.
|
||||||
|
2. **Единственный класс — TwoPassObfuscator.** Только он управляет состоянием (mapping, sorted_keys).
|
||||||
|
3. **Генераторы — изолированы.** Каждый в своём файле, не зависят друг от друга.
|
||||||
|
4. **Flask — тонкая прослойка.** Только принимает запрос, вызывает `obfuscate_files()`, отдаёт ответ.
|
||||||
|
5. **Без БД.** Всё в памяти. Никаких внешних зависимостей кроме LLM API.
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# Деплой, ВМ и переменные окружения
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Платформа
|
||||||
|
|
||||||
|
**Штурвал** — Managed Flask на pythonk8s (Kubernetes).
|
||||||
|
|
||||||
|
- Запускает Flask **сам** — без Dockerfile, без gunicorn
|
||||||
|
- Точка входа: `site/app.py` → глобальная переменная `app`
|
||||||
|
- Деплой: `git push origin master` → авто-деплой через UI Штурвала
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Деплой
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/naeel/nubes/drhider
|
||||||
|
git add -A
|
||||||
|
git commit -m "описание изменений"
|
||||||
|
git push origin master
|
||||||
|
```
|
||||||
|
|
||||||
|
После пуша — зайти в UI Штурвала и нажать ** Redeploy** для `drhider`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Переменные окружения (настраиваются в UI Штурвала)
|
||||||
|
|
||||||
|
| Переменная | Значение | Для чего |
|
||||||
|
|---|---|---|
|
||||||
|
| `LLM_API_KEY` | `sk-ucI5YvOticoOQ9Kuj5K9mQ` | Ключ доступа к LLM API |
|
||||||
|
| `LLM_URL` | `https://api.aillm.ru/v1/chat/completions` | URL LLM API |
|
||||||
|
| `LLM_MODEL` | `gpt-oss-120b` | Модель для NER |
|
||||||
|
|
||||||
|
Без `LLM_API_KEY` LLM-сканирование не работает (только regex).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ВМ contracts (legacy)
|
||||||
|
|
||||||
|
Старый сервер contracts.kube5s.ru. DrHider там — standalone HTTP-сервер на порту 8767.
|
||||||
|
|
||||||
|
### SSH
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213
|
||||||
|
```
|
||||||
|
|
||||||
|
### Где лежит старый drhider
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/naeel/contracts/drhider/
|
||||||
|
├── drhider.py # Ядро обфускации (отсюда скопировано в этот проект)
|
||||||
|
├── drhider_server.py # HTTP-сервер :8767 (НЕ ИСПОЛЬЗУЕТСЯ в новом проекте)
|
||||||
|
└── __init__.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сервисы на ВМ
|
||||||
|
|
||||||
|
| Сервис | Порт | systemd unit |
|
||||||
|
|---|---|---|
|
||||||
|
| drhider | 8767 | `contracts-drhider` |
|
||||||
|
| convert_server | 8766 | `contracts` |
|
||||||
|
| Flask UI | 5001 | — (start.sh) |
|
||||||
|
| nginx | 443 | `nginx` |
|
||||||
|
| PostgreSQL | 5432 | — |
|
||||||
|
|
||||||
|
### Управление drhider на ВМ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Статус
|
||||||
|
systemctl status contracts-drhider
|
||||||
|
|
||||||
|
# Логи
|
||||||
|
journalctl -u contracts-drhider -f
|
||||||
|
|
||||||
|
# Перезапуск
|
||||||
|
sudo systemctl restart contracts-drhider
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Локальная разработка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/naeel/nubes/drhider
|
||||||
|
|
||||||
|
# Установка зависимостей
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Запуск Flask (dev-сервер)
|
||||||
|
cd site && python3 app.py
|
||||||
|
|
||||||
|
# Проверка синтаксиса ВСЕХ файлов
|
||||||
|
python3 -c "
|
||||||
|
import py_compile, os
|
||||||
|
for root, dirs, files in os.walk('.'):
|
||||||
|
for f in files:
|
||||||
|
if f.endswith('.py'):
|
||||||
|
path = os.path.join(root, f)
|
||||||
|
py_compile.compile(path, doraise=True)
|
||||||
|
print(f'OK: {path}')
|
||||||
|
"
|
||||||
|
|
||||||
|
# Проверка импорта
|
||||||
|
python3 -c "from drhider import obfuscate_files, LLMClient; print('OK')"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как править код
|
||||||
|
|
||||||
|
1. Понять что меняем → **описать план**
|
||||||
|
2. Получить **«делай»**
|
||||||
|
3. Внести изменения
|
||||||
|
4. `python3 -c "import py_compile; py_compile.compile('файл.py', doraise=True)"` — КАЖДЫЙ изменённый .py
|
||||||
|
5. `python3 -c "from drhider import obfuscate_files, LLMClient"` — проверка импорта
|
||||||
|
6. `git add -A && git commit -m "подробно что и зачем" && git push`
|
||||||
|
7. Записать в `History/` что сделано
|
||||||
|
8. Зайти в UI Штурвала → Redeploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Как добавить новый генератор
|
||||||
|
|
||||||
|
1. Создать `drhider/generators/новый_тип.py` с функцией `generate_xxx(original: str) -> str`
|
||||||
|
2. Добавить regex-паттерн в `drhider/config.py` → `ENTITY_PATTERNS`
|
||||||
|
3. Добавить импорт и маппинг в `drhider/generators/__init__.py` → `ENTITY_GENERATORS`
|
||||||
|
4. Проверить синтаксис → commit → push → Redeploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## История версий
|
||||||
|
|
||||||
|
| Версия | Дата | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| 2.0.0 | 2026-07-12 | Модульный рефакторинг (22 модуля вместо монолита) |
|
||||||
|
| 1.0.0 | 2026-07-11 | Начальная версия (monolith drhider.py + Flask) |
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
# DrHider — обфускация документов (Managed Flask)
|
||||||
|
|
||||||
|
**URL:** https://drhider.pythonk8s.dev.nubes.ru/
|
||||||
|
**Репозиторий:** https://gitea.services.ngcloud.ru/Nail/drhider
|
||||||
|
**Платформа:** Штурвал (Managed Flask на pythonk8s)
|
||||||
|
**Дизайн фронтенда:** `/home/naeel/nubes/design/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что делает
|
||||||
|
|
||||||
|
Загружаешь документы (.docx, .pdf, .txt, .zip) — получаешь ZIP с обфусцированными копиями.
|
||||||
|
Все персональные данные, реквизиты, телефоны, email заменяются на фиктивные.
|
||||||
|
В архиве — mapping.csv с таблицей замен (оригинал → фиктивное).
|
||||||
|
|
||||||
|
Обфускация двухпроходная:
|
||||||
|
1. **Проход 1 (сбор):** regex + LLM находят все сущности во всех файлах → глобальный словарь замен
|
||||||
|
2. **Проход 2 (замена):** применяем замены ко всем файлам → ZIP
|
||||||
|
|
||||||
|
Согласованность: одна и та же сущность во всех файлах → одно и то же фиктивное значение.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⛔ ПРАВИЛА ДЛЯ АГЕНТОВ (читать перед ЛЮБЫМ действием)
|
||||||
|
|
||||||
|
### 1. НЕ смотреть локальные папки contracts/contracts-flask
|
||||||
|
Весь старый код drhider — ТОЛЬКО на ВМ (`5.172.178.213`). НЕ локально.
|
||||||
|
Единственное исключение: `/home/naeel/nubes/loadtest/` (образец структуры managed Flask).
|
||||||
|
|
||||||
|
### 2. Штурвал запускает Flask САМ
|
||||||
|
- БЕЗ Dockerfile (удалён)
|
||||||
|
- БЕЗ gunicorn (нет в requirements.txt)
|
||||||
|
- Точка входа: `site/app.py` → `app = create_app()`
|
||||||
|
- Деплой: `git push origin master` → авто-деплой в UI Штурвала
|
||||||
|
|
||||||
|
### 3. НЕ менять код без «делай»
|
||||||
|
Даже если ошибка очевидна. Показать → описать → ЖДАТЬ.
|
||||||
|
|
||||||
|
### 4. После ЛЮБОЙ правки:
|
||||||
|
```bash
|
||||||
|
python3 -c "import py_compile; py_compile.compile('файл.py', doraise=True)"
|
||||||
|
git add -A && git commit -m "подробное описание" && git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Документировать ВСЁ в History/
|
||||||
|
Каждое изменение, план, ошибку — в отдельный .md файл.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
### `drhider/` — пакет ядра обфускации
|
||||||
|
|
||||||
|
| Файл | Назначение | Строк |
|
||||||
|
|---|---|---|
|
||||||
|
| `__init__.py` | re-export: `obfuscate_files`, `LLMClient`, `TwoPassObfuscator` | 10 |
|
||||||
|
| `config.py` | Константы: regex-паттерны (ENTITY_PATTERNS, COMPANY_PATTERN, PERSON_PATTERN), словари имён/городов/улиц | 80 |
|
||||||
|
| `checksum.py` | Контрольные суммы: ИНН10, ИНН12, ОГРН | 60 |
|
||||||
|
| `random_utils.py` | Утилиты: `random_digits`, `random_letters` | 20 |
|
||||||
|
| `llm_client.py` | LLMClient — HTTP-клиент к LLM API (aillm.ru) | 50 |
|
||||||
|
| `extractor.py` | Извлечение текста из .docx/.pdf/.txt + `expand_zips` + `convert_pdfs_to_docx` | 180 |
|
||||||
|
| `scanner.py` | Проход 1: `scan_regex` (regex) + `scan_llm_ner` (LLM NER) | 110 |
|
||||||
|
| `replacer.py` | Проход 2: `apply_replacements`, `replace_in_docx`, `replace_in_text` | 100 |
|
||||||
|
| `builder.py` | Сборка: `build_zip`, `build_mapping_csv` | 65 |
|
||||||
|
| `obfuscator.py` | `TwoPassObfuscator` — оркестратор + `obfuscate_files()` | 100 |
|
||||||
|
|
||||||
|
### `drhider/generators/` — генераторы фиктивных значений
|
||||||
|
|
||||||
|
Каждый файл — один генератор (10–40 строк). Интерфейс: `generate_xxx(original: str) -> str`.
|
||||||
|
|
||||||
|
| Файл | Что генерирует |
|
||||||
|
|---|---|
|
||||||
|
| `phone.py` | +7 (XXX) XXX-XX-XX |
|
||||||
|
| `email.py` | user@fake-domain |
|
||||||
|
| `inn.py` | ИНН 10 и 12 знаков (с контрольной суммой) |
|
||||||
|
| `ogrn.py` | ОГРН 13 знаков |
|
||||||
|
| `kpp.py` | КПП 9 знаков |
|
||||||
|
| `bik.py` | БИК 9 знаков (04XXXXXXX) |
|
||||||
|
| `accounts.py` | Расчётный счёт (40702...) + корр. счёт (30101...) |
|
||||||
|
| `passport.py` | Паспорт (XX XX XXXXXX) |
|
||||||
|
| `company.py` | ООО/ЗАО/АО «СлучайноеНазвание» |
|
||||||
|
| `person.py` | Фамилия И.О. |
|
||||||
|
| `address.py` | Город, ул. Улица, д. N |
|
||||||
|
|
||||||
|
`__init__.py` содержит `ENTITY_GENERATORS` — маппинг `entity_type → генератор`.
|
||||||
|
|
||||||
|
### `site/` — Flask-приложение
|
||||||
|
|
||||||
|
| Файл | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `app.py` | `create_app()` — создание Flask, регистрация blueprint'ов, VERSION |
|
||||||
|
| `routes/__init__.py` | `register_routes(app)` |
|
||||||
|
| `routes/main_bp.py` | `GET /` — HTML-интерфейс |
|
||||||
|
| `routes/health_bp.py` | `GET /health` — `{"ok": true, "version": "..."}` |
|
||||||
|
| `routes/api_bp.py` | `POST /api/drhider` — multipart files → ZIP |
|
||||||
|
| `templates/index.html` | UI (выбор файлов, прогресс, скачивание) |
|
||||||
|
| `static/favicon.svg` | Иконка |
|
||||||
|
|
||||||
|
### Корневые файлы
|
||||||
|
|
||||||
|
| Файл | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `requirements.txt` | `flask`, `python-docx`, `pdfplumber`, `httpx` |
|
||||||
|
| `.gitignore` | `__pycache__/`, `*.pyc`, `.env` |
|
||||||
|
| `.gitea/workflows/deploy.yaml` | CI: валидация Python |
|
||||||
|
| `History/` | Документация всех изменений |
|
||||||
|
| `README.md` | Этот файл |
|
||||||
|
| `ARCHITECTURE.md` | Архитектура |
|
||||||
|
| `DEPLOY.md` | Деплой, ВМ, переменные окружения |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Метод | Путь | Что делает |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/` | HTML-интерфейс |
|
||||||
|
| `GET` | `/health` | `{"ok": true, "version": "2.0.0"}` |
|
||||||
|
| `POST` | `/api/drhider` | multipart/form-data (поле `files`) → `application/zip` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Переменные окружения (Штурвал)
|
||||||
|
|
||||||
|
| Переменная | Значение |
|
||||||
|
|---|---|
|
||||||
|
| `LLM_API_KEY` | `sk-ucI5YvOticoOQ9Kuj5K9mQ` |
|
||||||
|
| `LLM_URL` | `https://api.aillm.ru/v1/chat/completions` |
|
||||||
|
| `LLM_MODEL` | `gpt-oss-120b` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Связь с ВМ contracts (legacy)
|
||||||
|
|
||||||
|
На ВМ `5.172.178.213` (contracts.kube5s.ru) есть старая версия drhider:
|
||||||
|
- `drhider_server.py` на порту 8767 — standalone HTTP-сервер
|
||||||
|
- `drhider.py` — та же логика, что и в этом проекте (скопирована оттуда)
|
||||||
|
|
||||||
|
Этот проект (`drhider.pythonk8s.dev.nubes.ru`) — **независимый managed Flask**, заменяет ВМ-версию.
|
||||||
|
|
||||||
|
SSH к ВМ: `ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213`
|
||||||
Reference in New Issue
Block a user