GitLab KEV: патчить мало, если не проверены CI-секреты и журналы доступа
Virusologia briefing: проблема, доказательства, defensive-контроль и ретест.
Коротко: Критические уязвимости в DevSecOps-платформах требуют не только обновления версии. Если атакующий мог читать данные или трогать repository API, после патча нужна проверка секретов, runners и audit trail.

Проблема

GitLab, GitHub Enterprise, CI runners и package registries давно стали частью Tier-0. Через них проходят исходники, deploy keys, tokens, container images и история изменений.

Поэтому критический баг в такой платформе нельзя закрывать формулой patch applied. Нужно доказать, что до патча не было чтения чувствительных файлов, доступа к секретам, подозрительных pipeline-запусков и новых токенов.

Что проверить

  • Версию GitLab и наличие всех security releases на self-managed инстансе.
  • Access logs и audit events за окно до обновления.
  • Создание deploy tokens, personal access tokens, runners, webhooks и integrations.
  • Pipeline variables, masked secrets, protected branches и registry credentials.
  • Необычные обращения к API, repository archives, raw files и commits endpoints.

Решение

Правильный response: upgrade, freeze sensitive admin changes, собрать evidence, провести scoped rotation, проверить runners, затем открыть change window обратно.

Для бизнеса важно объяснить не CVSS, а blast radius: какие проекты могли быть затронуты, какие секреты там жили и какие релизы собирались через эту цепочку.

Defensive-чеклист

  • Обновить до исправленной версии и зафиксировать digest/версию.
  • Сохранить журналы до ротации, чтобы не потерять forensic context.
  • Ротировать секреты по приоритету: production deploy, cloud, registry, signing, then lower-tier.
  • Проверить CI runners на неизвестные jobs и новые registration tokens.
  • Добавить KEV-политику: DevSecOps-система = emergency class, а не ordinary web app.

Безопасный пример

# пример безопасной инвентаризации GitLab audit JSONL без вывода секретов
import json
from collections import Counter

events = Counter()
with open("gitlab-audit.jsonl", encoding="utf-8") as fh:
    for line in fh:
        row = json.loads(line)
        action = row.get("action", "unknown")
        target = row.get("target_type", "unknown")
        events[(action, target)] += 1

for (action, target), count in events.most_common(20):
    print(f"{count:5}  {action:32} {target}")
Этическая рамка: материал предназначен для defensive security, легального аудита собственных систем, обучения и подготовки remediation. Он не содержит эксплуатационных цепочек, вредоносных payload или инструкций по обходу защиты.