Commit Graph

49 Commits

Author SHA1 Message Date
Marker689
682b340d7d fix: system prompt — защита от prompt injection (hard gate)
- constants.py: новый LLM_ANALYSIS_SYSTEM_PROMPT с защитой от injection
- Явное разделение: message = CLAIMS (untrusted), code = FACTS
- Инструкция игнорировать user message при конфликте с code/rule
- examples/test-prompt-variants.py: тест 3 вариантов промпта
- Результат: baseline=safe (обманут), V2/V3=malicious (устояли)
- examples/test-prompt-poisoning.py: 4 вектора атаки с оригинальным промптом
2026-05-10 15:34:22 +03:00
Marker689
c1258dde99 refactor: FastAPI best practices — return types, Pydantic schemas, middleware, code dedup
- Все 18 роутов получили return type annotations
- Создан schemas.py с Pydantic-моделями (ScanOut, PackageOut, FindingOut, ...)
- API-роуты: response_model на list/detail/export/stats
- 404 через HTTPException(404) вместо {'detail':'Not found'} (200)
- RequestLoggingMiddleware: method, path, status, duration_ms
- Глобальный exception handler: ловит необработанные исключения → 500
- _parse_flagged(): вынесен дублирующийся string→bool
- parse_package_path(): общий для web.py и api_packages.py
- selectinload: вынесены в top-level imports
- harvester: makedirs/mkdtemp/rmtree обёрнуты в asyncio.to_thread()
2026-05-10 12:53:33 +03:00
Marker689
935d96b35a fix: thead th — text-align:center явно 2026-05-10 12:09:16 +03:00
Marker689
6ca652db97 fix: thead th — явные font-size, weight, padding (убрана разница до/после сорта) 2026-05-10 12:06:24 +03:00
Marker689
3077f3faad fix: thead th — border:none, только border-bottom #202632 2026-05-10 12:03:21 +03:00
Marker689
d5cd47957e fix: thead — border-bottom вместо border-color, дефолтная сортировка по ID desc 2026-05-10 12:01:43 +03:00
Marker689
b2bd1fb336 fix: заголовки таблиц — убраны синие фон/рамка Pico (теперь всегда transparent + #202632) 2026-05-10 11:57:35 +03:00
Marker689
d046260e7f ui: единый стиль шапки таблиц — иконки сортировки всегда opacity:1 2026-05-10 11:50:03 +03:00
Marker689
dea9e0a6e4 a11y: Web Interface Guidelines — 19 правок доступности и семантики
- base.html: lang динамический, meta theme-color, skip-link, убран дубль Dashboard
- Фильтры: labels на search/select, type=search, placeholder с …, autocomplete=off
- llm-retry: <span> → <button> во всех шаблонах (_llm_report, scan, package)
- sortable th: tabindex=0, role=button через JS + keyboard handler
- sort-icon: aria-hidden=true
- style.css: color-scheme:dark, prefers-reduced-motion, tabular-nums, touch-action
- app.js: clipboard fallback для не-HTTPS
2026-05-10 11:45:41 +03:00
Marker689
32dc179f25 refactor: удалены внешние зависимости — pico.css и htmx.js теперь локально
Скачаны и сохранены в /static:
- pico.min.css v2 (83 KB)
- htmx.min.js v2.0.4 (51 KB)
Добавлены в .gitignore
2026-05-10 11:35:36 +03:00
Marker689
29b463a1b2 fix: замена несуществующих CSS-переменных на hex-цвета
35 var(--pico-color-*) → hex (#e04040, #4caf50, #f0c000, #2196f3, #202632, #2a3140...)
Все бордеры, фоны и цвета статусов теперь рендерятся корректно
2026-05-10 11:32:17 +03:00
Marker689
2c753748f0 ui: scan-info-block — фон, рамка, скругление (карточка вместо плоского текста) 2026-05-10 11:19:47 +03:00
Marker689
53bb095f83 fix: таблицы — overflow-x:auto только на мобилке (max-width:768px) 2026-05-10 11:13:49 +03:00
Marker689
1341404568 fix: аудит — 19 фиксов безопасности, надёжности, UI и 16 новых тестов
- S4: bump jinja2>=3.1.4, python-multipart>=0.0.18, httpx>=0.28.0
- S5: _detect_ecosystem — DEFAULT_ECOSYSTEM для неизвестных форматов
- S6: harvester — log.exception() вместо log.error()
- S8: _scan_component — urlencode параметров
- P1: scanner — proc.kill() при таймауте
- P3: api_packages — selectinload(Scan.findings), убран N+1
- P4+P5: утечка _url_locks и _llm_locks при early return
- P6: DB reaper — сброс {'status':'analyzing'} при старте
- UI: htmx-пагинация, фильтры не теряют flagged, 404 с layout
- UI: мобильные таблицы overflow-x, полная стата на дашборде
- UI: i18n статусов в _status_badge, urlencode package_name
- 16 новых тестов: analyze endpoint (6), scanner errors (4),
  webhook signature (2), llm client (4)
2026-05-10 10:45:44 +03:00
Marker689
6984844161 feat: LLM-анализ — индикатор прогресса, кнопка рескана, статистика на дашборде
- Добавлен статус {"status": "analyzing"} в finding.report на время LLM-анализа
- Кнопка рескана (Retry) под LLM-отчётом в ручном режиме
- LLM-статистика на дашборде: analysed / pending
- Защита от двойного анализа через per-finding asyncio.Lock
- _llm_spinner.html — фрагмент спиннера для состояния analysing
- Удалён мёртвый код: constants, i18n, CSS, queries
- Фиксы: _env_int, индексы БД, UnicodeDecodeError, time.mktime и др.
- Шаблоны: shared includes (_status_badge, _pagination)
- AGENTS.md: workflow (lint, test, commit, rebuild)
2026-05-10 09:54:04 +03:00
Marker689
c99a7bf67c fix: правильный парсинг initiator из вебхука Nexus
Nexus присылает initiator в формате 'username/IP' (напр. admin/172.21.0.3).
Теперь initiator и source_ip правильно разделяются из этого поля.
Лог Webhook payload выводит распарсенные значения.
2026-05-10 08:06:06 +03:00
Marker689
904e917f1f refactor: фаза 5 — чистка (APP_VERSION, LLM константы)
- constants.py: APP_VERSION, LLM_DEFAULT_TEMPERATURE, LLM_RESPONSE_FORMAT
- main.py: версия из APP_VERSION (вместо хардкод '0.1.0'×2)
- llm.py: temperature и response_format из constants

Всего: 85 тестов, ruff clean
2026-05-10 07:59:57 +03:00
Marker689
d11be24c5f fix(i18n): формальный русский перевод + t() во всех шаблонах
- i18n.py: формальный русский (сканирования, панель управления, и т.д.)
- Все шаблоны: замена хардкод-строк на t(key, request.state.lang)
- dashboard_stats_fragment: добавлен request в контекст
2026-05-10 07:52:58 +03:00
Marker689
d33411b719 feat: фаза 3 — i18n RU/EN, /metrics, AI disclaimer, initiator+IP, LLM очередь
3.3 i18n: модуль с RU/EN словарями, LangMiddleware (cookie+query),
     Jinja-фильтр t(), переключатель EN/RU в nav, перевод ключевых
     строк интерфейса
3.5 /metrics: Prometheus-совместимый endpoint (scans_total,
     scans_flagged, findings_total, by_status, by_ecosystem,
     last_scan_timestamp)
3.2 AI disclaimer: сноска под каждым LLM-вердиктом (.llm-disclaimer)
3.4 LLM очередь: asyncio.Semaphore(LLM_MAX_CONCURRENT_ANALYSES)
3.1 initiator + source_ip: поля в Scan, захват из webhook payload,
     показ в scan_detail + API
3.6 UI: убран stat-minibar и heatmap с дашборда
2026-05-10 07:37:22 +03:00
Marker689
4ae893a025 feat: фаза 3 (часть 1) — disclaimer, очередь, initiator + IP
3.6 UI: убрать stat-minibar и heatmap с дашборда
3.2 AI disclaimer под каждым LLM-вердиктом
3.4 LLM_MAX_CONCURRENT_ANALYSES + Semaphore в llm.py
3.1 Scan.initiator + source_ip, webhook захватывает, UI показывает
2026-05-10 07:32:14 +03:00
Marker689
6e6f45ce03 fix: фаза 2 — критические фиксы
READМЕ: убрать NEXUS_REPOSITORIES, CREATED→UPDATED, go/npm/Gem→go/npm,
  добавить MAX_CONCURRENT_SCANS, CSV-экспорт, инструкцию по вебхукам Nexus
Dockerfile: uv pip install --system . (единый источник deps — pyproject.toml)
docker-compose: WEBHOOK_SECRET, SCAN_TIMEOUT_SECONDS
pyproject.toml: убрать deprecated [tool.ruff].select
config.py: default из DEFAULT_MAX_CONCURRENT_SCANS
constants.py: убрать GUARDDOG_ERRORS_KEY (мёртвый), .gem из PACKAGE_EXTENSIONS,
  LLM prompt: «Python»→«software»
queries.py: убрать return_total
Makefile: docker-up +--build, docker-down без -v, +docker-destroy,
  +docker-rebuild, убран typecheck
2026-05-10 07:23:43 +03:00
Marker689
8726b65808 refactor: реструктуризация — core/, db/, routes/, web/
guarddog_nexus/
├── core/          scanner, harvester, nexus, llm
├── db/            engine, models, queries
├── routes/        webhooks, api_*, web
└── web/           templates + static

- 11 файлов перемещено (git mv — сохранена история)
- Все импорты обновлены (~15 файлов)
- main.py, tests — исправлены пути
- 50/50 тестов, ruff clean
2026-05-10 07:17:41 +03:00
Marker689
22dc87851a fix: Go-пакеты со слешами в имени ломали роутинг
Использован :path в FastAPI-роутах, имя+версия парсятся из URL.
Шаблоны urlencode-ят имена пакетов при генерации ссылок.
2026-05-10 06:41:00 +03:00
Marker689
6523f55dcd feat: поддержка Go и npm экосистем
- setup-nexus.sh: создание go-proxy (proxy.golang.org) и npm-proxy (registry.npmjs.org)
- nexus_client.py: extract_go_info() и extract_npm_info() для парсинга путей
  Go:  packages/github.com/gorilla/mux/@v/v1.8.0.zip → name=github.com/gorilla/mux ver=v1.8.0
  npm: packages/lodash/-/lodash-4.17.21.tgz → name=lodash ver=4.17.21
- nexus_client.py: EXTRACTORS dict + extract_package_info() универсальный extractor
- webhooks.py: _detect_ecosystem() — определяет экосистему из asset.format
- harvester.py: использует extract_package_info() вместо extract_pypi_info()
- Всё в Docker-контейнере, на хосте ничего не ставится
- GuardDog поддерживает go и npm из коробки
2026-05-10 06:29:34 +03:00
Marker689
646a50d01a fix(ui): добавить name="status" на select + hx-include крос-связку
- select status-filter: добавлен name="status" (htmx берёт name, не id)
- search input + status select: взаимные hx-include
  (поиск включает статус, статус включает поиск)
2026-05-10 06:17:25 +03:00
Marker689
ab60d1944d fix(ui): фильтр-бар больше не в htmx-фрагменте — не дублируется
Фильтр-бар вынесен из _scans_table.html / _packages_table.html
в scans_list.html / packages_list.html — вне htmx target.
htmx заменяет только таблицу, фильтр остаётся на месте.
2026-05-10 06:14:10 +03:00
Marker689
c4c27deb79 fix(ui): исправить раздвоение интерфейса при htmx-фильтрации
Проблема: htmx через hx-target="#scans-table-container" получал
полную HTML-страницу (с <html>, <nav>, <head>) и вставлял её внутрь
существующей страницы → дублировался header.

Решение: шаблоны разделены на полные + фрагменты:
  - _scans_table.html — только filter-bar + таблица + пагинация
  - _packages_table.html — аналогично
  - web/routes.py: проверка HX-Request хедера → отдаём фрагмент
2026-05-10 06:08:24 +03:00
Marker689
d23abe8b4b feat: лимит конкурентных сканов через asyncio.Semaphore
- config.py: MAX_CONCURRENT_SCANS (default=4)
- harvester.py: глобальный _scan_semaphore оборачивает scan_package()
  — при N одновременных сканах, (N+1)-й будет ждать освобождения слота
  — download и SHA256 не лимитируются, только guarddog subprocess
- docker-compose.yml, .env.example: переменная добавлена
2026-05-10 05:52:23 +03:00
Marker689
c4dcd79ecd fix: защита от дубликатов сканов — UPDATED-only + per-URL мьютекс
- constants.py: RELEVANT_WEBHOOK_ACTIONS теперь только UPDATED
  (CREATED игнорируется, Nexs proxy шлёт UPDATED при обновл кэша)
- harvester.py: asyncio.Lock на каждый download_url
  — при параллельных вебхуках только первый пройдёт, остальные skipped
  — lock проверяется + DB re-check внутри критической секции
- tests: обновлены фикстуры (CREATED→UPDATED), добавлен тест ignores_created
2026-05-10 05:47:35 +03:00
Marker689
1343c3f415 refactor(ui): блочный дизайн + LLM-отчёт с цветовой индикацией
dashboard:
- Убран: severity ratio bar, Top Rules Triggered, Most Flagged Packages
- Заменены 6 stat-карточек на компактную stat-minibar
- 2-колоночная сетка: heatmap + Latest Flagged (с красной рамкой)
- Таблицы сжаты (.compact)

findings (scan/package detail):
- Findings всегда раскрыты как блоки (.finding-block)
- Убран accordion (details/summary) и toggle-all кнопка
- Код в тёмном блоке, кнопка Copy сверху

LLM-отчёт:
- Цветовая индикация по вердикту:
  - safe       → зелёный badge, зелёная рамка, лёгкий зелёный фон
  - suspicious → жёлтый badge, жёлтая рамка, лёгкий жёлтый фон
  - malicious  → красный badge, красная рамка, лёгкий красный фон
- Бейдж с вердиктом + severity rating в хедере
- Summary курсивом, analysis с pre-line для абзацев

Скан-инфо:
- scan-info-block с grid-раскладкой (8 полей)
- SHA256 компактным шрифтом
- Блок ошибки скана отдельно выделен

CSS:
- Удалены: .stats-grid, .stat-card, .severity-bar*, .top-rules-chart*,
  .finding-card*, .finding-details, .finding-header-row, .finding-summary-hint
- Добавлены: .stat-minibar, .dashboard-grid, .dash-block, .scan-info-*,
  .finding-block, .llm-{safe,suspicious,malicious}, .llm-badge-*,
  table.compact
- app.js: удалён toggleFindings()
2026-05-10 05:24:43 +03:00
Marker689
a754f91abf fix: устойчивый парсинг LLM-ответа — обработка markdown code blocks
- llm.py:  возвращён (нужен для structured output)
- Добавлен fallback-парсинг для ответов обёрнутых в ```json
- Устранён undefined-variable баг в except-блоке
2026-05-10 05:14:38 +03:00
Marker689
0a22c2cce8 fix: LLM таймаут + debug-лог для диагностики 2026-05-10 05:01:31 +03:00
Marker689
3404df0f28 fix: авто-миграция SQLite — добавление недостающих колонок из моделей
При добавлении нового поля в модель (report в Finding) старое create_all
не добавляет колонку в существующую БД → 500 ошибка.
Теперь _migrate() проверяет PRAGMA table_info и добавляет
недостающие колонки через ALTER TABLE
2026-05-10 04:45:31 +03:00
Marker689
834138368a refactor: вынос хардкода + LLM-анализ finding'ов
## Часть A: Вынос хардкода
- Новый модуль constants.py — все magic strings, лимиты, severity, ключи
  (104 хардкод-значения централизованы)
- Новый модуль queries.py — общие SQL-запросы (build_scan_list_query,
  build_package_list_query, get_dashboard_stats)
  Убрана дупликация между api/*.py и web/routes.py (~90%)

- config.py: добавлены NLP_ENABLED, nexus_timeout, guarddog_binary,
  log_syslog_facility, LLM-переменные
- nexus_client.py: таймауты из конфига, SHA256_CHUNK_SIZE из constants
- scanner.py: error-ключи из constants, GUARDDOG_OUTPUT_FORMAT из constants
- webhooks.py: RELEVANT_WEBHOOK_ACTIONS, METADATA_PATTERNS, ignore-строки
  из constants
- logging_setup.py: конфигурируемый syslog facility, APP_PACKAGE из constants
- main.py: APP_NAME, APP_DESCRIPTION, APP_PACKAGE из constants
- models.py: поле report: JSON | None в Finding для LLM-отчётов
- harvester.py: авто-очистка tmpdir через finally; ERROR_MESSAGE_MAX_LENGTH
  из constants; PACKAGE_EXTENSIONS вместо SUPPORTED_EXTENSIONS (с .gem)
- api/*.py + web/routes.py: используют build_*_query из queries.py,
  константы для лимитов и сортировок
- tests/conftest.py: SEVERITY_WARNING, DEFAULT_ECOSYSTEM из constants

## Часть B: LLM-анализ finding'ов
- llm.py: клиент для OpenAI-совместимых API с промптом security-аналитика
- harvester.py: авто-триггер после flagged scan, сохранение report в БД
- api/findings.py: POST /{id}/analyze — ручной триггер
- web/routes.py: POST /api/v1/findings/{id}/analyze — HTMX-фрагмент
- _llm_report_fragment.html: шаблон фрагмента с вердиктом
- scan_detail.html, package_detail.html: кнопка Analyze with LLM
  (htmx-post, spinner, inline-замена на LLM-отчёт)
- style.css: стили для .llm-report .verdict-safe/suspicious/malicious

## Часть C: Тесты
- 50 тестов, все зелёные
- Линтер чистый
- Тесты используют constants где нужно
2026-05-10 04:37:07 +03:00
Marker689
c43e7c4c9b fix: критические баги и качество кода — полный аудит
Критические фиксы:
- main.py: монтировать /static из web/static/ (CSS не грузился совсем)
- api/scans.py: filtered total count (был всегда общий, игнорируя фильтры)
- web/routes.py: исправлен VALID_SORT_FIELDS (отсутствовали ключи packages)
- web/routes.py: filtered total count для web scans list
- package_detail.html: f.data.X вместо f.X (findings не отображались)

Чистка мёртвого кода:
- config.py: удалён _parse_repos и nexus_repositories (не использовались)
- web/routes.py: удалён completed_scans/failed_scans (не отображались)
- удалён мёртвый guarddog_nexus/static/style.css (67-байтный стаб)

Качество кода:
- web/routes.py: Jinja2 Environment кэшируется на уровне модуля
- Вынесен дублирующийся JS в web/static/app.js
- Вынесены дублирующиеся inline-стили в CSS-классы
- Исправлен duplicate class attribute в списках
- Удалены гигантские SVG из empty states

Тесты:
- 20 новых edge-case тестов (CSV export, search/filter/sort, 404, pagination)
- Добавлен sample_flagged_scan fixture
- Итого: 50 тестов, все зелёные
2026-05-10 03:46:05 +03:00
Marker689
6c8e89c95e fix: убрать неиспользуемый импорт text из packages.py 2026-05-10 03:17:32 +03:00
Marker689
97b89d3fdf ui: добавить экспорт в CSV для сканирований и пакетов
- api/scans.py: добавить /api/v1/scans/export endpoint с фильтрами
- api/packages.py: добавить /api/v1/packages/export endpoint с фильтрами
- scans_list.html: добавить кнопку Export CSV в filter bar
- packages_list.html: добавить кнопку Export CSV в filter bar
- CSV включает все поля с правильным форматированием
2026-05-10 03:16:58 +03:00
Marker689
d5df1d75b8 ui: улучшить карточки findings — сворачивание, копирование кода, breadcrumbs
- Добавить collapsible details/summary для findings
- Добавить кнопку Copy с feedback 'Copied!'
- Добавить toggle All (Expand/Collapse)
- Добавить breadcrumbs на detail-страницах
- Добавить spinner для scanning статуса на detail-страницах
- Добавить empty state для findings
- Добавить scripts block в base.html
2026-05-10 03:13:54 +03:00
Marker689
00424b494a ui: добавить поиск, фильтрацию и сортировку на списки
API:
- scans.py: добавить search, status, sort_by, sort_dir параметры
- packages.py: добавить search, sort_by, sort_dir параметры
- web/routes.py: передать новые параметры в шаблоны

UI:
- scans_list.html: search input (htmx debounce), status filter dropdown,
  sortable columns с hx-get, empty state
- packages_list.html: search input (htmx debounce), sortable columns,
  empty state
- pagination сохраняет все параметры фильтрации/сортировки
2026-05-10 03:12:26 +03:00
Marker689
d00cee3432 ui: улучшить дашборд — bar-графики, tooltips, empty states, spinner для scanning
- Заменить inline div-графики на styled bars с transition
- Добавить tooltips на heatmap
- Добавить empty state когда нет findings
- Добавить spinner для статуса scanning
- Добавить breadcrumbs на dashboard
- Добавить title block
2026-05-10 03:07:07 +03:00
Marker689
18d33cdfd7 ui: вынести CSS в отдельный файл, добавить sticky nav и title block
- Создать guarddog_nexus/web/static/style.css со всеми стилями
- Убрать inline <style> из base.html
- Добавить sticky header для навигации
- Добавить title block для кастомных заголовков страниц
- Добавить базовые анимации (pulse, spin, hover effects)
2026-05-10 03:05:55 +03:00
Marker689
42dcfe33b5 fix: SHA256-based dedup — same version different file now re-scans 2026-05-09 06:20:43 +03:00
Marker689
41a8745198 fix: async subprocess + httpx — no more event loop blocking during scans 2026-05-09 05:58:55 +03:00
Marker689
e577f1944c refactor: JSON data column for findings, code snippets captured and displayed 2026-05-09 05:52:10 +03:00
Marker689
e83167a938 feat: rich dashboard with severity bars, heatmap, most flagged, live poll 2026-05-09 05:35:36 +03:00
Marker689
d776d037e7 fix: real nexus webhook format, atomic dedup, tested live 2026-05-09 05:25:58 +03:00
Marker689
4bfead8d6e fix: scanner now handles real guarddog v2 JSON format 2026-05-09 04:55:58 +03:00
Marker689
4ce99d3c85 feat: guarddog-nexus — webhook-based PyPI scanner with web UI 2026-05-09 04:48:10 +03:00
Marker689
bdcc82807d init: guarddog-nexus project skeleton 2026-05-09 04:32:48 +03:00