Cтатья будет полезна разработчикам и инженерам по ИБ, желающим повысить уровень безопасности приложений за счет внедрения проверок, запрещающих вносить в ПО сторонние компоненты с известными уязвимостями.

Автор:

Голяков Сергей

DevSecOps, в информационной безопасности с 2013 года, в СПАО «‎Ингосстрах»‎ встраиваю безопасность в процесс разработки

При написании этой статьи я вновь пришел к мысли, что каждый ИТ-шник и ИБ-шник должен быть хоть чуточку разработчиком для более эффективного решения своих задач.

Проблематика

Разработчики программного обеспечения часто применяют уязвимые зависимости, особенно на первых этапах разработки нового проекта:

Скрытый текст
Пример сработки композиционного анализа в PR
Пример сработки композиционного анализа в PR

Когда возможна угроза использования известных уязвимостей в зависимых компонентах, то это приводит к тому, что ПО становится уязвимым.

Помощник Тим и уязвимости

Чтобы привлечь внимание разработчиков к данной проблеме, мы используем нашего корпоративного кота Тим. Познакомиться с ним и его друзьями можно в нашем блоге: В самое сердечко: ребрендинг ИТ «Ингосстраха».

Кот Тим
Кот Тим

Тим способен испытывать разные эмоции, например:

[string] $ingoCatImage = "" # Определяем эмоцию кота Фича:
if($failed_vulns.Count -gt 30){ # Выявлено более 30 CVE
    $ingoCatImage = "![Sadness IngoCat](<ссылка на картинку> ""Sadness IngoCat"")"
}
elseif($failed_vulns.Count -gt 20)
{
    $ingoCatImage = "![Evil IngoCat](<ссылка на картинку> ""Evil IngoCat"")"
}elseif($failed_vulns.Count -gt 10)
{
    $ingoCatImage = "![Annoyance IngoCat](<ссылка на картинку> ""Annoyance IngoCat"")"
}elseif($failed_vulns.Count -gt 0)
{
    $ingoCatImage = "![Surprised IngoCat](<ссылка на картинку> ""Surprised IngoCat"")"
}

Как уже вы догадались, эмоции Тима варьируются от количества выявленных уязвимостей. Когда много уязвимостей где-то страдает один котик Тим ☹ .

Тим устал напоминать об уязвимостях
Тим устал напоминать об уязвимостях

Композиционный анализ

Композиционный анализ представляет собой процесс построения дерева зависимостей программного обеспечения и идентификацию известных уязвимостей на основе этого дерева.

Рассмотрим пример известной (опубликованной) уязвимости в пакете org.yaml:snakeyaml@1.33, используемой в качестве зависимости.

CVE-2022-1471

Оригинальное описание уязвимости:

SnakeYaml's Constructor() class does not restrict types which can be instantiated during deserialization. Deserializing yaml content provided by an attacker can lead to remote code execution. We recommend using SnakeYaml's SafeConsturctor when parsing untrusted content to restrict deserialization. We recommend upgrading to version 2.0 and beyond.

CVE ID

CVE-2022-1471

GHSA ID

GHSA-mjmj-j48q-9wg2

CWEs

CWE-20: Improper Input Validation
CWE-502: Deserialization of Untrusted Data

Опубликована

01.12.2022

Уточнена

19.11.2023

Критичность (Score) по шкале CVSS v. 3

9.8 / 10 - Critical

Проще говоря, если злоумышленнику удастся воспользоваться данной уязвимостью - будет плохо, а именно злоумышленник может выполнить нужный ему код на сервере от имени конечного ПО в котором используется org.yaml:snakeyaml@1.33.

Часто бывает так, что разработчики, особенно из подрядных организаций, предоставляя нам готовое ПО или дорабатывая имеющееся ПО, применяют не самые «безопасные» версии заимствованных компонентов.

Консольный агент CodeScoring

Существует широкий спектр решений для выполнения композиционного анализа. В рамках стратегии импортозамещения можно рассмотреть интеграцию CodeScoring в процесс проверки каждого pull request в защищённых ветвях кода.

CodeScoring предлагает различные методы анализа репозиториев. В данной статье мы сосредоточимся исключительно на функционале, который может быть представлен консольным агентом Johnny в максимально доступной и понятной форме - это информация об уязвимостях:

Пример некоторых возможностей CodeScoring
Пример некоторых возможностей CodeScoring

Согласно документации, консольный агент Johnny обладает широкими возможностями, но мы сосредоточимся на одной из функций - создание дерева зависимостей и формирование SBOM.

SBOM

Software Bill of Materials - это список:

  • зависимостей с подробной информацией:

    Пример информации о зависимости ch.qos.logback/logback-classic@1.2.11
    Пример информации о зависимости ch.qos.logback/logback-classic@1.2.11
  • лицензий под которой опубликована зависимость и ее компоненты:

    Пример лицензий ch.qos.logback/logback-classic@1.2.11
    Пример лицензий ch.qos.logback/logback-classic@1.2.11
  • имеющихся известных уязвимостях в зависимостях:

    Пример уязвимостей ch.qos.logback/logback-classic@1.2.11
    Пример уязвимостей ch.qos.logback/logback-classic@1.2.11

Проще говоря, SBOM можно сравнить с этикеткой на упаковке товара в магазине, но с более обширной информацией о компонентах и даже о «плохих».

Я встречал SBOM объемом до десятков мегабайт, только представьте, сколько строк может содержаться в таком файле…

Применение SBOM от CodeScoring в pull request

Методика обработки SBOM

Для того, чтобы обработать SBOM от CodeScoring при проверке каждого pull request нам потребуется:

  • Запустить консольный плагин Johnny, передав ему папку с исходным кодом в качестве аргумента;

  • Johnny найдет среди поддерживаемых манифестов информацию, которая нас интересует, и отправит дерево зависимостей и lock-файлы на сервер CodeScoring;

  • CodeScoring осуществит композиционный анализ полученного дерева и вернет файл SBOM;

  • Изучить файл bom.json, полученный от Johnny;

  • Прикрепить bom.json в качестве артефакта нашей валидационной сборки;

  • Принять решение о блокировке pull request на основе информации из bom.json , предварительно исключив ранее внесенные уязвимости в исключения:

    • Заблокировать pull request;

    • Пропустить pull request, сохранив уязвимость.

Примерно так выглядел бы максимально упрощенный BPNM-процесс композиционного анализа:

Упрощённый процесс SCA и его место среди других проверок
Упрощённый процесс SCA и его место среди других проверок

В общем и целом, компонентная схема работы с CodeScoring представлена ниже:

Компонентная схема CodeScoring
Компонентная схема CodeScoring

Далее каждый элемент схемы и его действия будут раскрыты подробнее.

Как мы обрабатываем уязвимости в pull request

Об этом я уже писал в другой статье нашего блога: Внедряем Gitleaks для анализа pull request на наличие секретов в Azure DevOps Server. Если кратко, то наличие хотя бы одной уязвимости, выявленной в процессе анализа pull request, приводит к блокировке завершения pull request. Разработчик не сможет добавить уязвимый код, пока AppSec-инженер не внесет уязвимость в исключения или пока разработчик не обновит зависимость до версии, свободной от данной проблемы. Таким образом, мы гарантируем наименьшее количество известных уязвимостей в нашем ПО.

Кроме того, в упомянутой статье можно ознакомиться с тем, как мы осуществляем техническую реализацию указанного подхода к обработке pull request.

Вызываем Johnny

Для того, чтобы вызвать Johnny, необходимо в конвейере запустить скрипт, который уже запустит Johnny. В этом процессе валидационная сборка, инициируемая при создании pull request, вызывает нужный скрипт:

- task: Bash@3
  displayName: "Композиционный анализ"
  env:
    # Токен, выданный сервером, CodeScoring и передаваемый Johnny 
    token: $(token)
    # Уточняем какую версию Johnny использовать:
    johnny: johnny-linux-amd64-$(token)
  inputs:
    targetType: filePath
    filePath: $(System.DefaultWorkingDirectory)/папка/CodeScoring.sh
    arguments: $(token)
  continueOnError: false

Для вызова Johnny в CodeScoring.sh используется довольно простая конструкция:

ignore="--ignore .APK --ignore .IPA" # Список файлов, не сканируемых CodeScoring
command="$johnnyFilePath/$johnny scan dir
  "$SYSTEM_DEFAULTWORKINGDIRECTORY/$BUILD_REPOSITORY_NAME/"
  --api_token $token
  --api_url "https://site.domain"
  --project $SYSTEM_TEAMPROJECT/$BUILD_REPOSITORY_NAME
  --timeout 1200
  --stage dev
  $ignore

$command # Запускаем SCA при помощи CodeScoring johnny
# Далее идет проверка того, что SCA выполнился успешно, пропустим неинтересную часть

Johnny выполняет заданную команду и сохраняет файл bom.jsonв ту же папку, откуда был запущен. Вне зависимости от результата валидационной сборки, файл будет выглядеть следующим образом:

Пример прикрепления SBOM в артефакты сборки
Пример прикрепления SBOM в артефакты сборки

Как упоминалось ранее, в рамках данной статьи нас интересуют только уязвимости. Проверка рискованных лицензий или других вопросов не рассматривается. Следовательно, нас интересует раздел vulnerabilities файла SBOM:

Vulnerabilities из bom.json
Vulnerabilities из bom.json

Обрабатываем vulnerabilities из SBOM

Для того, чтобы получить список уязвимостей из SBOM, нам необходимо пробежаться по дереву и выбрать всю полезную информацию по каждой уязвимости.

Чтобы инициировать данную активность, запустим скрипт:

- task: PowerShell@2
  env:
    TFS_TOKEN: $(token)
  displayName: "Принятие решения"
  inputs:
    targetType: filePath
    filePath: $(System.DefaultWorkingDirectory)/папка/CodeScoring-failer.ps1
    arguments: >
      -reportPath $(System.DefaultWorkingDirectory) -Token $env:TFS_TOKEN
    # Где -reportPath - папка в которой искать bom.json и куда сохранять
    # какие-нибудь файлы
  continueOnError: false
  enabled: true

Собственно, именно часть DevOps в DevSecOps на этом заканчивается и начинается разработка.

CodeScoring-failer.ps1 начинает свою работу с того, что начинает обход секции vulnerabilities в SBOM:

if (!(Test-Path $SBOM_file_path -PathType leaf)){ # Проверка существования SBOM
    Write-Host "##[error]Файл отсутствует"
    # Фейлим сборку если файл не создался:
    Write-Host "##vso[task.complete result=SucceededWithIssues;]"
}
else
{
  # Подгружаем список исключений,
  # о которых ранее договорились с разработчиками репозитория
  # Пропустим данный код
    $SBOM_file = Get-Content $SBOM_file_path -Encoding UTF8 
    $CodeScoring_Report_Object = $SBOM_file | ConvertFrom-Json
    $vulnerabilities = $CodeScoring_Report_Object.vulnerabilities
    if($vulnerabilities.Count -gt 0)
    {
        Write-Host "##[command]Выполняю анализ уязвимостей" -ForegroundColor Blue
        $failed_vulns = [System.Collections.Generic.List[PSCustomObject]]::new()
        $isNotParsedVulns = $false # Есть ли уязвимости, для которых нет обработчика
        foreach($vuln in $vulnerabilities)
        {
            if($null -ne $vuln.recommendation -and $vuln.ratings.score -gt 5)
            {   # Вычисляем ссылки на базы уязвимостей
                $references_text = ""
                $references_Array = [System.Collections.Generic.List[PSCustomObject]]::new()
                if($null -ne $($vuln.references)){
                    foreach($ref in $($vuln.references)){
                        $references_text = $references_text + "$($ref.source.name) : [$($ref.id)]($($ref.source.url)) "
                        $references_Array.Add($($ref.id))
                    }
                }
                $isExludedIssue = $false # По умолчанию все уязвимости не исключены
                # Проверяем уязвимость на возможность исключения
                # Пропустим данный блок. По ходу проверки на исключения, значение
                # переменной $isExludedIssue может измениться на True
                if($isExludedIssue -eq $true){ # Если уязвимость исключена,сохраним ее
                    $excludedCount = $excludedCount + 1
                }else{ # Заполняем карточку уязвимости, уязвимость не исключена:
                  # Парсим PURL уязвимости
                  # Пропустим данный блок
                  foreach($rating in $($vuln.ratings)) # Выводим только полезную инфу
                  { # об уязвимости
                      # Раскрашиваем цвет риска CVSSv3
                      $severity = $rating.severity.ToUpper()
                      $score = $rating.score
                      if($severity -eq "critical"){
                          $severity = "<span style=`"color:DarkRed`">${severity}</span>"
                          $score = "<span style=`"color:DarkRed`">${score}</span>"
                      }
                      elseif($severity -eq "high"){
                          $severity = "<span style=`"color:Red`">${severity}</span>"
                          $score = "<span style=`"color:Red`">${score}</span>"
                      }
                      elseif($severity -eq "medium"){
                          $severity = "<span style=`"color:Orange`">${severity}</span>"
                          $score = "<span style=`"color:Orange`">${score}</span>"
                      }
                      elseif($severity -eq "low"){
                          $severity = "<span style=`"color:Green`">${severity}</span>"
                          $score = "<span style=`"color:Green`">${score}</span>"
                      }
                      elseif($severity -eq "none"){
                          $severity = "<span style=`"color:LightBlue`">${severity}</span>"
                          $score = "<span style=`"color:LightBlue`">${score}</span>"
                      }
                      # Заполняем уровень риска CVSSv3
                      if($rating.severity -and $rating.score -and $rating.method)
                      {
                          if($rating.severity -and $rating.severity -notlike "none" -and $rating.score -and $rating.method)
                          { # Есть и severity и score
                              $ratingString = $ratingString + " " + $rating.method + ": " + $severity + " (" + $score + ")"
                          }
                          elseif($rating.severity -and $rating.severity -notlike "none" -and ($null -eq $rating.score -or $rating.score -eq "") -and $rating.method)
                          { # Нет score, но есть severity
                              $ratingString = $ratingString + " " + $rating.method + ": " + $severity
                          } 
                          elseif($rating.score -and ($rating.severity -like "none" -or $null -eq $rating.severity -or $rating.severity -eq "") -and $rating.method)
                          { # Нет severity, но есть score
                              $ratingString = $ratingString + " " + $rating.method + ": " + $score
                          }
                      }
                  }
              }
              # Подчищаем описание уязвимости, чтобы marckdows в PR не ломался
              $vulnDescription = $($vuln.description).Replace("</", "'")
              $vulnDescription = $vulnDescription.Replace("<", "'")
              $vulnDescription = $vulnDescription.Replace(">", "'")
              $failed_check_object = [PSCustomObject]@{
                  id = $($vuln.id)
                  ratingString = $ratingString
                  description = $($vuln.description) -replace "<", "'"
                  references_text = $references_text
                  affects_text = $affects_text
                  artiURL = $artiURL
                  recommendation = $($vuln.recommendation)
                  artiURL_fixed = $artiURL_fixed
                  pkgName = $pkgName
                  pkgVersion = $pkgVersion
              }
              $failed_vulns.Add($failed_check_object)
                }
            }
        }
        # В переменную ниже будем записывать красивый текст с уязвимостями
        $vulns_text = [System.Collections.Generic.List[string]]::new()
}

Получив сведения о выявленной уязвимости, мы формируем текст комментария, который наш бот разместит в pull request по факту обнаружения этой проблемы:

Скрытый текст
foreach($vuln in $failed_vulns) # Формируем красивый markdown текст для PR
{
    $vulns_text.Add("> <span style=`"color:#0053bd`">**Идентификатор уязвимости**</span>: $($vuln.id)")
    $vulns_text.Add("<span style=`"color:#0053bd`">Критичность</span>: $($vuln.ratingString)")
    $vulns_text.Add("<span style=`"color:#0053bd`">Описание уязвимости</span>: $($vuln.description)")
    if($null -ne $($vuln.references_text))
    {
        $vulns_text.Add("<span style=`"color:#0053bd`">Полезные ссылки</span>: $($vuln.references_text)")
    }
    else{ Write-Host "<span style=`"color:#0053bd`">Полезные ссылки</span>: отсутствуют" }
    
    if($null -ne $($vuln.affects_text)) {
        $vulns_text.Add("<span style=`"color:#0053bd`">Затрагиваемые пакеты</span>: $($vuln.affects_text)")
    }
    else{
        Write-Host "<span style=`"color:#0053bd`">Затрагиваемые пакеты</span>: отсутствуют"
    }

    $vulns_text.Add("<span style=`"color:#0053bd`">Рекомендация</span>: повысить до [$($vuln.pkgName)@$($vuln.recommendation)]($($vuln.artiURL_fixed)) (если пакета нет, см. [FAQ](https://<URI нашего wiki>))")
    $vulns_text.Add("----------------------------------------------------<br/>")
}
# Очищаем итоговый текст от различных вариаций переноса строк,
# которые имеются в описании уязвимостей.
# Пропустим данный блок

Подготавливаем ссылку на артефакт валидационной сборки с bom.json:

$reportName = "bom.json"
$buildDefinitionName = $($env:BUILD_DEFINITIONNAME)
$buildResultPath = "${instance}${project}/_build/results?buildId=${buildID}&view=artifacts&pathAsName=false&type=publishedArtifacts"
$buildURI = "${instance}${project}/_build/results?buildId=${buildID}"

Прикреплятьbom.json к артефактам сборки будем так:

# Шаг запуска CodeScoring-failer.ps1

# Публикация отчета CodeScoring в любом случае
- task: PublishBuildArtifacts@1
  displayName: "Публикация SBOM"
  inputs:
    pathToPublish: $(System.DefaultWorkingDirectory)/bom.json
    artifactName: CodeScoring
  continueOnError: false
  enabled: true

Оставляем информацию для разработчика по работе композиционного анализа в журнале валидационной сборки:

Скрытый текст
Write-Host "##[error] Выявленные известные уязвимости в зависимостях: ($($failed_vulns.Count) шт.):" # Выгружаем в лог ссылку на файл отчета с уязвимостями
Write-Host "##[group]Развернуть <-- уязвимости в зависимостях"
Write-Host $vulns_string # Выгружаем в лог список уязвимостей в зависимостях
Write-Host "##[endgroup]"
# Подсвечиваем разработчикам что делать, если выявлена уязвимость:
Write-Host "##[error] Обнаружены зависимости с известными уязвимостями, валидационный билд будет помечен как неуспешный."
Write-Host "##[error] Композиционный анализ завершен, если конвейер упал, значит возможен один из вариантов для текущего запроса на вытягивание:"
Write-Host "##[error]   - обнаружена реальная уязвимость в зависимости;"
Write-Host "##[error]   - уязвимость не актуальна;"
Write-Host "##[error]   - сработка является ошибочной и требует внесения в исключения."
Write-Host "##[error] В любом случае см. инструкцию: https://<URI нашего wiki> чтобы понять как действовать дальше."

Завершаем валидационную сборку с ошибкой, если выявлены уязвимости. Это не позволит разработчику внести известную уязвимость в код:

Write-Host "##[command]Согласно конфигу ${configFile} разрешено фейлить все валидационные сборки при известных уязвимостей"
Write-Host "##vso[task.logissue type=error;]Обнаружены известные уязвимости (${failed_vulns} шт.)" # Фейлим сборку с красивой ошибкой
Write-Host "##vso[task.complete result=SucceededWithIssues;]"

На данном этапе в журнале валидационной сборки разработчик может увидеть следующую информацию:

Пример журнала валидационной сборки
Пример журнала валидационной сборки

Оставляем комментарий в pull request

Подготовка комментария

Подготавливаем содержимое комментария:

Скрытый текст
[string] $footer = "
<details>

<summary>Полезная информация:</summary>

> **Риск согласно OWASP**: [A6 Vulnerable and Outdated Components](https://<URI на наш wiki>)
**Что делать дальше?**: См. инструкцию [CodeScoring FAQ](https://<URI на наш wiki>)
**Перечень найденных уязвимостей**: См. в файл [${reportName}](${buildResultPath}), либо журнал сборки [${buildDefinitionName}](${buildURI})

</details>

<details>

<summary>Список известных уязвимостей в зависимостях ($($failed_vulns.Count) шт.):</summary>

${vulns_string}

</details>

${ingoCatImage}
"

Публикуем комментарий в pull request:

[string] $PR_message = ":warning:Выявлены уязвимые компоненты:warning:"
$PR_message = $PR_message + $footer
$responseCommen = $pr.postNewThread($PR_message, $true, "Выявлены уязвимые компоненты") # Вторая передаваемая переменная boolean отвечает за необходимость удаления предыдущих комментов
if ($responseCommen.StatusCode -eq 200){
    Write-Host "##[debug]Комментарий к PR успешно оставлен."
}
Write-Host "##vso[task.setvariable variable=BUILD_FAILED;]" # Фейлим сборку

Пример комментария

В случае, если была выявлена хотя бы одна уязвимость, в своем pull request разработчик увидит следующий комментарий:

Пример комментария и эмоция Тима
Пример комментария и эмоция Тима

При этом разработчик не сможет завершить pull request, как мы указывали ранее, о чем свидетельствует:

Статус блокировки pull request
Статус блокировки pull request

Технически же блокировка pull request достигается обязательностью нашей валидационной сборки:

Параметры валидационной сборки защищаемой ветви кода
Параметры валидационной сборки защищаемой ветви кода

Полезная информация об уязвимости

Если развернуть markdown текст, разработчик сможет ознакомиться со списком выявленных уязвимостей:

Пример известной уязвимости из базы CVE

Видно, что комментарий включает в себя:

  1. Всю необходимую информацию об уязвимости;

  2. Ссылку на Wiki, где изложены действия разработчика в случае обнаружения такого комментария, а также раздел FAQ.

Ссылки на файл с уязвимостью нет ни в SBOM ни в журнале работы Johhny. Не каждому разработчику удается найти ту самую верхнеуровневую зависимость, в которую встроена уязвимая транзитивная зависимость.

Как мы помогаем в поиске той самой верхнеуровневой зависимости

Не каждому разработчику удается найти ту самую верхнеуровневую зависимость, в которую встроена уязвимая транзитивная зависимость. Вот тут и приходит на помощь CodeScoring. В отличие от JFrog Xray, этот инструмент способен отображать верхнеуровневую зависимость в 90% случаев.

Когда разработчик сдался:

Пример запроса источника уязвимости
Пример запроса источника уязвимости

Пример поиска уязвимой зависимости по дереву зависимостей средствами CodeScoring в нашем искусственном проекте:

Простота поиска источника уязвимых транзитивных зависимостей
Простота поиска источника уязвимых транзитивных зависимостей

Если у разработчика отсутствуют необходимые ресурсы для создания дерева зависимостей или полученное дерево не дает ясности относительно того, какая из зависимостей первого уровня является основной, то решение CodeScoring эффективно решает эту проблему:

Ссылка на зависимость уровнем выше (уровень 3)
Ссылка на зависимость уровнем выше (уровень 3)
Ссылка на файл с верхнеуровневой зависимостью в UI (уровень 0)
Ссылка на файл с верхнеуровневой зависимостью в UI (уровень 0)

Базовый функционал Johnny по блокировке pull request

На самом деле, можно было бы просто блокировать pull request средствами самого Johnny и вообще ничего не программировать. В журнале валидационной сборки выглядело бы это так:

Пример с сайта CodeScoring
Пример с сайта CodeScoring

Я абсолютно убежден, что вывод данной информации неудобен тем, что:

  • Разработчик вынужден тратить время на переход к журналу сборки, что может вызывать у него тревогу и раздражение;

  • Он также должен прокручивать журнал валидационной сборки, при этом в Azure Pipelines журнал Johnny не сворачивается, как это делается в других CI/CD системах:

Пример короткого журнала
Пример короткого журнала
  • Этот метод представления данных не включает кликабельные гиперссылки на нашу Wiki, репозиторий артефактов или информацию об уязвимостях в самом интерфейсе CodeScoring;

  • Вывод не может быть настроен под конкретные нужды;

  • Таблица с данными об уязвимостях довольно ясна и удобна, однако, таблица с нарушениями политик требует дополнительного времени для понимания:

    Пример журнала сработки политик
    Пример журнала сработки политик

В этой статье мы не затрагивали политики CodeScoring, а результаты в формате, отличном от консольного вывода в Johnny пока не внедрены. Возможно, эта тема будет освещена в будущих публикациях.

Фиксируем весь материал

На первый взгляд, внедрение композиционного анализа кажется довольно простым: запустил тулзу, она что‑то нашла, заблокировала pull request/сборку, пускай разработчик сам разберется в логах тулзы или возьмет сработку себе в бэклог.

Тем не менее, если стремиться сделать данный анализ максимально эффективным и удобным для разработчиков, без программирования не обойтись. Мы не откладываем устранение уязвимостей, не ждем завершения pull request и сборки ПО, а проводим анализ на этапе pull request. Причем делаем это в блокирующем режиме, при этом не забываем развеселить некоторых коллег, впервые столкнувшихся с Тимом, несмотря на многолетний опыт работы в компании.

Делюсь полезными рекомендациями для тех, кто планирует реализовать композиционный анализ в pull request:

  1. Установить пороговые уровни уязвимостей по шкале CVSS, начиная с которых pull request будет блокироваться без исключений, и постепенно повышать планку:

    1. понижать минимальный уровень по шкале CVSS;

    2. блокировать protestware либо вести свой "черный" список зависимостей;

    3. блокировать не только по наличию CVE, но и по наличию CWE;

    4. и т.д.

  2. Освоить методику построения дерева зависимостей для всех языков программирования, применяемых в компании, и зафиксировать это методику в инструкциях на Wiki;

  3. Разработать детальные инструкции и FAQ для разработчиков и коллег;

  4. Установить на сборочные агенты все доступные системы сборки и восстановления пакетов, поддерживаемые решением для анализа;

  5. Подготовить коллектив соответствующим образом, например, так:

    Пример уведомления о грядущих блокировках pull request
    Пример уведомления о грядущих блокировках pull request
  6. Выбрать систему исключений: собственную или реализованную в композиционном анализаторе;

  7. Не оставлять разработчика без поддержки, оказывайте помощь и консультируйте его. В нашей компании мы придерживаемся принципа клиентоцентричности: каждый коллега = клиент, и следует оперативно предложить помощь в повышении версии пакета либо внесению уязвимости в исключения;

  8. Комментарии в pull request должны быть краткими, но содержательными;

  9. Будьте открытыми, объясняйте командам структуру процесса, его преимущества для разработчиков и тех, кто занимается продажей "безопасного" ПО, а также проводите митапы.

И хотел бы узнать мнение сообщества по следующим вопросам

  1. С какими сложностями вы столкнулись при внедрении композиционного анализа в pull request?

  2. Охотно ли команды исправляют найденные уязвимости?

  3. Как вы мотивируете разработчиков?

Другие мои статьи по безопасной разработке

Другие мои кейсы в части безопасности разработки смотри в статьях:

  1. Выявление bidirectional unicode троянов (не все unicode символы нужны в исходном коде);

  2. Дерево атак на исходный код в Azure Repos (руководство по защите от атак, направленных на компрометацию исходного кода и выбору мер защиты);

  3. Шпаргалка по сегментации приложений от OWASP (автор шпаргалки);

  4. Небезопасная разработка в Github (примеры публичных ошибок разработчиков);

  5. История утечки персональных данных через Github (пример публичных ошибок одного разработчика).

Комментарии (0)