# Sprint 5 «Pre-prod tooling» Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Активировать phase-3 tooling, не требующий YC-инфраструктуры: Semgrep SAST (#25) + Semgrep MCP + Dependabot (#27); подготовить Trivy workflow (#26) к будущей активации после Docker pipeline в YC.

**Architecture:** Три независимых потока — (1) Dependabot: native GitHub config, без кода; (2) Semgrep: `npm run sast` в корне + CI workflow + MCP-запись в `.mcp.json` + allow в `.claude/settings.json`; (3) Trivy prep: workflow-файл создан, но отключён (`if: false`) до Sprint 7.

**Tech Stack:** Semgrep CLI (pip/CI), GitHub Actions, Dependabot native, Trivy (CI-only), MCP JSON config.

---

## Карта файлов

| Файл | Действие | Назначение |
|------|----------|------------|
| `.github/dependabot.yml` | Создать | Dependabot: npm×2 (root + app) + composer (app) |
| `.github/workflows/sast.yml` | Создать | Semgrep SAST на push/PR в main |
| `.github/workflows/trivy.yml` | Создать | Trivy Docker scan — отключён до YC (`if: false`) |
| `.semgrep.yml` | Создать | Semgrep: пути + исключения |
| `trivy.yaml` | Создать | Trivy config — готов к активации |
| `package.json` (корень) | Изменить | Добавить скрипт `"sast"` |
| `.mcp.json` | Изменить | Добавить запись Semgrep MCP server |
| `.claude/settings.json` | Изменить | Allow `Bash(npm run sast:*)` |

---

## Task 1: Dependabot — `.github/dependabot.yml`

Dependabot создаёт автоматические PR для обновлений зависимостей прямо через GitHub (без кода на стороне проекта). Существующий `.github/workflows/dependency-check.yml` остаётся — он выполняет другую задачу (уведомление о устаревших версиях), не создаёт PR.

**Files:**

- Create: `.github/dependabot.yml`

- [ ] **Step 1: Создать `.github/dependabot.yml`**

```yaml
# Dependabot — Лидерра (#27)
# Docs: https://docs.github.com/code-security/dependabot/dependabot-version-updates
# Создаёт PR автоматически при выходе обновлений зависимостей.
# CI-workflow dependency-check.yml — параллельный, оставить (разные задачи).
version: 2

updates:
  # Root package.json: markdownlint-cli2, cspell, pa11y-ci, stylelint, lefthook, npm-run-all2
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
      day: monday
      time: "09:00"
      timezone: Europe/Moscow
    open-pull-requests-limit: 5
    labels:
      - dependencies
    groups:
      dev-tools:
        patterns:
          - "markdownlint-cli2"
          - "cspell"
          - "@cspell/*"
          - "pa11y*"
          - "stylelint*"
          - "lefthook"
          - "npm-run-all2"

  # app/package.json: Vue, Vuetify, Vite, Vitest, ESLint, Prettier, Histoire, etc.
  - package-ecosystem: npm
    directory: /app
    schedule:
      interval: weekly
      day: monday
      time: "09:00"
      timezone: Europe/Moscow
    open-pull-requests-limit: 10
    labels:
      - dependencies
    groups:
      vue-ecosystem:
        patterns:
          - "vue"
          - "vue-*"
          - "@vue/*"
          - "vuetify"
          - "vite"
          - "vite-*"
          - "@vitejs/*"
          - "vite-plugin-*"
          - "laravel-vite-plugin"
      vitest:
        patterns:
          - "vitest"
          - "@vitest/*"
      eslint:
        patterns:
          - "eslint"
          - "eslint-*"
          - "@eslint/*"
          - "prettier"
          - "eslint-config-prettier"
      histoire:
        patterns:
          - "histoire"
          - "@histoire/*"

  # app/composer.json: Laravel 13, Pest 4, Pint, Larastan, Boost, IDE Helper, etc.
  - package-ecosystem: composer
    directory: /app
    schedule:
      interval: weekly
      day: monday
      time: "09:00"
      timezone: Europe/Moscow
    open-pull-requests-limit: 10
    labels:
      - dependencies
    groups:
      laravel-framework:
        patterns:
          - "laravel/framework"
          - "laravel/sanctum"
          - "laravel/tinker"
          - "laravel/pail"
          - "laravel/pao"
          - "nunomaduro/collision"
      pest:
        patterns:
          - "pestphp/*"
      dev-tools:
        patterns:
          - "larastan/larastan"
          - "barryvdh/laravel-ide-helper"
          - "fakerphp/faker"
          - "laravel/pint"
          - "mockery/mockery"
```

- [ ] **Step 2: Commit**

```bash
git add .github/dependabot.yml
git commit -m "chore(deps): add Dependabot config — npm×2 + composer weekly (#27)"
```

- [ ] **Step 3: Проверить на GitHub после push**

После `git push origin main`: открыть GitHub → репозиторий → tab **Insights** → **Dependency graph** → **Dependabot**.  
Expected: 3 экосистемы (`npm /`, `npm /app`, `composer /app`). Первые PR появятся в течение 24 часов.

---

## Task 2: Semgrep config + `npm run sast`

Semgrep — статический анализ безопасности (SAST). Запускается в CI (`ubuntu-latest`), поэтому `npm run sast` не требует локальной установки Python на dev-машине.  
**На Windows dev:** `semgrep` недоступен без Python 3.9+. Локально пропустить; для ручного запуска — `choco install python` + `pip install semgrep`.

**Files:**

- Create: `.semgrep.yml`
- Modify: `package.json` (корень) — добавить скрипт `"sast"`

- [ ] **Step 1: Создать `.semgrep.yml`**

```yaml
# Semgrep ruleset — Лидерра (#25)
# Docs: https://semgrep.dev/docs/writing-rules/rule-syntax
# Локально: npm run sast  (требует pip install semgrep)
# CI: .github/workflows/sast.yml  (ubuntu-latest, без установки)

rules: []  # custom rules — пустые; используем облачные рулсеты через --config p/...

paths:
  include:
    - app/app
    - app/resources/js
    - app/database/migrations
  exclude:
    - app/vendor
    - app/node_modules
    - app/storage
    - "**/*.min.js"
    - "**/*.min.css"
    - "**/_ide_helper*.php"
    - "**/phpstan-baseline.neon"
```

- [ ] **Step 2: Добавить скрипт `"sast"` в корневой `package.json`**

Открыть `package.json` в корне (не в `app/`). Текущий раздел `scripts`:

```json
"scripts": {
    "lint:md": "...",
    "lint:md:fix": "...",
    "spell": "...",
    "links": "...",
    "lint:css": "...",
    "lint:sql": "...",
    "format:sql:check": "...",
    "format:sql": "...",
    "a11y": "...",
    "check:docs": "run-p lint:md spell links a11y"
}
```

Добавить `"sast"` после `"check:docs"`:

```json
    "check:docs": "run-p lint:md spell links a11y",
    "sast": "semgrep --config=p/php --config=p/javascript --config=p/typescript --config=p/secrets --config=.semgrep.yml --error --time"
```

Итоговый scripts-раздел должен быть:

```json
"scripts": {
    "lint:md": "markdownlint-cli2 \"docs/**/*.md\" \"db/**/*.md\" \"*.md\"",
    "lint:md:fix": "markdownlint-cli2 --fix \"docs/**/*.md\" \"db/**/*.md\" \"*.md\"",
    "spell": "cspell --no-progress --show-suggestions \"docs/**/*.md\" \"db/**/*.md\" \"*.md\" \"web/**/*.html\"",
    "links": "bin\\lychee.exe --config .lychee.toml \"docs/**/*.md\" \"db/**/*.md\" \"*.md\"",
    "lint:css": "stylelint \"web/**/*.html\"",
    "lint:sql": "bin\\squawk.exe db/schema.sql",
    "format:sql:check": "perl -I bin/pgFormatter/lib bin/pgFormatter/pg_format db/schema.sql > db/.schema-formatted.tmp.sql && diff -q db/schema.sql db/.schema-formatted.tmp.sql || echo \"pgFormatter would reformat — run npm run format:sql to apply\"",
    "format:sql": "perl -I bin/pgFormatter/lib bin/pgFormatter/pg_format -o db/schema.sql.formatted db/schema.sql && echo \"Wrote db/schema.sql.formatted — review diff before replacing source\"",
    "a11y": "pa11y-ci --config pa11y.config.json",
    "check:docs": "run-p lint:md spell links a11y",
    "sast": "semgrep --config=p/php --config=p/javascript --config=p/typescript --config=p/secrets --config=.semgrep.yml --error --time"
}
```

- [ ] **Step 3: Commit**

```bash
git add .semgrep.yml package.json
git commit -m "feat(tooling): Semgrep config + npm run sast script (#25)"
```

---

## Task 3: Semgrep CI workflow — `.github/workflows/sast.yml`

**Files:**

- Create: `.github/workflows/sast.yml`

- [ ] **Step 1: Создать `.github/workflows/sast.yml`**

```yaml
name: SAST — Semgrep

on:
  push:
    branches: [main]
    paths:
      - 'app/app/**'
      - 'app/resources/js/**'
      - 'app/database/migrations/**'
      - '.semgrep.yml'
      - '.github/workflows/sast.yml'
  pull_request:
    branches: [main]
    paths:
      - 'app/app/**'
      - 'app/resources/js/**'
      - 'app/database/migrations/**'

permissions:
  contents: read
  security-events: write  # нужно для upload-sarif

jobs:
  semgrep:
    runs-on: ubuntu-latest
    name: Semgrep SAST scan

    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/php
            p/javascript
            p/typescript
            p/secrets
        env:
          # SEMGREP_APP_TOKEN — опциональный, для Semgrep Cloud dashboard.
          # Без него: open-source режим, результаты только в GitHub Security tab.
          # Добавить: GitHub → Settings → Secrets → Actions → SEMGREP_APP_TOKEN
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

      - name: Upload SARIF to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: semgrep.sarif
        continue-on-error: true  # не блокировать PR если SARIF upload упал
```

**Важно — первый запуск:** Semgrep может найти реальные проблемы в существующем коде (не только новом). При первом прогоне нужен тriage:

1. Смотреть вкладку **Security → Code scanning alerts** на GitHub.
2. Ложные срабатывания — помечать «Dismissed» с пояснением.
3. Реальные находки P0/P1 — исправлять до merge.

- [ ] **Step 2: Commit**

```bash
git add .github/workflows/sast.yml
git commit -m "feat(ci): Semgrep SAST workflow — push/PR to main (#25)"
```

---

## Task 4: Semgrep MCP server — `.mcp.json`

Semgrep MCP даёт Claude Code инструменты для семантического поиска по Semgrep-правилам прямо в разговоре (аналитика кода, поиск паттернов безопасности).

**Files:**

- Modify: `.mcp.json`

- [ ] **Step 1: Добавить запись `semgrep` в `.mcp.json`**

Текущий `.mcp.json` содержит ключи `playwright`, `github`, `laravel-boost`. Добавить `semgrep` четвёртым:

```json
{
  "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/schemas/mcp.json",
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["-y", "@playwright/mcp@latest"],
      "comment": "Фаза 0 #2 — открыть web/*.html, screenshot, проверка интерактива"
    },
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp",
      "headers": {
        "Authorization": "Bearer ${GITHUB_TOKEN}"
      },
      "comment": "Фаза 0 #3 — официальный hosted GitHub MCP (https://github.com/github/github-mcp-server). Требует env GITHUB_TOKEN с PAT (scopes: repo, read:org, не давать admin/delete). Раньше использовали deprecated @modelcontextprotocol/server-github — заменён 06.05.2026."
    },
    "laravel-boost": {
      "command": "php",
      "args": ["app/artisan", "boost:mcp"],
      "comment": "Фаза 1 #10 — Laravel Boost MCP (laravel/boost v2.4.6). Заменяет PostgreSQL MCP из фазы 0. Через Roster детектит установленные пакеты (Laravel 13, Pest 4, Pint, Larastan, IDE Helper) и серверит соответствующие guidelines + DB query/schema/tinker tools. Кастомный Vuetify 3 guideline — в app/.ai/guidelines/vuetify.md. Конфиг wizard'а — app/boost.json (написан вручную: wizard сломан на кириллице-пути)."
    },
    "semgrep": {
      "command": "npx",
      "args": ["-y", "semgrep-mcp"],
      "comment": "Фаза 3 #25 — Semgrep MCP (SAST). Семантический поиск/анализ кода через Semgrep rules в Claude Code. Пакет: npmjs.com/package/semgrep-mcp — если 404, запустить 'npm search semgrep mcp' для актуального имени."
    }
  }
}
```

- [ ] **Step 2: Проверить подключение MCP**

Перезапустить Claude Code. Выполнить в разговоре:

```
/mcp
```

Expected: в списке серверов появился `semgrep`. Если статус `error` вместо `connected` — пакет `semgrep-mcp` не найден на npm, нужно уточнить имя:

```bash
npm search semgrep mcp --json | Select-Object -First 5
```

Заменить `semgrep-mcp` в `.mcp.json` на найденное имя пакета.

- [ ] **Step 3: Commit**

```bash
git add .mcp.json
git commit -m "feat(tooling): Semgrep MCP server in .mcp.json (#25)"
```

---

## Task 5: Allow `npm run sast` в `.claude/settings.json`

**Files:**

- Modify: `.claude/settings.json`

- [ ] **Step 1: Добавить разрешение в `.claude/settings.json`**

Текущий `permissions.allow` содержит `"Bash(npm run check:docs:*)"` и аналогичные строки. Добавить `"Bash(npm run sast:*)"` рядом с остальными npm-скриптами:

```json
"allow": [
  "Bash(npm install:*)",
  "Bash(npm run lint:md:*)",
  "Bash(npm run spell:*)",
  "Bash(npm run links:*)",
  "Bash(npm run lint:css:*)",
  "Bash(npm run a11y:*)",
  "Bash(npm run check:docs:*)",
  "Bash(npm run lint:md:fix:*)",
  "Bash(npm run sast:*)",
  "Bash(git status)",
  ...
]
```

- [ ] **Step 2: Commit**

```bash
git add .claude/settings.json
git commit -m "chore(claude): allow npm run sast in settings.json (#25)"
```

---

## Task 6: Trivy prep — workflow + config (disabled)

Trivy сканирует Docker-образы на CVE. Docker pipeline будет настроен в Sprint 7 (YC Container Registry). До тех пор — файлы готовы, workflow отключён через `if: false`.

**Files:**

- Create: `trivy.yaml`
- Create: `.github/workflows/trivy.yml`

- [ ] **Step 1: Создать `trivy.yaml`**

```yaml
# Trivy config — Лидерра (#26)
# Активировать в Sprint 7 после настройки Docker pipeline в YC.
# Docs: https://aquasecurity.github.io/trivy/latest/docs/configuration/

exit-code: 1
severity: CRITICAL,HIGH
format: sarif
output: trivy-results.sarif
ignore-unfixed: true

vulnerability:
  type:
    - os
    - library
```

- [ ] **Step 2: Создать `.github/workflows/trivy.yml` (disabled)**

```yaml
name: Trivy — Docker image scan

# ОТКЛЮЧЕНО до Sprint 7 (YC Docker pipeline).
# Для активации:
#   1. Убрать `if: false` у job trivy
#   2. Добавить GitHub secret YC_REGISTRY (полный адрес, напр. cr.yandex/crp.../liderra)
#   3. Убедиться, что CI job собирает образ перед этим workflow
# См. roadmap Sprint 7 «YC infrastructure setup».

on:
  push:
    branches: [main]
    paths:
      - 'Dockerfile'
      - 'docker-compose*.yml'
      - '.github/workflows/trivy.yml'
      - 'trivy.yaml'
  schedule:
    - cron: '0 10 * * 1'  # каждый понедельник 10:00 UTC

permissions:
  contents: read
  security-events: write

jobs:
  trivy:
    runs-on: ubuntu-latest
    if: false  # TODO Sprint 7: убрать после настройки Docker pipeline

    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy image scan
        uses: aquasecurity/trivy-action@0.30.0
        with:
          image-ref: '${{ secrets.YC_REGISTRY }}:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'
          ignore-unfixed: true
          vuln-type: 'os,library'
          trivy-config: 'trivy.yaml'

      - name: Upload Trivy SARIF to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif
```

- [ ] **Step 3: Commit**

```bash
git add trivy.yaml .github/workflows/trivy.yml
git commit -m "feat(tooling): Trivy CI workflow prep — disabled until YC Docker (#26)"
```

---

## Task 7: Push и тriage первого Semgrep прогона

- [ ] **Step 1: Push всех коммитов**

```bash
git push origin main
```

- [ ] **Step 2: Наблюдать за CI**

Открыть GitHub → Actions → workflow **SAST — Semgrep**. Ждать завершения (~2–3 мин).

- [ ] **Step 3: Тriage Security alerts**

Открыть GitHub → Security → **Code scanning** (появляется после первого upload-sarif).

Алгоритм тriage:

- **CRITICAL/HIGH** с реальным уязвимым паттерном → создать Issue, исправить в ближайшем спринте.
- **Ложное срабатывание** (напр., флаг в тесте, mock-данные) → Dismiss с пояснением «test/mock data».
- **MEDIUM/LOW** → Dismiss как «acceptable risk» или создать тикет Post-MVP.

- [ ] **Step 4: Проверить Dependabot**

GitHub → Insights → Dependency graph → Dependabot. Expected: 3 экосистемы зарегистрированы.

---

## Self-Review

### Spec coverage

| Требование roadmap Sprint 5 | Task | Статус |
|-----------------------------|------|--------|
| #25 Semgrep local `npm run sast` | Task 2 | ✅ |
| #25 Semgrep CI workflow | Task 3 | ✅ |
| #25 Semgrep MCP | Task 4 | ✅ |
| #26 Trivy prep (disabled until YC) | Task 6 | ✅ |
| #27 Dependabot | Task 1 | ✅ |
| Allow `npm run sast` в settings | Task 5 | ✅ |
| Push + первый тriage | Task 7 | ✅ |

### Placeholder scan

- Task 2 Step 2: показан полный итоговый блок `scripts` целиком ✅
- Task 4 Step 1: полный `.mcp.json` показан, не «добавить что-то» ✅
- Task 4 Step 2: конкретная команда для поиска пакета если npm 404 ✅
- Task 6 Step 2: TODO с конкретными инструкциями активации, не просто «активировать потом» ✅
- Task 7 Step 3: конкретный алгоритм тriage по severity ✅

### Type consistency

Конфигурационные файлы — без типов. Имена файлов, скриптов и ключей согласованы по всем задачам: `.semgrep.yml` / `p/php p/javascript p/typescript p/secrets` — одинаковый набор в Task 2 и Task 3. `trivy.yaml` + `trivy-config: 'trivy.yaml'` в workflow совпадают ✅
