From 8cba5f7710236918f1ed9565c6f18d9d8dd6a775 Mon Sep 17 00:00:00 2001 From: khalil-bot Date: Thu, 28 May 2026 16:24:39 +0200 Subject: [PATCH] ci(notification-service): add SAST, dependency scan and DAST security gates Add CodeQL (SAST), SpotBugs+FindSecBugs (SAST via verify), OWASP Dependency Check and OWASP ZAP baseline scan (DAST) to the CI/CD pipeline. Docker deploy is gated behind ci, sast-codeql and dast jobs. Fix DM_DEFAULT_ENCODING spotted by SpotBugs: use StandardCharsets.UTF_8 in Basic Auth encoding. --- .github/workflows/notification-service.yml | 123 +++++++++++++++++- notification_service/pom.xml | 34 +++++ .../service/AirQualityService.java | 3 +- 3 files changed, 155 insertions(+), 5 deletions(-) diff --git a/.github/workflows/notification-service.yml b/.github/workflows/notification-service.yml index 442d473..85519df 100644 --- a/.github/workflows/notification-service.yml +++ b/.github/workflows/notification-service.yml @@ -31,14 +31,129 @@ jobs: distribution: temurin cache: maven - - name: Run tests + - name: Build & test (SpotBugs included via verify) run: mvn verify -q - # ── 2. Docker build & push ─────────────────────────────────────────────────── + # ── 2. SAST – CodeQL ───────────────────────────────────────────────────────── + sast-codeql: + name: SAST – CodeQL + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + + steps: + - uses: actions/checkout@v4 + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + cache: maven + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: java + queries: security-and-quality + + - name: Build for CodeQL + run: mvn compile -q -f notification_service/pom.xml + + - name: Analyze + uses: github/codeql-action/analyze@v3 + with: + category: /language:java + + # ── 3. Dependency vulnerability scan (OWASP) ───────────────────────────────── + dependency-check: + name: Dependency vulnerability scan + runs-on: ubuntu-latest + defaults: + run: + working-directory: notification_service + + steps: + - uses: actions/checkout@v4 + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + cache: maven + + - name: Cache NVD database + uses: actions/cache@v4 + with: + path: ~/.m2/repository/org/owasp/dependency-check-data + key: nvd-${{ runner.os }}-${{ hashFiles('notification_service/pom.xml') }} + restore-keys: nvd-${{ runner.os }}- + + - name: OWASP Dependency Check + run: mvn dependency-check:check -q + continue-on-error: true + + - name: Upload dependency check report + if: always() + uses: actions/upload-artifact@v4 + with: + name: dependency-check-report + path: notification_service/target/dependency-check-report.* + retention-days: 14 + + # ── 4. DAST – OWASP ZAP ────────────────────────────────────────────────────── + dast: + name: DAST – OWASP ZAP + runs-on: ubuntu-latest + needs: ci + + steps: + - uses: actions/checkout@v4 + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + cache: maven + + - name: Build JAR + run: mvn package -DskipTests -q -f notification_service/pom.xml + + - name: Start service + run: | + java -jar notification_service/target/notification-service-*.jar & + timeout 60 bash -c 'until curl -sf http://localhost:8080/actuator/health; do sleep 2; done' + env: + TELEGRAM_BOT_TOKEN: dummy-token + TELEGRAM_CHAT_ID: "0" + AIR_QUALITY_API_URL: http://localhost:9999 + API_USERNAME: dummy + API_PASSWORD: dummy + MOCK_MODE: "true" + + - name: ZAP Baseline Scan + uses: zaproxy/action-baseline@v0.12.0 + with: + target: http://localhost:8080 + fail_action: false + allow_issue_writing: false + + - name: Upload ZAP report + if: always() + uses: actions/upload-artifact@v4 + with: + name: zap-report + path: report_html.html + retention-days: 14 + + # ── 5. Docker build & push ─────────────────────────────────────────────────── docker: name: Docker build & push runs-on: ubuntu-latest - needs: ci + needs: [ci, sast-codeql, dast] if: github.ref == 'refs/heads/main' outputs: sha_tag: ${{ steps.tag.outputs.sha }} @@ -80,7 +195,7 @@ jobs: ${{ steps.tag.outputs.sha }} ${{ steps.tag.outputs.extra }} - # ── 3. Deploy to physical server (SSH) ─────────────────────────────────────── + # ── 6. Deploy to physical server (SSH) ─────────────────────────────────────── deploy: name: Deploy runs-on: ubuntu-latest diff --git a/notification_service/pom.xml b/notification_service/pom.xml index aff6c17..c58ca5d 100644 --- a/notification_service/pom.xml +++ b/notification_service/pom.xml @@ -63,6 +63,40 @@ + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.8.3.1 + + Max + High + + + com.h3xstream.findsecbugs + findsecbugs-plugin + 1.13.0 + + + + + + check + + + + + + + org.owasp + dependency-check-maven + 9.0.9 + + 7 + HTML,JSON + + diff --git a/notification_service/src/main/java/ch/hesso/pi/notification/service/AirQualityService.java b/notification_service/src/main/java/ch/hesso/pi/notification/service/AirQualityService.java index dbdec71..ee9c499 100644 --- a/notification_service/src/main/java/ch/hesso/pi/notification/service/AirQualityService.java +++ b/notification_service/src/main/java/ch/hesso/pi/notification/service/AirQualityService.java @@ -12,6 +12,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; @@ -41,7 +42,7 @@ public class AirQualityService { var request = restClient.get().uri(url); if (StringUtils.hasText(username) && StringUtils.hasText(password)) { - String encoded = Base64.getEncoder().encodeToString((username + ":" + password).getBytes()); + String encoded = Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); request = request.header(HttpHeaders.AUTHORIZATION, "Basic " + encoded); }