From ee549592d5bcf3cca2cd53cc5226a7f8aff87b13 Mon Sep 17 00:00:00 2001 From: Matthew Powelson Date: Wed, 20 Aug 2025 09:53:57 -0400 Subject: [PATCH 1/4] Add Bandit to linters --- .gitignore | 3 +- pyproject.toml | 6 ++++ scripts/linters/run_bandit.sh | 52 +++++++++++++++++++++++++++++++++++ scripts/run_linters.sh | 52 +++++++++++++++++++++++++++-------- 4 files changed, 100 insertions(+), 13 deletions(-) create mode 100755 scripts/linters/run_bandit.sh diff --git a/.gitignore b/.gitignore index eb997d2..695ab49 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist/ .pytest_cache *.egg-info .DS_Store -_version.py \ No newline at end of file +_version.py +bandit-report.json \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 761dff0..7a0c6d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dev = [ "pytest", "flake8", "mypy", + "bandit[toml]", ] [tool.setuptools] @@ -60,3 +61,8 @@ python_version = "3.8" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true + +[tool.bandit] +exclude_dirs = ["tests", "venv", ".venv", "env", ".env"] +skips = ["B101", "B601"] +targets = ["arm_cli"] diff --git a/scripts/linters/run_bandit.sh b/scripts/linters/run_bandit.sh new file mode 100755 index 0000000..8a3a5fa --- /dev/null +++ b/scripts/linters/run_bandit.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# run_bandit.sh - Runs Bandit and prints a summary, failing only on HIGH severity + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +bandit -r arm_cli/ -f json -o bandit-report.json || true + +if [ -f bandit-report.json ]; then + python3 - <<'EOF' +import json, sys + +with open("bandit-report.json") as f: + data = json.load(f) + +high_issues = [] +medium_issues = [] +low_issues = [] + +for issue in data.get("results", []): + sev = issue.get("issue_severity", "").upper() + if sev == "HIGH": + high_issues.append(issue) + elif sev == "MEDIUM": + medium_issues.append(issue) + elif sev == "LOW": + low_issues.append(issue) + +all_issues = high_issues + medium_issues + low_issues + +if all_issues: + print("\033[1;33mBandit security summary:\033[0m") + for issue in all_issues: + sev = issue.get("issue_severity") + conf = issue.get("issue_confidence") + file = issue.get("filename") + line = issue.get("line_number") + text = issue.get("issue_text") + print(f"[{sev}/{conf}] {file}:{line} {text}") + print("") + +if high_issues: + print(f"\033[0;31mFound {len(high_issues)} HIGH severity issues - failing!\033[0m") + sys.exit(1) +else: + print("\033[0;32mNo HIGH severity issues found.\033[0m") +EOF +fi diff --git a/scripts/run_linters.sh b/scripts/run_linters.sh index 6d0b96e..d4fe121 100755 --- a/scripts/run_linters.sh +++ b/scripts/run_linters.sh @@ -38,26 +38,30 @@ fi print_status "Starting linting checks for arm-cli..." print_status "Ensuring required tools are installed..." python -m pip install --upgrade pip >/dev/null -python -m pip install black==24.8.0 isort==5.13.2 >/dev/null +python -m pip install black==24.8.0 isort==5.13.2 bandit[toml] >/dev/null # Run Black (code formatter) print_status "Running Black (code formatter)..." if black --check .; then print_success "Black check passed - code is properly formatted" + BLACK_STATUS="PASSED" else print_error "Black check failed - code needs formatting" print_status "Run 'black .' to automatically format the code" BLACK_FAILED=true + BLACK_STATUS="FAILED" fi # Run isort (import sorter) print_status "Running isort (import sorter)..." if isort --check-only .; then print_success "isort check passed - imports are properly sorted" + ISORT_STATUS="PASSED" else print_error "isort check failed - imports need sorting" print_status "Run 'isort .' to automatically sort imports" ISORT_FAILED=true + ISORT_STATUS="FAILED" fi # # Run Flake8 (style checker) @@ -69,6 +73,18 @@ fi # FLAKE8_FAILED=true # fi +# Run Bandit (security checker) +print_status "Running Bandit (security checker)..." +if ./scripts/linters/run_bandit.sh; then + print_success "Bandit check passed!" + BANDIT_STATUS="PASSED" +else + print_error "Bandit found HIGH severity issues!" + BANDIT_FAILED=true + BANDIT_STATUS="FAILED" +fi + + # # Run MyPy (type checker) # print_status "Running MyPy (type checker)..." # if mypy .; then @@ -81,17 +97,29 @@ fi # Summary echo print_status "Linting Summary:" -if [ "$BLACK_FAILED" = true ] || [ "$ISORT_FAILED" = true ] || [ "$FLAKE8_FAILED" = true ] || [ "$MYPY_FAILED" = true ]; then - print_error "Some linting checks failed!" - echo - print_status "To fix formatting issues:" - echo " black ." - echo " isort ." - echo - print_status "To run individual linters:" - echo " flake8 ." - echo " mypy ." + +# Black summary +echo "- Black: ${BLACK_STATUS:-SKIPPED}" +if [ "$BLACK_STATUS" = "FAILED" ]; then + echo " run: black ." +fi + +# isort summary +echo "- isort: ${ISORT_STATUS:-SKIPPED}" +if [ "$ISORT_STATUS" = "FAILED" ]; then + echo " run: isort ." +fi + +# Bandit summary +echo "- Bandit: ${BANDIT_STATUS:-SKIPPED}" +if [ "$BANDIT_STATUS" = "FAILED" ]; then + echo " run: ./scripts/linters/run_bandit.sh" +fi + +# Final result +if [ "$BLACK_FAILED" = true ] || [ "$ISORT_FAILED" = true ] || [ "$BANDIT_FAILED" = true ]; then + print_error "One or more checks failed. See above for commands to rerun failed linters." exit 1 else print_success "All linting checks passed! 🎉" -fi \ No newline at end of file +fi \ No newline at end of file From bfd1d301592c8efd12803252ec8e75ddeed66961 Mon Sep 17 00:00:00 2001 From: Matthew Powelson Date: Wed, 20 Aug 2025 09:58:26 -0400 Subject: [PATCH 2/4] Add Bandit to code quality workflow --- .github/workflows/code-quality.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index dab8eeb..ad33a40 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -29,7 +29,10 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install black==24.8.0 isort==5.13.2 + pip install black==24.8.0 isort==5.13.2 bandit[toml] + + - name: Make Bandit helper executable + run: chmod +x scripts/linters/run_bandit.sh - name: Run Black run: black --check . @@ -37,6 +40,9 @@ jobs: - name: Run Isort run: isort --check-only . + - name: Run Bandit + run: ./scripts/linters/run_bandit.sh + # - name: Run Flake8 # run: flake8 . From 626257e7ef11c701cd10b3eda741c57e6015efbd Mon Sep 17 00:00:00 2001 From: Matthew Powelson Date: Wed, 20 Aug 2025 10:02:14 -0400 Subject: [PATCH 3/4] Fix isort failing erroneously --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 7a0c6d1..fe82585 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ target-version = ['py38'] profile = "black" line_length = 100 multi_line_output = 3 +extend_skip = ["arm_cli/_version.py"] [tool.mypy] python_version = "3.8" From d3bea63e3d0605454f96eb7282282e3ec4febdfb Mon Sep 17 00:00:00 2001 From: Matthew Powelson Date: Wed, 20 Aug 2025 10:04:43 -0400 Subject: [PATCH 4/4] Add CodeQL Badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 14d11eb..999709c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![PyPI](https://img.shields.io/pypi/v/arm-cli.svg)](https://pypi.org/project/arm-cli/) [![Changelog](https://img.shields.io/github/v/release/mpowelson/arm-cli?include_prereleases&label=changelog)](https://github.com/mpowelson/arm-cli/releases) [![Tests](https://github.com/mpowelson/arm-cli/actions/workflows/test.yml/badge.svg)](https://github.com/mpowelson/arm-cli/actions/workflows/test.yml) +[![CodeQL Analysis](https://github.com/mpowelson/arm-cli/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/mpowelson/arm-cli/actions/workflows/codeql-analysis.yml) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/mpowelson/arm-cli/blob/master/LICENSE) Experimental CLI for deploying robotic applications