diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52b10b2..5a5dfa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,30 @@ jobs: - name: Scan for security vulnerabilities in JavaScript dependencies run: bin/importmap audit + scan_container: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Build Docker image + run: docker build -t clinch:${{ github.sha }} . + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: clinch:${{ github.sha }} + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + + - name: Upload Trivy results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + lint: runs-on: ubuntu-latest steps: diff --git a/.ruby-version b/.ruby-version index 1cf8253..7921bd0 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.6 +3.4.8 diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 0000000..c79d3ec --- /dev/null +++ b/.trivyignore @@ -0,0 +1,47 @@ +# Trivy ignore file +# This file tells Trivy to skip specific vulnerabilities or files +# See: https://aquasecurity.github.io/trivy/latest/docs/configuration/filtering/ + +# ============================================================================= +# False Positives - Test Fixtures +# ============================================================================= + +# Capybara test fixture - not a real private key +# Ignore secrets in test fixtures +*/capybara-*/spec/fixtures/key.pem + +# ============================================================================= +# Unfixable CVEs - No Patches Available (Status: affected/fix_deferred) +# ============================================================================= + +# GnuPG vulnerabilities - not used by Clinch at runtime +# Low risk: dirmngr/gpg tools not invoked during normal operation +CVE-2025-68973 + +# Image processing library vulnerabilities +# Low risk for Clinch: Only admins upload images (app icons), not untrusted users +# Waiting on Debian security team to release patches + +# ImageMagick - Integer overflow (32-bit only) +CVE-2025-66628 + +# glib - Integer overflow in URI escaping +CVE-2025-13601 + +# HDF5 - Critical vulnerabilities in scientific data format library +CVE-2025-2153 +CVE-2025-2308 +CVE-2025-2309 +CVE-2025-2310 + +# libmatio - MATLAB file format library +CVE-2025-2338 + +# OpenEXR - Image format vulnerabilities +CVE-2025-12495 +CVE-2025-12839 +CVE-2025-12840 +CVE-2025-64181 + +# libvips - Image processing library +CVE-2025-59933 diff --git a/Dockerfile b/Dockerfile index d1a1a43..7905b21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html # Make sure RUBY_VERSION matches the Ruby version in .ruby-version -ARG RUBY_VERSION=3.4.6 +ARG RUBY_VERSION=3.4.8 FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base LABEL org.opencontainers.image.source=https://github.com/dkam/clinch @@ -16,8 +16,9 @@ LABEL org.opencontainers.image.source=https://github.com/dkam/clinch # Rails app lives here WORKDIR /rails -# Install base packages +# Install base packages and upgrade to latest security patches RUN apt-get update -qq && \ + apt-get upgrade -y && \ apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives diff --git a/Gemfile b/Gemfile index 9154e09..a63535e 100644 --- a/Gemfile +++ b/Gemfile @@ -90,4 +90,7 @@ group :test do # Code coverage analysis gem "simplecov", require: false + + # Pin minitest to < 6.0 until Rails 8.1 supports the new API + gem "minitest", "< 6.0" end diff --git a/Gemfile.lock b/Gemfile.lock index 809523d..2e3090c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - action_text-trix (2.1.15) + action_text-trix (2.1.16) railties actioncable (8.1.1) actionpack (= 8.1.1) @@ -80,14 +80,14 @@ GEM android_key_attestation (0.3.0) ast (2.4.3) base64 (0.3.0) - bcrypt (3.1.20) - bcrypt_pbkdf (1.1.1) - bigdecimal (3.3.1) + bcrypt (3.1.21) + bcrypt_pbkdf (1.1.2) + bigdecimal (4.0.1) bindata (2.5.1) bindex (0.8.1) - bootsnap (1.19.0) + bootsnap (1.20.1) msgpack (~> 1.2) - brakeman (7.1.1) + brakeman (7.1.2) racc builder (3.3.0) bundler-audit (0.9.3) @@ -106,37 +106,37 @@ GEM childprocess (5.1.0) logger (~> 1.5) chunky_png (1.4.0) - concurrent-ruby (1.3.5) - connection_pool (2.5.5) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) crass (1.0.6) - date (3.5.0) - debug (1.11.0) + date (3.5.1) + debug (1.11.1) irb (~> 1.10) reline (>= 0.3.8) docile (1.4.1) - dotenv (3.1.8) + dotenv (3.2.0) drb (2.2.3) ed25519 (1.4.0) - erb (6.0.0) + erb (6.0.1) erubi (1.13.1) et-orbi (1.4.0) tzinfo - ffi (1.17.2-aarch64-linux-gnu) - ffi (1.17.2-aarch64-linux-musl) - ffi (1.17.2-arm-linux-gnu) - ffi (1.17.2-arm-linux-musl) - ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86_64-linux-gnu) - ffi (1.17.2-x86_64-linux-musl) + ffi (1.17.3-aarch64-linux-gnu) + ffi (1.17.3-aarch64-linux-musl) + ffi (1.17.3-arm-linux-gnu) + ffi (1.17.3-arm-linux-musl) + ffi (1.17.3-arm64-darwin) + ffi (1.17.3-x86_64-linux-gnu) + ffi (1.17.3-x86_64-linux-musl) fugit (1.12.1) et-orbi (~> 1.4) raabro (~> 1.4) globalid (1.3.0) activesupport (>= 6.1) - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) image_processing (1.14.0) mini_magick (>= 4.9.5, < 6) @@ -145,18 +145,18 @@ GEM actionpack (>= 6.0.0) activesupport (>= 6.0.0) railties (>= 6.0.0) - io-console (0.8.1) - irb (1.15.3) + io-console (0.8.2) + irb (1.16.0) pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jbuilder (2.14.1) actionview (>= 7.0.0) activesupport (>= 7.0.0) - json (2.16.0) + json (2.18.0) jwt (3.1.2) base64 - kamal (2.9.0) + kamal (2.10.1) activesupport (>= 7.0) base64 (~> 0.2) bcrypt_pbkdf (~> 1.0) @@ -176,7 +176,7 @@ GEM launchy (>= 2.2, < 4) lint_roller (1.1.0) logger (1.7.0) - loofah (2.24.1) + loofah (2.25.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.9.0) @@ -190,9 +190,9 @@ GEM mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (5.26.2) + minitest (5.27.0) msgpack (1.8.0) - net-imap (0.5.12) + net-imap (0.6.2) date net-protocol net-pop (0.1.2) @@ -207,21 +207,21 @@ GEM net-protocol net-ssh (7.3.0) nio4r (2.7.5) - nokogiri (1.18.10-aarch64-linux-gnu) + nokogiri (1.19.0-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) + nokogiri (1.19.0-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) + nokogiri (1.19.0-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) + nokogiri (1.19.0-arm-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) + nokogiri (1.19.0-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) + nokogiri (1.19.0-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) + nokogiri (1.19.0-x86_64-linux-musl) racc (~> 1.4) - openssl (3.3.2) + openssl (4.0.0) openssl-signature_algorithm (1.3.0) openssl (> 2.0) ostruct (0.6.3) @@ -232,12 +232,12 @@ GEM pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.6.0) + prism (1.7.0) propshaft (1.3.1) actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack - psych (5.2.6) + psych (5.3.1) date stringio public_suffix (7.0.0) @@ -251,7 +251,7 @@ GEM rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) rails (8.1.1) actioncable (= 8.1.1) @@ -285,7 +285,7 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.3.1) - rdoc (6.16.1) + rdoc (7.0.3) erb psych (>= 4.0.0) tsort @@ -309,22 +309,22 @@ GEM rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.48.0) + rubocop-ast (1.49.0) parser (>= 3.3.7.2) - prism (~> 1.4) + prism (~> 1.7) rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (1.13.0) - ruby-vips (2.2.5) + ruby-vips (2.3.0) ffi (~> 1.12) logger rubyzip (3.2.2) safety_net_attestation (0.5.0) jwt (>= 2.0, < 4.0) securerandom (0.4.1) - selenium-webdriver (4.38.0) + selenium-webdriver (4.39.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -358,14 +358,14 @@ GEM fugit (~> 1.11) railties (>= 7.1) thor (>= 1.3.1) - sqlite3 (2.8.1-aarch64-linux-gnu) - sqlite3 (2.8.1-aarch64-linux-musl) - sqlite3 (2.8.1-arm-linux-gnu) - sqlite3 (2.8.1-arm-linux-musl) - sqlite3 (2.8.1-arm64-darwin) - sqlite3 (2.8.1-x86_64-linux-gnu) - sqlite3 (2.8.1-x86_64-linux-musl) - sshkit (1.24.0) + sqlite3 (2.9.0-aarch64-linux-gnu) + sqlite3 (2.9.0-aarch64-linux-musl) + sqlite3 (2.9.0-arm-linux-gnu) + sqlite3 (2.9.0-arm-linux-musl) + sqlite3 (2.9.0-arm64-darwin) + sqlite3 (2.9.0-x86_64-linux-gnu) + sqlite3 (2.9.0-x86_64-linux-musl) + sshkit (1.25.0) base64 logger net-scp (>= 1.1.2) @@ -386,22 +386,22 @@ GEM rubocop-performance (~> 1.26.0) stimulus-rails (1.3.4) railties (>= 6.0.0) - stringio (3.1.8) + stringio (3.2.0) tailwindcss-rails (4.4.0) railties (>= 7.0.0) tailwindcss-ruby (~> 4.0) - tailwindcss-ruby (4.1.16) - tailwindcss-ruby (4.1.16-aarch64-linux-gnu) - tailwindcss-ruby (4.1.16-aarch64-linux-musl) - tailwindcss-ruby (4.1.16-arm64-darwin) - tailwindcss-ruby (4.1.16-x86_64-linux-gnu) - tailwindcss-ruby (4.1.16-x86_64-linux-musl) + tailwindcss-ruby (4.1.18) + tailwindcss-ruby (4.1.18-aarch64-linux-gnu) + tailwindcss-ruby (4.1.18-aarch64-linux-musl) + tailwindcss-ruby (4.1.18-arm64-darwin) + tailwindcss-ruby (4.1.18-x86_64-linux-gnu) + tailwindcss-ruby (4.1.18-x86_64-linux-musl) thor (1.4.0) - thruster (0.1.16) - thruster (0.1.16-aarch64-linux) - thruster (0.1.16-arm64-darwin) - thruster (0.1.16-x86_64-linux) - timeout (0.4.4) + thruster (0.1.17) + thruster (0.1.17-aarch64-linux) + thruster (0.1.17-arm64-darwin) + thruster (0.1.17-x86_64-linux) + timeout (0.6.0) tpm-key_attestation (0.14.1) bindata (~> 2.4) openssl (> 2.0) @@ -437,7 +437,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.3) + zeitwerk (2.7.4) PLATFORMS aarch64-linux @@ -463,6 +463,7 @@ DEPENDENCIES jwt (~> 3.1) kamal letter_opener + minitest (< 6.0) propshaft public_suffix (~> 7.0) puma (>= 5.0) @@ -487,4 +488,4 @@ DEPENDENCIES webauthn (~> 3.0) BUNDLED WITH - 2.7.2 + 4.0.3 diff --git a/README.md b/README.md index 64b66f0..48788aa 100644 --- a/README.md +++ b/README.md @@ -715,12 +715,30 @@ bin/bundler-audit check --update # Dependency vulnerability scan bin/importmap audit # JavaScript dependency scan ``` +**Container Image Scanning:** + +```bash +# Install Trivy +brew install trivy # macOS +# or use Docker: alias trivy='docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy' + +# Build and scan image (CRITICAL and HIGH severity only, like CI) +docker build -t clinch:local . +trivy image --severity CRITICAL,HIGH clinch:local + +# Scan only for fixable vulnerabilities +trivy image --severity CRITICAL,HIGH --ignore-unfixed clinch:local +``` + **CI/CD Integration:** All security scans run automatically on every pull request and push to main via GitHub Actions. **Security Tools:** - **Brakeman** - Static analysis for Rails security vulnerabilities - **bundler-audit** - Checks gems for known CVEs +- **Trivy** - Container image vulnerability scanning (OS/system packages) +- **Dependabot** - Automated dependency updates +- **GitHub Secret Scanning** - Detects leaked credentials with push protection - **SimpleCov** - Code coverage tracking - **RuboCop** - Code style and quality enforcement diff --git a/docs/beta-checklist.md b/docs/beta-checklist.md index 398262a..b775235 100644 --- a/docs/beta-checklist.md +++ b/docs/beta-checklist.md @@ -24,6 +24,18 @@ This checklist ensures Clinch meets security, quality, and documentation standar - [x] **importmap audit** - JavaScript dependency scanning - CI: Runs on every PR and push to main +- [x] **Trivy** - Container image vulnerability scanning + - Scans Docker images for OS and system package vulnerabilities + - CI: Builds and scans image on every PR and push to main + - Results uploaded to GitHub Security tab + +- [x] **Dependabot** - Automated dependency updates + - Creates PRs for outdated dependencies + - Enabled for Ruby gems and GitHub Actions + +- [x] **GitHub Secret Scanning** - Detects leaked credentials + - Push protection enabled to block commits with secrets + - [x] **Test Coverage** - SimpleCov integration - Command: `COVERAGE=1 bin/rails test` - Coverage report: `coverage/index.html` @@ -238,7 +250,11 @@ To move from "experimental" to "Beta", the following must be completed: **Nice to have (Can defer to post-Beta):** - [ ] Bug bounty program - [ ] Advanced monitoring/alerting -- [ ] Automated security testing in CI beyond brakeman/bundler-audit +- [x] Automated security testing in CI beyond brakeman/bundler-audit + - [x] Dependabot (automated dependency updates) + - [x] GitHub Secret Scanning (automatic with push protection enabled) + - [x] Container image scanning (Trivy scans Docker images for OS/system vulnerabilities) + - [ ] DAST/Dynamic testing (OWASP ZAP) - optional for post-Beta ## Status Summary