🛠 Bandit как linter на pre-commit
Салют,
Давай сегодня посмотрим с тобой на SAST для python. Bandit смотрим как линтер, потому что удобно использовать без разбора и углубления в бизнес логику, потому что инструмент не умеет в это. Тебе же полезен для интеграции в команду разработки на python.
Bandit парсит код каждого файла проекта в AST (Abstract Syntax Tree - дерево абстрактного синтаксиса) и прогоняет набор правил (плагины через entry points). Тип лицензии: Apache-2.0 license. Форматы отчетов: csv, custom, html, json, screen, txt, xml, yaml.
Самое классное, что если использовать wemake-python-styleguide в совокупности с Bandit как анализатором, то мы решаем вопросы разименования, затенения встроенных функций, когнитивной сложность, а также проблемы с непоследовательными возвратами, неправильными разрывами строк, непоследовательными выражениями и сработка с явными уязвимостями.
Команды
python -m venv .venv
pip install bandit
bandir -r ./my_python_project/ # Рекурсивное сканирование
--severity-level
--confidence-level # Пороги
--profile
-t # Выбор правил
-s # Пропуск правил
nosec # Точечное игнорирование
docker pull ghcr.io/pycqa/bandit/bandit:<tag>
cosign verify ghcr.io/pycqa/bandit/bandit:latest \ # проверка подписи образа перед использованием
--certificate-identity https://github.com/pycqa/bandit/.github/workflows/build-publish-image.yml@refs/tags/<version> \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Файл конфигурации
# .bandit
exclude_dirs: ['tests', 'venv']
skips: ['B101', 'B311']
any_other_function_with_shell_equals_true:
no_shell: ['subprocess.Popen']
hardcoded_password_string:
hardcoded_password_string_re: '(?i)(password|passwd|pwd)'
Использование как pre-commit
#.pre-commit-config.yaml
repos:
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
args: ['-ll', '-ii']
CI/CD
variables:
BANDIT_SEVERITY: "medium"
BANDIT_CONFIDENCE: "medium"
BANDIT_OUTDIR: "bandit-report"
bandit_scan:
stage: security
image: ghcr.io/pycqa/bandit/bandit:<tag>
script:
- mkdir -p "$BANDIT_OUTDIR"
- bandit -r . \
--severity-level="$BANDIT_SEVERITY" \
--confidence-level="$BANDIT_CONFIDENCE" \
-f json -o "$BANDIT_OUTDIR/bandit.json"
pipeline {
agent any
options { timestamps() }
environment {
BANDIT_SEVERITY = 'medium'
BANDIT_CONFIDENCE = 'medium'
REPORT_DIR = 'bandit-report'
}
stages {
stage('Setup Python venv') {
steps {
sh '''
python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install bandit
mkdir -p "${REPORT_DIR}"
'''
}
}
stage('Bandit Scan') {
steps {
sh '''
. .venv/bin/activate
bandit -r . --severity-level=${BANDIT_SEVERITY} --confidence-level=${BANDIT_CONFIDENCE} \
-f json -o ${REPORT_DIR}/bandit.json
bandit -r . --severity-level=${BANDIT_SEVERITY} --confidence-level=${BANDIT_CONFIDENCE} \
-f sarif -o ${REPORT_DIR}/bandit.sarif
bandit -r . --severity-level=${BANDIT_SEVERITY} --confidence-level=${BANDIT_CONFIDENCE} \
-f html -o ${REPORT_DIR}/bandit.html
'''
}
}
}
post {
always {
archiveArtifacts artifacts: 'bandit-report/**', fingerprint: true
}
unsuccessful {
script { currentBuild.result = 'UNSTABLE' }
}
}
}
Итого: сам SAST дает AST-подход без исполнения кода, где baseline позволяет внедрять постепенно и расширяем через плагины, но большое количество некорректных правила дающих FP, которые надо перебрать руками. Инструмент легковесен, но не как целевой SAST, а очень комфортное решение для проверок до Merge/ Pull Requeste. Совместно с wemake-python-styleguide решается проблема структуры и стилистики кода внутри команды разработки.
#toolchain #sast
