From 957a25623d5246dc0737c2621fd09b5e0046f83a Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 09:45:53 +0000 Subject: [PATCH 1/8] Add DevSecOps security pipeline --- .github/workflows/dast.yml | 127 ++++++++++++ .github/workflows/security.yml | 154 +++++++++++++++ .zap/rules.tsv | 7 + .zap/zap-auth.yaml | 76 +++++++ docs/DEVSECOPS.md | 186 ++++++++++++++++++ docs/FORK_MAPPING.md | 33 ++++ docs/PIPELINE_README.md | 50 +++++ docs/THREATMODEL.md | 76 +++++++ ...\277\320\270\321\201\320\272\320\260.docx" | Bin 0 -> 14852 bytes security/dojo_upload.py | 96 +++++++++ security/gate.py | 178 +++++++++++++++++ 11 files changed, 983 insertions(+) create mode 100644 .github/workflows/dast.yml create mode 100644 .github/workflows/security.yml create mode 100644 .zap/rules.tsv create mode 100644 .zap/zap-auth.yaml create mode 100644 docs/DEVSECOPS.md create mode 100644 docs/FORK_MAPPING.md create mode 100644 docs/PIPELINE_README.md create mode 100644 docs/THREATMODEL.md create mode 100644 "docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" create mode 100644 security/dojo_upload.py create mode 100644 security/gate.py diff --git a/.github/workflows/dast.yml b/.github/workflows/dast.yml new file mode 100644 index 00000000000..e9ef10cf10c --- /dev/null +++ b/.github/workflows/dast.yml @@ -0,0 +1,127 @@ +name: dast + +# Динамический анализ: поднимаем DefectDojo через его штатный docker-compose +# и сканируем OWASP ZAP (baseline + full). Тяжёлый full-scan — по расписанию и вручную. + +on: + workflow_dispatch: + schedule: + - cron: "0 2 * * 1" # еженедельно, пн 02:00 UTC + push: + branches: [ main, master ] + +permissions: + contents: read + issues: write + security-events: write + +env: + TARGET_URL: http://localhost:8080 + REPORTS_DIR: reports + +jobs: + dast: + name: OWASP ZAP + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build & start DefectDojo + run: | + mkdir -p "$REPORTS_DIR" + # DefectDojo требует профиль БД; предпочитаем штатные helper-скрипты + if [ -x ./dc-build.sh ] && [ -x ./dc-up-d.sh ]; then + ./dc-build.sh + ./dc-up-d.sh postgres-redis + else + docker compose build + docker compose --profile postgres-redis up -d + fi + echo "Ожидаем инициализацию (миграции, статик, суперюзер)..." + + - name: Wait for service + run: | + for i in $(seq 1 60); do + if curl -skf "$TARGET_URL/login" >/dev/null; then + echo "DefectDojo поднят"; exit 0 + fi + echo "ждём... ($i/60)"; sleep 15 + done + echo "Сервис не поднялся вовремя"; docker compose logs --tail=100; exit 1 + + # --- ZAP baseline (пассивный, быстрый) --- + - name: ZAP Baseline Scan + uses: zaproxy/action-baseline@v0.12.0 + continue-on-error: true + with: + target: ${{ env.TARGET_URL }} + rules_file_name: .zap/rules.tsv + cmd_options: "-J zap-baseline.json -w zap-baseline.md" + allow_issue_writing: true + + # --- ZAP full scan (активный: инъекции, обход и т.п.) --- + - name: ZAP Full Scan + uses: zaproxy/action-full-scan@v0.12.0 + continue-on-error: true + with: + target: ${{ env.TARGET_URL }} + rules_file_name: .zap/rules.tsv + cmd_options: "-J zap-full.json" + allow_issue_writing: false + + # --- ZAP АУТЕНТИФИЦИРОВАННЫЙ скан (browser-based, обходит CSRF Django) --- + - name: ZAP Authenticated Scan + continue-on-error: true + env: + ZAP_USER: ${{ secrets.ZAP_USER }} + ZAP_PASSWORD: ${{ secrets.ZAP_PASSWORD }} + run: | + mkdir -p zap_wrk && chmod 777 zap_wrk + cp .zap/zap-auth.yaml zap_wrk/ + docker run --rm --network host \ + -e ZAP_USER -e ZAP_PASSWORD \ + -v "$PWD/zap_wrk:/zap/wrk:rw" \ + ghcr.io/zaproxy/zaproxy:stable \ + zap.sh -cmd -autorun /zap/wrk/zap-auth.yaml || true + + - name: Collect ZAP reports + if: always() + run: | + cp -v zap-baseline.json "$REPORTS_DIR/" 2>/dev/null || true + cp -v zap-full.json "$REPORTS_DIR/" 2>/dev/null || true + # аутентифицированные отчёты: SARIF для гейта + traditional-json для DefectDojo + cp -v zap_wrk/zap-auth.sarif "$REPORTS_DIR/" 2>/dev/null || true + cp -v zap_wrk/zap-auth.json "$REPORTS_DIR/zap-auth.sarif" 2>/dev/null || true + cp -v zap_wrk/zap-auth-dojo.json "$REPORTS_DIR/" 2>/dev/null || true + + - name: Upload ZAP results to DefectDojo + if: always() + continue-on-error: true + env: + DOJO_URL: ${{ secrets.DOJO_URL }} + DOJO_TOKEN: ${{ secrets.DOJO_TOKEN }} + DOJO_PRODUCT: "DefectDojo (self)" + DOJO_ENGAGEMENT: "DAST ${{ github.ref_name }}" + run: | + pip install requests + python security/dojo_upload.py --stage dast + + - name: Security Gateway (DAST) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GATE_FAIL_ON: CRITICAL # DAST шумит — блокируем только на critical + run: | + python security/gate.py --reports "$REPORTS_DIR" + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: dast-reports + path: | + ${{ env.REPORTS_DIR }} + zap-*.md + + - name: Tear down + if: always() + run: docker compose down -v diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000000..44b07f6377d --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,154 @@ +name: security + +# SAST + SCA + secrets + IaC/image checks + Security Gateway. +# Все отчёты приводятся к SARIF, публикуются во вкладку Security, +# выгружаются в DefectDojo и проходят через gate.py. +# +# ВНИМАНИЕ: версии сторонних actions приведены как ориентир. Перед сдачей +# зафиксируйте их (лучше — к commit SHA) и проверьте актуальность. + +on: + push: + branches: [ main, master ] + pull_request: + workflow_dispatch: + +permissions: + contents: read + pull-requests: write # для комментариев gate.py в PR + security-events: write # для загрузки SARIF во вкладку Security + +env: + REPORTS_DIR: reports + +jobs: + sast-sca: + name: SAST / SCA / IaC + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # нужна полная история для Gitleaks + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Prepare reports dir + run: mkdir -p "$REPORTS_DIR" + + # --- Установка сканеров --- + - name: Install scanners + run: | + python -m pip install --upgrade pip + pip install semgrep bandit bandit-sarif-formatter njsscan checkov requests + + # --- SAST: Semgrep (мультиязычный) --- + - name: Semgrep + continue-on-error: true + run: | + semgrep scan \ + --config p/default --config p/security-audit \ + --config p/python --config p/javascript \ + --sarif --output "$REPORTS_DIR/semgrep.sarif" || true + + # --- SAST: Bandit (Python) --- + - name: Bandit + continue-on-error: true + run: | + bandit -r . -f sarif -o "$REPORTS_DIR/bandit.sarif" \ + -x ./tests,./unittests || true + + # --- SAST: njsscan (JavaScript) --- + - name: njsscan + continue-on-error: true + run: | + njsscan --sarif -o "$REPORTS_DIR/njsscan.sarif" . || true + + # --- SCA: Trivy filesystem (зависимости pip/npm) --- + - name: Trivy filesystem (SCA) + uses: aquasecurity/trivy-action@0.28.0 + continue-on-error: true + with: + scan-type: fs + scanners: vuln + format: sarif + output: ${{ env.REPORTS_DIR }}/trivy-fs.sarif + severity: MEDIUM,HIGH,CRITICAL + + # --- IaC / Dockerfile misconfig: Trivy config --- + - name: Trivy config (IaC) + uses: aquasecurity/trivy-action@0.28.0 + continue-on-error: true + with: + scan-type: config + format: sarif + output: ${{ env.REPORTS_DIR }}/trivy-config.sarif + + # --- IaC: Checkov (docker-compose / Dockerfile) --- + - name: Checkov + continue-on-error: true + run: | + checkov -d . --framework dockerfile,docker_compose \ + -o sarif --output-file-path "$REPORTS_DIR" || true + # checkov кладёт results.sarif в каталог — переименуем + [ -f "$REPORTS_DIR/results.sarif" ] && mv "$REPORTS_DIR/results.sarif" "$REPORTS_DIR/checkov.sarif" || true + + # --- Dockerfile lint: Hadolint по ВСЕМ Dockerfile форка --- + - name: Hadolint (all Dockerfiles) + continue-on-error: true + run: | + # (зона роста: пиннинговать версию вместо latest) + curl -sSL -o /tmp/hadolint \ + https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 + chmod +x /tmp/hadolint + files=$(git ls-files | grep -Ei '(^|/)Dockerfile' | sort -u || true) + echo "Найдены Dockerfile: $files" + if [ -n "$files" ]; then + /tmp/hadolint -f sarif $files > "$REPORTS_DIR/hadolint.sarif" || true + fi + + # --- Секреты: Gitleaks --- + - name: Gitleaks + uses: gitleaks/gitleaks-action@v2 + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "false" + with: + # SARIF окажется в results.sarif — скопируем ниже + args: detect --report-format sarif --report-path ${{ env.REPORTS_DIR }}/gitleaks.sarif --redact + + # --- Публикация во вкладку Security (GitHub code scanning) --- + - name: Upload SARIF to GitHub Security + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: ${{ env.REPORTS_DIR }} + + # --- Выгрузка в DefectDojo --- + - name: Upload results to DefectDojo + continue-on-error: true + env: + DOJO_URL: ${{ secrets.DOJO_URL }} + DOJO_TOKEN: ${{ secrets.DOJO_TOKEN }} + DOJO_PRODUCT: "DefectDojo (self)" + DOJO_ENGAGEMENT: "CI ${{ github.ref_name }}" + run: | + python security/dojo_upload.py --stage sast + + # --- Сохранить отчёты как артефакт --- + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: sast-reports + path: ${{ env.REPORTS_DIR }} + + # --- SECURITY GATEWAY --- + - name: Security Gateway + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GATE_FAIL_ON: HIGH # Critical/High роняют пайплайн + run: | + python security/gate.py --reports "$REPORTS_DIR" diff --git a/.zap/rules.tsv b/.zap/rules.tsv new file mode 100644 index 00000000000..206e1739a11 --- /dev/null +++ b/.zap/rules.tsv @@ -0,0 +1,7 @@ +# ZAP scan rules — управление уровнем алертов (FAIL / WARN / IGNORE) +# Формат: \t\t +# Полный список ID: https://www.zaproxy.org/docs/alerts/ +# Ниже — пример подавления типичного шума на dev-инстансе. +10015 WARN (Incomplete or No Cache-control Header Set) +10096 IGNORE (Timestamp Disclosure — низкий риск на тестовом стенде) +10027 IGNORE (Information Disclosure - Suspicious Comments) diff --git a/.zap/zap-auth.yaml b/.zap/zap-auth.yaml new file mode 100644 index 00000000000..ab69e85154a --- /dev/null +++ b/.zap/zap-auth.yaml @@ -0,0 +1,76 @@ +# ZAP Automation Framework — АУТЕНТИФИЦИРОВАННЫЙ скан DefectDojo. +# Browser-based авторизация: ZAP реально заполняет форму логина в headless-браузере, +# поэтому Django CSRF-токен (csrfmiddlewaretoken) подхватывается автоматически. +# +# Запуск (см. dast.yml): +# docker run --network host -e ZAP_USER -e ZAP_PASSWORD \ +# -v $PWD/zap_wrk:/zap/wrk:rw ghcr.io/zaproxy/zaproxy:stable \ +# zap.sh -cmd -autorun /zap/wrk/zap-auth.yaml +# ${ZAP_USER}/${ZAP_PASSWORD} подставляются ZAP из переменных окружения. + +env: + contexts: + - name: defectdojo + urls: + - "http://localhost:8080" + includePaths: + - "http://localhost:8080.*" + excludePaths: + - "http://localhost:8080/logout.*" + - ".*/logout/?$" + authentication: + method: "browser" + parameters: + loginPageUrl: "http://localhost:8080/login" + loginPageWait: 5 + browserId: "firefox-headless" + verification: + method: "poll" + loggedInRegex: '\\bLogout\\b' + loggedOutRegex: '\\bLogin\\b' + pollUrl: "http://localhost:8080/" + pollFrequency: 60 + pollUnits: "requests" + sessionManagement: + method: "cookie" + users: + - name: "zaptester" + credentials: + username: "${ZAP_USER}" + password: "${ZAP_PASSWORD}" + parameters: + failOnError: false + failOnWarning: false + progressToStdout: true + +jobs: + - type: spider + parameters: + context: defectdojo + user: "zaptester" + maxDuration: 5 + - type: spiderAjax + parameters: + context: defectdojo + user: "zaptester" + maxDuration: 5 + browserId: "firefox-headless" + - type: passiveScan-wait + parameters: + maxDuration: 5 + - type: activeScan + parameters: + context: defectdojo + user: "zaptester" + maxRuleDurationInMins: 3 + maxScanDurationInMins: 30 + - type: report # для Security Gateway + parameters: + template: "sarif-json" + reportDir: "/zap/wrk" + reportFile: "zap-auth" + - type: report # для нативного парсера DefectDojo + parameters: + template: "traditional-json" + reportDir: "/zap/wrk" + reportFile: "zap-auth-dojo" diff --git a/docs/DEVSECOPS.md b/docs/DEVSECOPS.md new file mode 100644 index 00000000000..ec982bc00a0 --- /dev/null +++ b/docs/DEVSECOPS.md @@ -0,0 +1,186 @@ +# DevSecOps-пайплайн для DefectDojo + +Дипломная работа по профессии «Специалист по информационной безопасности», трек DevSecOps. + +Документ описывает архитектуру безопасного CI/CD-конвейера, обоснование выбора инструментов, +матрицу покрытия проверками, политику Security Gateway и зоны роста. + +--- + +## 1. Объект защиты + +**Целевой проект:** [DefectDojo](https://github.com/DefectDojo/django-DefectDojo) — открытая +платформа управления уязвимостями (OWASP). + +Проект выбран потому, что удовлетворяет всем требованиям задания: это полноценный веб-сервис +с несколькими языками и фреймворками, реляционной СУБД и кешем. + +| Компонент | Технология | +|---|---| +| Веб-фреймворк | Django (Python) | +| Фронтенд | HTML-шаблоны, JavaScript, Vue-компоненты, SCSS | +| База данных | PostgreSQL | +| Кеш / брокер | Redis | +| Очереди задач | Celery (worker + beat) | +| Веб-сервер | nginx (reverse proxy) | +| Упаковка | Docker / docker-compose | + +Дополнительный плюс выбора: DefectDojo сам является системой менеджмента уязвимостей, поэтому +для этапа «выгрузка результатов в систему менеджмента» поднимается **отдельный** инстанс +DefectDojo (не тот, что тестируется), куда через REST API стекаются все отчёты сканеров. +Это разделяет роли «мишень» и «хаб управления» и исключает конфликт интересов. + +--- + +## 2. Архитектура пайплайна + +```mermaid +flowchart TD + dev[Разработчик: push / pull_request] --> gha[GitHub Actions] + + subgraph CI["CI: workflow security.yml"] + secrets[Gitleaks — секреты] + sast_py[Semgrep + Bandit — Python] + sast_js[Semgrep + njsscan — JS] + sca[Trivy fs — зависимости pip/npm] + iac[Checkov + Trivy config — IaC/Dockerfile] + img[Hadolint + Trivy image — образы] + end + + subgraph CD["CD/DAST: workflow dast.yml"] + build[docker-compose up DefectDojo] + zap[OWASP ZAP baseline + full scan] + end + + gha --> CI + gha --> CD + + CI --> sarif[SARIF-отчёты] + CD --> sarif + sarif --> gate[Security Gateway: gate.py] + sarif --> tab[GitHub Security tab] + sarif --> dojo[(DefectDojo — менеджмент)] + + gate -->|severity >= порога| block[Пайплайн падает,\nрелиз остановлен] + gate -->|комментарий| pr[Комментарий в Pull Request\nс рекомендациями] +``` + +Единый формат обмена — **SARIF** (Static Analysis Results Interchange Format). Все сканеры, +где это возможно, выводят SARIF; это позволяет одним gate-скриптом агрегировать результаты +и одновременно загружать их во вкладку GitHub Security через `codeql-action/upload-sarif`. + +--- + +## 3. Обоснование выбора инструментов + +Принцип отбора: приоритет open-source инструментам с активным сообществом, поддержкой SARIF, +готовыми парсерами в DefectDojo и удобным запуском в CI. Для требования «ни один язык не должен +быть пропущен» базовым выбран мультиязычный Semgrep, усиленный язык-специфичными анализаторами. + +### Этап 2 — SAST + +| Инструмент | Роль | Почему он | Рассмотренные альтернативы | +|---|---|---|---| +| **Semgrep** | Основной мультиязычный SAST | Один инструмент покрывает Python, JS/TS и др.; правила OWASP/CWE; нативный SARIF; парсер в DefectDojo | CodeQL (мощнее, но тяжелее и заточен под GitHub), SonarQube (нужен сервер) | +| **Bandit** | Углублённый SAST для Python | Специализирован на Python-специфике (небезопасная десериализация, `subprocess`, слабая криптография) | pylint-security | +| **njsscan** | SAST для JavaScript | Быстрые проверки Node/JS-паттернов, дополняет Semgrep | ESLint + eslint-plugin-security | +| **Trivy (fs)** | SCA — анализ зависимостей | Единый инструмент для pip и npm; база уязвимостей CVE/GHSA; SARIF | Grype, OWASP Dependency-Check, Snyk (коммерческий) | + +### Этап 3 — DAST + +| Инструмент | Роль | Почему он | Альтернативы | +|---|---|---|---| +| **OWASP ZAP** | Динамический анализ развёрнутого сервиса | Отраслевой стандарт; baseline (пассивный) + full (активный) сканы; Automation Framework для аутентифицированных сканов; парсер в DefectDojo | Nikto (проще, слабее), Nuclei (шаблонный), Burp (коммерческий) | + +### Этап 4 — Security Checks + +| Инструмент | Роль | Почему он | Альтернативы | +|---|---|---|---| +| **Gitleaks** | Поиск секретов в коде и истории | Быстрый, CI-native, настраиваемые правила, поддержка pre-commit | TruffleHog, detect-secrets | +| **Hadolint** | Линтер Dockerfile | Ловит небезопасные и неоптимальные инструкции Dockerfile | dockerfilelint | +| **Trivy (image)** | Скан собранных образов | ОС-пакеты и уязвимости слоёв; один вендор с SCA | Grype, Clair | +| **Trivy config / Checkov** | Проверка IaC и docker-compose на мисконфиги | Правила по CIS и best practices для Docker/compose | kics, terrascan | + +### Этап 5 — Security Gateway + +| Компонент | Роль | +|---|---| +| **gate.py** (собственный скрипт) | Агрегирует SARIF, считает находки по severity, роняет пайплайн при превышении порога | +| **GitHub PR API** | Оставляет в Pull Request комментарий со сводкой и рекомендациями по исправлению | +| **DefectDojo API** | Дедупликация, тренды, жизненный цикл уязвимостей, отчётность | + +--- + +## 4. Матрица покрытия + +Требование задания — покрыть каждый язык и фреймворк проекта. Соответствие: + +| Технология в проекте | SAST | SCA | Прочее | +|---|---|---|---| +| Python / Django | Semgrep, Bandit | Trivy (requirements) | — | +| JavaScript / Vue | Semgrep, njsscan | Trivy (package.json) | — | +| Dockerfile | Trivy config | — | Hadolint | +| docker-compose / IaC | Checkov, Trivy config | — | — | +| Собранные образы | — | Trivy image | — | +| Секреты (все файлы) | — | — | Gitleaks | +| Запущенный сервис (HTTP/API) | — | — | OWASP ZAP | + +Таким образом ни один язык, фреймворк или артефакт сборки не остаётся без проверки. + +--- + +## 5. Политика Security Gateway + +Гейт реализован в `security/gate.py`. Логика: + +1. Собирает все файлы `*.sarif` из отчётов сканеров. +2. Нормализует severity: числовой `security-severity` (шкала CVSS 0–10) → Critical/High/Medium/Low; + при его отсутствии используется `level` (error → High, warning → Medium, note → Low). +3. Сравнивает с порогом `GATE_FAIL_ON` (по умолчанию `HIGH`): наличие находок этого уровня и выше + останавливает пайплайн (exit code 1) — **триггер на остановку релиза**. +4. Формирует markdown-сводку: пишет в `$GITHUB_STEP_SUMMARY` и оставляет комментарий в Pull Request + через GitHub API, включая правило, файл, строку и ссылку на рекомендацию (`helpUri`). + +Пороги настраиваются через переменные окружения и позволяют разную строгость для веток +(например, `HIGH` для feature-веток и `MEDIUM` для `main`/релизов). + +--- + +## 6. Требуемые секреты и переменные (GitHub → Settings → Secrets) + +| Имя | Назначение | +|---|---| +| `DOJO_URL` | URL инстанса DefectDojo-менеджмента (например `https://:8080`) | +| `DOJO_TOKEN` | API-токен DefectDojo (Profile → API v2 Key) | +| `GITHUB_TOKEN` | Стандартный токен Actions (даётся автоматически) для комментариев в PR | + +Для аутентифицированного DAST дополнительно: `ZAP_USER`, `ZAP_PASSWORD` (тестовая учётка). + +--- + +## 7. Зоны роста + +Реалистичные направления развития, которые стоит вынести в защиту как «что дальше»: + +- **SBOM и цепочка поставок:** генерация CycloneDX/SPDX через Syft, подпись образов cosign, + верификация артефактов (SLSA). +- **Policy-as-Code:** вынести правила гейта в OPA/Conftest вместо императивного скрипта. +- **Аутентифицированный DAST:** полноценный обход защищённых страниц DefectDojo через + ZAP Automation Framework (шаблон приложен в `.zap/zap-auth.yaml`). +- **IAST/интерактивное тестирование** во время функциональных тестов. +- **Runtime-безопасность:** Falco/Tetragon на VPS для контроля поведения контейнеров. +- **Baseline и подавление шума:** ведение baseline ложных срабатываний, триаж в DefectDojo, + SLA на устранение по severity. +- **Пиннинг GitHub Actions к commit SHA** вместо тегов — защита от компрометации тегов действий. + +--- + +## 8. Как это отвечает критериям задания + +| Этап | Критерий | Реализация | +|---|---|---| +| 1. CI/CD | Настроенный пайплайн, облако, документация | GitHub Actions, VPS-раннер/облачные раннеры, этот документ | +| 2. SAST | Покрытие, успешные проверки, выгрузка | Semgrep/Bandit/njsscan/Trivy → SARIF → GitHub Security + DefectDojo | +| 3. DAST | Покрытие сервиса, все методы, выгрузка | ZAP baseline+full → DefectDojo | +| 4. Security Checks | Секреты, конфигурации/образы | Gitleaks + Hadolint + Trivy image/config + Checkov | +| 5. Security Gateway | Остановка релиза, комментарии, рекомендации | gate.py: exit-код + комментарий в PR с `helpUri` | diff --git a/docs/FORK_MAPPING.md b/docs/FORK_MAPPING.md new file mode 100644 index 00000000000..f20e0c663b6 --- /dev/null +++ b/docs/FORK_MAPPING.md @@ -0,0 +1,33 @@ +# Привязка к раскладке форка DefectDojo + +Пайплайн настроен под стандартную структуру upstream-репозитория DefectDojo. +Перед запуском сверь пункты ниже со своим форком и поправь при расхождении. + +## Предполагаемая раскладка + +| Что | Значение по умолчанию | Где используется | +|---|---|---| +| Dockerfile'ы | `Dockerfile.django-alpine`, `Dockerfile.nginx-alpine`, `*-debian`, `Dockerfile.integration-tests-debian` | Hadolint (по маске, менять не нужно) | +| Подъём сервиса | `./dc-build.sh` + `./dc-up-d.sh postgres-redis` | `dast.yml` | +| URL мишени | `http://localhost:8080` | `dast.yml`, `.zap/zap-auth.yaml` | +| Страница логина | `/login`, поле CSRF `csrfmiddlewaretoken` | browser-auth ZAP (CSRF решается автоматически) | +| Языки | Python, JavaScript/Vue, SCSS, Dockerfile, shell | матрица покрытия SAST | + +## Чек-лист перед сдачей + +1. **Dockerfile'ы.** Hadolint берёт все файлы по маске `Dockerfile*` через `git ls-files` — + ручной правки не требует. Убедись, что твои Dockerfile попадают в маску. +2. **Порт.** Если nginx опубликован не на `8080`, поменяй `TARGET_URL` в `dast.yml` и + `urls`/`loginPageUrl` в `.zap/zap-auth.yaml`. +3. **scan_type.** Все SARIF грузятся парсером `SARIF`, ZAP — `ZAP Scan`. Сверь, что оба + парсера есть в твоей версии DefectDojo: `GET {DOJO_URL}/api/v2/test_types/`. +4. **Тестовый пользователь ZAP.** Заведи `zaptester` в мишени и положи `ZAP_USER`/`ZAP_PASSWORD` + в секреты репозитория. +5. **Профиль БД.** Если compose стартует иначе (другой профиль/скрипт) — поправь шаг + «Build & start DefectDojo» в `dast.yml`. +6. **Версии Actions.** Зафиксируй теги (лучше — commit SHA) в обоих workflow. + +## Что править НЕ нужно + +- `gate.py` — универсален, работает с любыми SARIF в каталоге `reports/`. +- Матрица покрытия SAST — Semgrep + Bandit + njsscan + Trivy покрывают все языки форка. diff --git a/docs/PIPELINE_README.md b/docs/PIPELINE_README.md new file mode 100644 index 00000000000..dd72f0f2c19 --- /dev/null +++ b/docs/PIPELINE_README.md @@ -0,0 +1,50 @@ +# DevSecOps starter kit для форка DefectDojo + +Комплект файлов для безопасного CI/CD-пайплайна поверх +[DefectDojo](https://github.com/DefectDojo/django-DefectDojo) на GitHub Actions. + +## Структура + +``` +.github/workflows/ + security.yml # SAST + SCA + секреты + IaC/образы + Security Gateway + dast.yml # OWASP ZAP baseline + full против запущенного DefectDojo +security/ + gate.py # Security Gateway: агрегация SARIF, блок релиза, комментарий в PR + dojo_upload.py # выгрузка отчётов в отдельный DefectDojo (менеджмент) +.zap/ + rules.tsv # подавление шума ZAP + zap-auth.yaml # шаблон аутентифицированного скана (зона роста) +docs/ + DEVSECOPS.md # документация процесса + аналитика выбора инструментов +``` + +## Как поднять + +1. Форкни DefectDojo и скопируй сюда каталоги `.github/`, `security/`, `.zap/`, `docs/`. +2. На выданной VPS подними **отдельный** инстанс DefectDojo как систему менеджмента + (по официальной инструкции: `./dc-up.sh postgres-redis`). Создай в нём API-токен. +3. В настройках репозитория (Settings → Secrets and variables → Actions) добавь: + - `DOJO_URL` — например `https://:8080` + - `DOJO_TOKEN` — API v2 Key из профиля DefectDojo + - (для аутентифицированного DAST) `ZAP_USER`, `ZAP_PASSWORD` +4. Запусти workflow `security` (на push/PR) и `dast` (вручную или по расписанию). +5. Проверь результаты в трёх местах: вкладка **Security** репозитория, комментарий + в **Pull Request**, дашборд **DefectDojo**. + +## Раскатка (этап 1 — облако/VPS) + +- Пайплайн исполняется на GitHub-hosted раннерах по умолчанию. +- Чтобы задействовать выданную VPS как исполнителя, зарегистрируй на ней + self-hosted runner (Settings → Actions → Runners) и замени в workflow + `runs-on: ubuntu-latest` на `runs-on: self-hosted`. +- DAST-инстанс мишени и DefectDojo-менеджмент разворачиваются на VPS через + их штатный `docker-compose`. + +## Важные оговорки + +- Версии сторонних GitHub Actions в workflow — ориентировочные. Перед сдачей + зафиксируй их и по возможности пиннингуй к commit SHA (это отдельный плюс к защите). +- Строки `scan_type` в `dojo_upload.py` должны совпадать с парсерами твоей версии + DefectDojo — сверься с `GET {DOJO_URL}/api/v2/test_types/`. +- Пороги гейта настраиваются переменной `GATE_FAIL_ON` (по умолчанию `HIGH`). diff --git a/docs/THREATMODEL.md b/docs/THREATMODEL.md new file mode 100644 index 00000000000..cc1ae9141f4 --- /dev/null +++ b/docs/THREATMODEL.md @@ -0,0 +1,76 @@ +# Модель угроз и SLA на устранение + +Документ связывает угрозы DefectDojo (по методологии STRIDE) с конкретными +контролями пайплайна и задаёт сроки устранения (SLA) по уровням severity. + +--- + +## 1. Границы доверия + +Рассматриваются три зоны: + +- **Репозиторий и CI** — исходный код, зависимости, конфигурации, секреты, workflow. +- **Артефакты сборки** — Docker-образы (django, nginx) и docker-compose. +- **Запущенный сервис** — DefectDojo за nginx, с PostgreSQL и Redis, доступный по HTTP. + +Пересечение границ (код → образ → работающий сервис) — точки, где вносятся и +проявляются уязвимости; на каждой из них стоит соответствующий контроль пайплайна. + +--- + +## 2. STRIDE: угрозы и контроли + +| Категория STRIDE | Угроза применительно к DefectDojo | Контроль в пайплайне | +|---|---|---| +| **S — Spoofing** (подмена субъекта) | Утечка учётных данных/токенов в коде; слабая аутентификация форм | Gitleaks (секреты), проверка потоков аутентификации аутентифицированным ZAP, отсутствие хардкода в SAST | +| **T — Tampering** (искажение) | Компрометация зависимостей и базовых образов (supply chain) | Trivy SCA (pip/npm), Trivy image, пиннинг GitHub Actions к SHA; SBOM/cosign — зона роста | +| **R — Repudiation** (отказ от авторства) | Нет аудита изменений и находок | Логи CI, история находок и их жизненный цикл в DefectDojo; подпись коммитов — зона роста | +| **I — Information Disclosure** (раскрытие) | Секреты в коде, подробные ошибки, SQLi с утечкой данных БД | Gitleaks; Semgrep/Bandit (SQLi, небезопасный вывод); ZAP-алерты о раскрытии информации | +| **D — Denial of Service** (отказ в обслуживании) | ReDoS, уязвимые зависимости, ресурсное истощение | Semgrep (небезопасные регулярные выражения), Trivy (CVE зависимостей) | +| **E — Elevation of Privilege** (повышение привилегий) | Инъекции (SQL/командные), небезопасная десериализация, контейнер от root, мисконфиги | Semgrep/Bandit, активный скан ZAP, Trivy config/Checkov, Hadolint (запуск не от root) | + +Такое сопоставление показывает, что каждая категория угроз покрыта минимум одним +автоматическим контролем, а критичные (I и E) — несколькими. + +--- + +## 3. SLA на устранение + +Сроки отсчитываются с момента появления находки в DefectDojo. Действие Security +Gateway определяется порогом `GATE_FAIL_ON` в workflow. + +| Severity | Срок устранения | Действие Gateway | Кто отвечает | +|---|---|---|---| +| Critical | 24–48 часов | Блокировка релиза | Владелец компонента + безопасность | +| High | 7 дней | Блокировка релиза | Владелец компонента | +| Medium | 30 дней | Предупреждение, тикет, комментарий в PR | Команда разработки | +| Low | 90 дней / по возможности | Информирование | Команда разработки | + +Порог блокировки настраивается по веткам: `HIGH` для feature-веток и `security.yml`, +`CRITICAL` для шумного DAST (`dast.yml`). Для релизной ветки рекомендуется ужесточить +до `MEDIUM`. + +--- + +## 4. Триаж в DefectDojo + +Жизненный цикл находки, чтобы SLA работал, а не копился шум: + +1. **New** — находка импортирована сканером. +2. **Triaged** — подтверждена как истинная либо помечена как ложное срабатывание. +3. **Active / False Positive / Risk Accepted** — решение по находке. +4. **Mitigated** — исправление внесено. +5. **Verified** — исправление подтверждено повторным сканом. + +Дедупликация DefectDojo не даёт одной и той же уязвимости порождать новые тикеты при +каждом запуске, а тренды показывают динамику долга по безопасности во времени. + +--- + +## 5. Остаточные риски и зоны роста + +- Ложные срабатывания активного DAST требуют ведения baseline и ручного триажа. +- Полное покрытие бизнес-логики требует аутентифицированных и, при необходимости, + ролевых сценариев (несколько пользователей с разными правами). +- Цепочка поставок усиливается генерацией SBOM, подписью образов cosign и + верификацией по SLSA — вынесено в перспективные направления. diff --git "a/docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" "b/docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" new file mode 100644 index 0000000000000000000000000000000000000000..7ef4a4b55243b5ce21e11c0abddf581c1c39d17e GIT binary patch literal 14852 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 literal 0 HcmV?d00001 diff --git a/security/dojo_upload.py b/security/dojo_upload.py new file mode 100644 index 00000000000..7273e359953 --- /dev/null +++ b/security/dojo_upload.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Выгрузка отчётов сканеров в DefectDojo через /api/v2/import-scan/. + +Использует auto_create_context — продукт и engagement создаются по имени. +Переменные окружения: DOJO_URL, DOJO_TOKEN, DOJO_PRODUCT, DOJO_ENGAGEMENT. + +Ключевое решение по scan_type: + - Все SARIF-отчёты грузятся УНИВЕРСАЛЬНЫМ парсером "SARIF" (он валиден для + Semgrep, Bandit, njsscan, Trivy, Checkov, Hadolint, Gitleaks — все умеют SARIF). + - ZAP грузится своим НАТИВНЫМ парсером "ZAP Scan" (traditional-json отчёт). + +Так исключён рассинхрон «файл SARIF, а парсер ждёт родной JSON». +Сверить доступные парсеры: GET {DOJO_URL}/api/v2/test_types/ +""" +import argparse +import os +import sys + +import requests + +# report file -> DefectDojo scan_type +REPORTS = { + "sast": { + "reports/semgrep.sarif": "SARIF", + "reports/bandit.sarif": "SARIF", + "reports/njsscan.sarif": "SARIF", + "reports/trivy-fs.sarif": "SARIF", + "reports/trivy-config.sarif": "SARIF", + "reports/checkov.sarif": "SARIF", + "reports/hadolint.sarif": "SARIF", + "reports/gitleaks.sarif": "SARIF", + }, + "dast": { + # нативный парсер ZAP ждёт traditional-json, НЕ sarif + "reports/zap-baseline.json": "ZAP Scan", + "reports/zap-full.json": "ZAP Scan", + "reports/zap-auth-dojo.json": "ZAP Scan", + }, +} + + +def upload(url, token, product, engagement, file_path, scan_type): + if not os.path.isfile(file_path): + print(f"[dojo] нет файла {file_path} — пропуск") + return + endpoint = url.rstrip("/") + "/api/v2/import-scan/" + data = { + "scan_type": scan_type, + "product_name": product, + "engagement_name": engagement, + "auto_create_context": "true", + "active": "true", + "verified": "false", + "minimum_severity": "Low", + "close_old_findings": "false", + } + with open(file_path, "rb") as fh: + files = {"file": (os.path.basename(file_path), fh)} + try: + resp = requests.post( + endpoint, + headers={"Authorization": f"Token {token}"}, + data=data, + files=files, + timeout=120, + verify=os.environ.get("DOJO_VERIFY_TLS", "true").lower() != "false", + ) + except requests.RequestException as exc: + print(f"[dojo] ошибка сети для {file_path}: {exc}") + return + if resp.status_code in (200, 201): + print(f"[dojo] загружено {file_path} как '{scan_type}'") + else: + print(f"[dojo] {file_path}: HTTP {resp.status_code} — {resp.text[:300]}") + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--stage", choices=list(REPORTS), required=True) + args = ap.parse_args() + + url = os.environ.get("DOJO_URL") + token = os.environ.get("DOJO_TOKEN") + product = os.environ.get("DOJO_PRODUCT", "DefectDojo (self)") + engagement = os.environ.get("DOJO_ENGAGEMENT", "CI") + + if not (url and token): + print("[dojo] DOJO_URL/DOJO_TOKEN не заданы — выгрузка пропущена") + sys.exit(0) + + for file_path, scan_type in REPORTS[args.stage].items(): + upload(url, token, product, engagement, file_path, scan_type) + + +if __name__ == "__main__": + main() diff --git a/security/gate.py b/security/gate.py new file mode 100644 index 00000000000..5ae6ffe5a36 --- /dev/null +++ b/security/gate.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Security Gateway. + +Агрегирует SARIF-отчёты сканеров, считает находки по severity, при превышении +порога роняет пайплайн (exit 1) и оставляет комментарий со сводкой и +рекомендациями в Pull Request. + +Порог задаётся переменной окружения GATE_FAIL_ON (CRITICAL|HIGH|MEDIUM|LOW). +""" +import argparse +import glob +import json +import os +import sys +import urllib.request +from collections import Counter, defaultdict + +SEV_ORDER = ["LOW", "MEDIUM", "HIGH", "CRITICAL"] + + +def sev_from_result(result, rules): + """SARIF result -> severity bucket.""" + rule_id = result.get("ruleId", "") + props = rules.get(rule_id, {}).get("properties", {}) + score = props.get("security-severity") or result.get("properties", {}).get("security-severity") + if score is not None: + try: + s = float(score) + except ValueError: + s = None + if s is not None: + if s >= 9.0: + return "CRITICAL" + if s >= 7.0: + return "HIGH" + if s >= 4.0: + return "MEDIUM" + return "LOW" + level = result.get("level", "warning") + return {"error": "HIGH", "warning": "MEDIUM", "note": "LOW"}.get(level, "MEDIUM") + + +def load_rules(run): + rules = {} + driver = run.get("tool", {}).get("driver", {}) + for r in driver.get("rules", []): + rules[r.get("id", "")] = r + return rules + + +def parse_sarif(path): + findings = [] + try: + with open(path, encoding="utf-8") as fh: + data = json.load(fh) + except Exception as exc: # noqa: BLE001 + print(f"[gate] пропуск {path}: {exc}") + return findings + for run in data.get("runs", []): + tool = run.get("tool", {}).get("driver", {}).get("name", os.path.basename(path)) + rules = load_rules(run) + for res in run.get("results", []): + rule = rules.get(res.get("ruleId", ""), {}) + loc = "" + locs = res.get("locations", []) + if locs: + phys = locs[0].get("physicalLocation", {}) + uri = phys.get("artifactLocation", {}).get("uri", "") + line = phys.get("region", {}).get("startLine", "") + loc = f"{uri}:{line}" if line else uri + findings.append({ + "tool": tool, + "rule": res.get("ruleId", ""), + "severity": sev_from_result(res, rules), + "message": (res.get("message", {}) or {}).get("text", "")[:200], + "location": loc, + "help": rule.get("helpUri", "") or (rule.get("help", {}) or {}).get("text", "")[:200], + }) + return findings + + +def post_pr_comment(body): + token = os.environ.get("GITHUB_TOKEN") + event_path = os.environ.get("GITHUB_EVENT_PATH") + repo = os.environ.get("GITHUB_REPOSITORY") + if not (token and event_path and repo): + print("[gate] нет данных для комментария в PR — пропуск") + return + try: + with open(event_path, encoding="utf-8") as fh: + event = json.load(fh) + pr = event.get("pull_request", {}).get("number") + except Exception: # noqa: BLE001 + pr = None + if not pr: + print("[gate] это не PR — комментарий не оставляем") + return + url = f"https://api.github.com/repos/{repo}/issues/{pr}/comments" + req = urllib.request.Request( + url, + data=json.dumps({"body": body}).encode("utf-8"), + headers={ + "Authorization": f"Bearer {token}", + "Accept": "application/vnd.github+json", + "Content-Type": "application/json", + }, + method="POST", + ) + try: + urllib.request.urlopen(req, timeout=30) + print("[gate] комментарий оставлен в PR") + except Exception as exc: # noqa: BLE001 + print(f"[gate] не удалось оставить комментарий: {exc}") + + +def build_report(findings, counts, threshold, blocked): + status = "❌ РЕЛИЗ ОСТАНОВЛЕН" if blocked else "✅ Проверки пройдены" + lines = [f"## Security Gateway — {status}", ""] + lines.append(f"Порог блокировки: **{threshold}** и выше.") + lines.append("") + lines.append("| Severity | Найдено |") + lines.append("|---|---|") + for sev in reversed(SEV_ORDER): + lines.append(f"| {sev} | {counts.get(sev, 0)} |") + lines.append("") + # топ блокирующих находок с рекомендациями + thr_idx = SEV_ORDER.index(threshold) + blocking = [f for f in findings if SEV_ORDER.index(f["severity"]) >= thr_idx] + if blocking: + lines.append("### Блокирующие находки (топ-15)") + lines.append("") + lines.append("| Severity | Инструмент | Правило | Где | Рекомендация |") + lines.append("|---|---|---|---|---|") + for f in blocking[:15]: + rec = f["help"] or "—" + lines.append(f"| {f['severity']} | {f['tool']} | `{f['rule']}` | {f['location']} | {rec} |") + if len(blocking) > 15: + lines.append("") + lines.append(f"…и ещё {len(blocking) - 15}. Полный список — в артефактах и DefectDojo.") + return "\n".join(lines) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--reports", default="reports") + args = ap.parse_args() + + threshold = os.environ.get("GATE_FAIL_ON", "HIGH").upper() + if threshold not in SEV_ORDER: + threshold = "HIGH" + + findings = [] + for path in glob.glob(os.path.join(args.reports, "*.sarif")): + findings.extend(parse_sarif(path)) + + counts = Counter(f["severity"] for f in findings) + thr_idx = SEV_ORDER.index(threshold) + blocked = any(SEV_ORDER.index(f["severity"]) >= thr_idx for f in findings) + + report = build_report(findings, counts, threshold, blocked) + print(report) + + # step summary + summary = os.environ.get("GITHUB_STEP_SUMMARY") + if summary: + with open(summary, "a", encoding="utf-8") as fh: + fh.write(report + "\n") + + post_pr_comment(report) + + if blocked: + print(f"\n[gate] найдены уязвимости уровня {threshold}+ — останавливаем релиз") + sys.exit(1) + print("\n[gate] порог не превышен") + + +if __name__ == "__main__": + main() From 755c690b2f87411c05626c2ac23e12900c0c5af8 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 15:42:49 +0000 Subject: [PATCH 2/8] fix: install trivy as binary, drop nonexistent trivy-action tag --- .github/workflows/security.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 44b07f6377d..31fd88a6cf8 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -42,6 +42,8 @@ jobs: run: | python -m pip install --upgrade pip pip install semgrep bandit bandit-sarif-formatter njsscan checkov requests + curl -sSL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin + trivy --version # --- SAST: Semgrep (мультиязычный) --- - name: Semgrep @@ -67,23 +69,15 @@ jobs: # --- SCA: Trivy filesystem (зависимости pip/npm) --- - name: Trivy filesystem (SCA) - uses: aquasecurity/trivy-action@0.28.0 continue-on-error: true - with: - scan-type: fs - scanners: vuln - format: sarif - output: ${{ env.REPORTS_DIR }}/trivy-fs.sarif - severity: MEDIUM,HIGH,CRITICAL + run: | + trivy fs --scanners vuln --severity MEDIUM,HIGH,CRITICAL --format sarif --output "$REPORTS_DIR/trivy-fs.sarif" . || true # --- IaC / Dockerfile misconfig: Trivy config --- - name: Trivy config (IaC) - uses: aquasecurity/trivy-action@0.28.0 continue-on-error: true - with: - scan-type: config - format: sarif - output: ${{ env.REPORTS_DIR }}/trivy-config.sarif + run: | + trivy config --format sarif --output "$REPORTS_DIR/trivy-config.sarif" . || true # --- IaC: Checkov (docker-compose / Dockerfile) --- - name: Checkov From 9ef6ad607647065e20a9d077bfbab6cd5e94e7b0 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 16:48:58 +0000 Subject: [PATCH 3/8] fix: add product_type_name for DefectDojo auto_create_context --- security/dojo_upload.py | 1 + 1 file changed, 1 insertion(+) diff --git a/security/dojo_upload.py b/security/dojo_upload.py index 7273e359953..f8d46a269c9 100644 --- a/security/dojo_upload.py +++ b/security/dojo_upload.py @@ -47,6 +47,7 @@ def upload(url, token, product, engagement, file_path, scan_type): data = { "scan_type": scan_type, "product_name": product, + "product_type_name": "Research", "engagement_name": engagement, "auto_create_context": "true", "active": "true", From 5f08c80989cbd15f26e64ea19e6ecd261901b840 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 17:25:46 +0000 Subject: [PATCH 4/8] dast: run on self-hosted runner --- .github/workflows/dast.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dast.yml b/.github/workflows/dast.yml index e9ef10cf10c..ad675fcb2b3 100644 --- a/.github/workflows/dast.yml +++ b/.github/workflows/dast.yml @@ -22,7 +22,7 @@ env: jobs: dast: name: OWASP ZAP - runs-on: ubuntu-latest + runs-on: self-hosted steps: - uses: actions/checkout@v4 From 05919049e270756c78898e74dc912e5b3f77bb9d Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 17:31:22 +0000 Subject: [PATCH 5/8] dast: trigger on devsecops-pipeline branch for testing --- .github/workflows/dast.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dast.yml b/.github/workflows/dast.yml index ad675fcb2b3..b03a21f455d 100644 --- a/.github/workflows/dast.yml +++ b/.github/workflows/dast.yml @@ -8,7 +8,7 @@ on: schedule: - cron: "0 2 * * 1" # еженедельно, пн 02:00 UTC push: - branches: [ main, master ] + branches: [ main, master, devsecops-pipeline ] permissions: contents: read From 36ead97e7dcbc1ec9c1227b9535635acc39cfce6 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 17:58:13 +0000 Subject: [PATCH 6/8] dast: use python3/pip3 on self-hosted runner --- .github/workflows/dast.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dast.yml b/.github/workflows/dast.yml index b03a21f455d..1e10d2e04aa 100644 --- a/.github/workflows/dast.yml +++ b/.github/workflows/dast.yml @@ -103,15 +103,15 @@ jobs: DOJO_PRODUCT: "DefectDojo (self)" DOJO_ENGAGEMENT: "DAST ${{ github.ref_name }}" run: | - pip install requests - python security/dojo_upload.py --stage dast + python3 -m pip install requests + python3 security/dojo_upload.py --stage dast - name: Security Gateway (DAST) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GATE_FAIL_ON: CRITICAL # DAST шумит — блокируем только на critical run: | - python security/gate.py --reports "$REPORTS_DIR" + python3 security/gate.py --reports "$REPORTS_DIR" - name: Upload artifacts if: always() From 47fced5db9bf05c4c0d68835fad3a8efa806e371 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 19:08:58 +0000 Subject: [PATCH 7/8] docs: remove docx from repository --- ...20\277\320\270\321\201\320\272\320\260.docx" | Bin 14852 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 "docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" diff --git "a/docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" "b/docs/\320\237\320\276\321\217\321\201\320\275\320\270\321\202\320\265\320\273\321\214\320\275\320\260\321\217_\320\267\320\260\320\277\320\270\321\201\320\272\320\260.docx" deleted file mode 100644 index 7ef4a4b55243b5ce21e11c0abddf581c1c39d17e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14852 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 From f045347db8d67932d3dcbbd47b532e476f806fe0 Mon Sep 17 00:00:00 2001 From: Drewplonq Date: Sun, 23 Aug 2026 19:14:05 +0000 Subject: [PATCH 8/8] docs: remove internal setup notes, keep analytical docs --- docs/FORK_MAPPING.md | 33 --------------------------- docs/PIPELINE_README.md | 50 ----------------------------------------- 2 files changed, 83 deletions(-) delete mode 100644 docs/FORK_MAPPING.md delete mode 100644 docs/PIPELINE_README.md diff --git a/docs/FORK_MAPPING.md b/docs/FORK_MAPPING.md deleted file mode 100644 index f20e0c663b6..00000000000 --- a/docs/FORK_MAPPING.md +++ /dev/null @@ -1,33 +0,0 @@ -# Привязка к раскладке форка DefectDojo - -Пайплайн настроен под стандартную структуру upstream-репозитория DefectDojo. -Перед запуском сверь пункты ниже со своим форком и поправь при расхождении. - -## Предполагаемая раскладка - -| Что | Значение по умолчанию | Где используется | -|---|---|---| -| Dockerfile'ы | `Dockerfile.django-alpine`, `Dockerfile.nginx-alpine`, `*-debian`, `Dockerfile.integration-tests-debian` | Hadolint (по маске, менять не нужно) | -| Подъём сервиса | `./dc-build.sh` + `./dc-up-d.sh postgres-redis` | `dast.yml` | -| URL мишени | `http://localhost:8080` | `dast.yml`, `.zap/zap-auth.yaml` | -| Страница логина | `/login`, поле CSRF `csrfmiddlewaretoken` | browser-auth ZAP (CSRF решается автоматически) | -| Языки | Python, JavaScript/Vue, SCSS, Dockerfile, shell | матрица покрытия SAST | - -## Чек-лист перед сдачей - -1. **Dockerfile'ы.** Hadolint берёт все файлы по маске `Dockerfile*` через `git ls-files` — - ручной правки не требует. Убедись, что твои Dockerfile попадают в маску. -2. **Порт.** Если nginx опубликован не на `8080`, поменяй `TARGET_URL` в `dast.yml` и - `urls`/`loginPageUrl` в `.zap/zap-auth.yaml`. -3. **scan_type.** Все SARIF грузятся парсером `SARIF`, ZAP — `ZAP Scan`. Сверь, что оба - парсера есть в твоей версии DefectDojo: `GET {DOJO_URL}/api/v2/test_types/`. -4. **Тестовый пользователь ZAP.** Заведи `zaptester` в мишени и положи `ZAP_USER`/`ZAP_PASSWORD` - в секреты репозитория. -5. **Профиль БД.** Если compose стартует иначе (другой профиль/скрипт) — поправь шаг - «Build & start DefectDojo» в `dast.yml`. -6. **Версии Actions.** Зафиксируй теги (лучше — commit SHA) в обоих workflow. - -## Что править НЕ нужно - -- `gate.py` — универсален, работает с любыми SARIF в каталоге `reports/`. -- Матрица покрытия SAST — Semgrep + Bandit + njsscan + Trivy покрывают все языки форка. diff --git a/docs/PIPELINE_README.md b/docs/PIPELINE_README.md deleted file mode 100644 index dd72f0f2c19..00000000000 --- a/docs/PIPELINE_README.md +++ /dev/null @@ -1,50 +0,0 @@ -# DevSecOps starter kit для форка DefectDojo - -Комплект файлов для безопасного CI/CD-пайплайна поверх -[DefectDojo](https://github.com/DefectDojo/django-DefectDojo) на GitHub Actions. - -## Структура - -``` -.github/workflows/ - security.yml # SAST + SCA + секреты + IaC/образы + Security Gateway - dast.yml # OWASP ZAP baseline + full против запущенного DefectDojo -security/ - gate.py # Security Gateway: агрегация SARIF, блок релиза, комментарий в PR - dojo_upload.py # выгрузка отчётов в отдельный DefectDojo (менеджмент) -.zap/ - rules.tsv # подавление шума ZAP - zap-auth.yaml # шаблон аутентифицированного скана (зона роста) -docs/ - DEVSECOPS.md # документация процесса + аналитика выбора инструментов -``` - -## Как поднять - -1. Форкни DefectDojo и скопируй сюда каталоги `.github/`, `security/`, `.zap/`, `docs/`. -2. На выданной VPS подними **отдельный** инстанс DefectDojo как систему менеджмента - (по официальной инструкции: `./dc-up.sh postgres-redis`). Создай в нём API-токен. -3. В настройках репозитория (Settings → Secrets and variables → Actions) добавь: - - `DOJO_URL` — например `https://:8080` - - `DOJO_TOKEN` — API v2 Key из профиля DefectDojo - - (для аутентифицированного DAST) `ZAP_USER`, `ZAP_PASSWORD` -4. Запусти workflow `security` (на push/PR) и `dast` (вручную или по расписанию). -5. Проверь результаты в трёх местах: вкладка **Security** репозитория, комментарий - в **Pull Request**, дашборд **DefectDojo**. - -## Раскатка (этап 1 — облако/VPS) - -- Пайплайн исполняется на GitHub-hosted раннерах по умолчанию. -- Чтобы задействовать выданную VPS как исполнителя, зарегистрируй на ней - self-hosted runner (Settings → Actions → Runners) и замени в workflow - `runs-on: ubuntu-latest` на `runs-on: self-hosted`. -- DAST-инстанс мишени и DefectDojo-менеджмент разворачиваются на VPS через - их штатный `docker-compose`. - -## Важные оговорки - -- Версии сторонних GitHub Actions в workflow — ориентировочные. Перед сдачей - зафиксируй их и по возможности пиннингуй к commit SHA (это отдельный плюс к защите). -- Строки `scan_type` в `dojo_upload.py` должны совпадать с парсерами твоей версии - DefectDojo — сверься с `GET {DOJO_URL}/api/v2/test_types/`. -- Пороги гейта настраиваются переменной `GATE_FAIL_ON` (по умолчанию `HIGH`).