Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/container-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,31 +57,31 @@ jobs:
- name: Upload Trivy SARIF to GitHub Security tab
id: upload_trivy
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.TRIVY_SCA_SARIF_OUTPUT }}
category: trivy-container-scanning

- name: Upload OSV Scanner SARIF to GitHub Security tab
id: upload_osv
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.OSV_SCA_SARIF_OUTPUT }}
category: osv-scanner-container-scanning

- name: Upload OpenGrep SARIF to GitHub Security tab
id: upload_opengrep
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.OPENGREP_SAST_SARIF_OUTPUT }}
category: opengrep-sast

- name: Upload Hadolint SARIF to GitHub Security tab
id: upload_hadolint
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.HADOLINT_SAST_SARIF_OUTPUT }}
category: hadolint-sast
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Upload Semgrep SARIF to GitHub Security tab
id: upload_semgrep
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.OPENGREP_SARIF_OUTPUT }}
category: semgrep-app
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/sca.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
TRIVY_SARIF_OUTPUT: trivy-platform-backend.sarif
OSV_SARIF_OUTPUT: osv-scanner-platform-backend.sarif
SCA_MERGED_SARIF_OUTPUT: SCA-platform-backend-merged.sarif
# CVSS gate: fail at >= FAIL, warn between WARN and FAIL.
GATE_FAIL_THRESHOLD: "8.0"
GATE_WARN_THRESHOLD: "5.0"

steps:
- name: Check out repository
Expand All @@ -38,10 +41,7 @@ jobs:
key: ${{ runner.os }}-m2-v1-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-v1-

- name: Resolve Maven dependencies
run: mvn dependency:resolve -q

- name: Setup tools
- name: Setup tools and generate SBOM
run: bash ci/setup-tools.sh --install-tool trivy,osv-scanner --sbom-ecosystem maven

- name: Run SCA tools
Expand All @@ -50,15 +50,15 @@ jobs:
- name: Upload Trivy SARIF to GitHub Security tab
id: upload_trivy
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.TRIVY_SARIF_OUTPUT }}
category: trivy-app

- name: Upload OSV Scanner SARIF to GitHub Security tab
id: upload_osv
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.2
with:
sarif_file: ${{ env.OSV_SARIF_OUTPUT }}
category: osv-scanner-app
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/secrets-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Secret Scanning (gitleaks)

on:
pull_request:
push:
branches:
- master
workflow_dispatch:

permissions:
contents: read
security-events: write

jobs:
gitleaks:
runs-on: ubuntu-latest
env:
GITLEAKS_SARIF_OUTPUT: gitleaks.sarif

steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Install gitleaks
run: bash ci/setup-tools.sh --install-tool gitleaks

- name: Install and run gitleaks
run: >
gitleaks dir .
--config ci/suppress_gitleaks.toml
--redact
--report-format sarif
--report-path "$GITLEAKS_SARIF_OUTPUT"

- name: Upload SARIF to code scanning
if: always()
uses: github/codeql-action/upload-sarif@c35d1b164463ee62a100735382aaaa525c5d3496 #v2.25.6
with:
sarif_file: ${{ env.GITLEAKS_SARIF_OUTPUT }}
category: gitleaks
11 changes: 10 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -13,3 +13,12 @@ repos:
- id: pretty-format-json
args: ["--autofix"]
exclude: slack.json
- id: check-xml
- id: detect-private-key
- id: check-case-conflict
- id: mixed-line-ending

- repo: https://github.com/gitleaks/gitleaks
rev: 83d9cd684c87d95d656c1458ef04895a7f1cbd8e # frozen: v8.30.1
hooks:
- id: gitleaks
27 changes: 5 additions & 22 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#######################################################
# Build the spring boot maven project
#######################################################
FROM maven:3.9.11-amazoncorretto-21 AS mvn-build-env
FROM maven:3.9.11-amazoncorretto-21@sha256:82d98fbed447e3f7dfbf1089840a51bfaeb5651cb47a9c5820139d054db3dde1 AS mvn-build-env
LABEL maintainer="Thanasis Karampatsis <tkarabatsis@athenarc.gr>"

ENV CODE_PATH="/opt/code"
WORKDIR $CODE_PATH

COPY pom.xml $CODE_PATH/

# Pre-fetch dependencies first to improve build cache efficiency.
RUN mvn -B -ntp dependency:go-offline

COPY src/ $CODE_PATH/src
Expand All @@ -19,17 +18,11 @@ RUN mvn -B -ntp clean package
#######################################################
# Setup the running container
#######################################################
FROM amazoncorretto:21-alpine3.21
FROM amazoncorretto:21-alpine3.21@sha256:392b286e53c7f4cd366bd2f752f509b7e24de9f414564bccd7d152a58214a8b6

#######################################################
# Setting up timezone
#######################################################
ENV TZ=Etc/GMT
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

#######################################################
# Setting up environment
#######################################################
ENV APP_CONFIG_TEMPLATE="/opt/config/application.tmpl"
ENV APP_CONFIG_LOCATION="/opt/config/application.yml"
ENV SPRING_CONFIG_LOCATION="file:/opt/config/application.yml"
Expand All @@ -43,25 +36,15 @@ WORKDIR /opt

RUN apk add --no-cache curl

#######################################################
# Install dockerize
#######################################################
ENV DOCKERIZE_VERSION=v0.14.0
# renovate: datasource=github-releases depName=jwilder/dockerize
ENV DOCKERIZE_VERSION=v0.15.0
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz


#######################################################
# Prepare the spring boot application files
#######################################################
COPY config/application.tmpl $APP_CONFIG_TEMPLATE
COPY --from=mvn-build-env /opt/code/target/platform-backend.jar /usr/share/jars/


#######################################################
# Configuration for the backend config files
#######################################################
ENV DISABLED_ALGORITHMS_CONFIG_PATH="/opt/platform/algorithms/disabledAlgorithms.json"
COPY config/disabledAlgorithms.json $DISABLED_ALGORITHMS_CONFIG_PATH
VOLUME /opt/platform/api
Expand All @@ -73,4 +56,4 @@ RUN addgroup -S appgroup && adduser -S appuser -G appgroup \
USER appuser
ENTRYPOINT ["sh", "-ec", "exec dockerize -template ${APP_CONFIG_TEMPLATE}:${APP_CONFIG_LOCATION} java --add-opens java.base/java.io=ALL-UNNAMED -Daeron.term.buffer.length -jar /usr/share/jars/platform-backend.jar"]
EXPOSE 8080
HEALTHCHECK --start-period=60s CMD curl --fail --silent --show-error http://localhost:8080/services/actuator/health | grep -q '"status":"UP"'
HEALTHCHECK --start-period=60s CMD ["sh", "-c", "curl --fail --silent --show-error http://localhost:8080/services/actuator/health | grep -q '\"status\":\"UP\"'"]
28 changes: 15 additions & 13 deletions ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@ Following the OWASP DevSecOps model, scanning is split into three independent pi

```
.github/
├── workflows/
│ ├── container-scan.yml # builds the image, scans the Dockerfile (SAST) and image (SCA)
│ ├── sca.yml # resolves deps, generates SBOM, scans it (SCA)
│ └── sast.yml # scans source code (SAST)
└── scripts/
├── setup-tools.sh # installs trivy, osv-scanner, opengrep, hadolint, semgrep-rules
├── container_scan.py # orchestrator for container-scan.yml
├── sca_scan.py # orchestrator for sca.yml
├── sast_scan.py # orchestrator for sast.yml
├── parse_sarif.py # shared: reads SARIF security-severity scores
├── suppress_trivy.yaml # shared Trivy ignore file
└── suppress_osv_scanner.toml # shared OSV-Scanner ignore file
└── workflows/
├── container-scan.yml # builds the image, scans the Dockerfile (SAST) and image (SCA)
├── sca.yml # resolves deps, generates SBOM, scans it (SCA)
├── sast.yml # scans source code (SAST)
├── publish_images.yml # publishes built images
└── ebrains.yml # EBRAINS-specific pipeline

ci/
├── setup-tools.sh # installs trivy, osv-scanner, opengrep, hadolint, semgrep-rules
├── container_scan.py # orchestrator for container-scan.yml
├── sca_scan.py # orchestrator for sca.yml
├── sast_scan.py # orchestrator for sast.yml
├── parse_sarif.py # shared: reads SARIF security-severity scores
├── suppress_trivy.yaml # shared Trivy ignore file
└── suppress_osv_scanner.toml # shared OSV-Scanner ignore file
```

> **Note:** all three workflows trigger on `pull_request`, `workflow_dispatch`, and a weekly Monday 02:00 UTC schedule, and run independently in parallel. Each has its own gate and its own category in the GitHub Security tab.

## 2. Architecture
Expand Down
13 changes: 6 additions & 7 deletions ci/container_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import json
import argparse
from parse_sarif import evaluate
from parse_sarif import evaluate, GATE_FAIL_THRESHOLD, GATE_WARN_THRESHOLD


GREEN = '\033[92m'
Expand Down Expand Up @@ -92,25 +92,24 @@ def handle_sca():
eval_result = evaluate(path)

if eval_result.gate_failed:
tool_status[name] = "FAILED" # this tool found CVSS >= 8.0
tool_status[name] = "FAILED"
gate_failed = True
elif eval_result.gate_warn:
tool_status[name] = "WARNING" # this tool found 5.0 <= CVSS < 8.0
tool_status[name] = "WARNING"
else:
tool_status[name] = "PASSED" # this tool found nothing >= 5.0
tool_status[name] = "PASSED"

# Print summary of results
logger.info(f"\n{BOLD}========== SCA PIPELINE SUMMARY =========={RESET}")
for name, status in tool_status.items():
if status == "PASSED":
logger.info(f"[{name}]: {GREEN}PASSED{RESET}")
elif status == "WARNING":
logger.warning(f"[{name}]: {YELLOW}WARNING (findings between 5.0 and 8.0){RESET}")
logger.warning(f"[{name}]: {YELLOW}WARNING (findings between {GATE_WARN_THRESHOLD} and {GATE_FAIL_THRESHOLD}){RESET}")
elif status == "ERROR":
logger.error(f"[{name}]: {RED}ERROR (tool did not run correctly){RESET}")
else:
logger.error(f"[{name}]: {RED}FAILED (CVSS >= 8.0 found){RESET}")
logger.info(f"{BOLD}=========================================={RESET}\n")
logger.error(f"[{name}]: {RED}FAILED (CVSS >= {GATE_FAIL_THRESHOLD} found){RESET}")

# Exit with non-zero code if any tool failed the gate
if gate_failed:
Expand Down
8 changes: 6 additions & 2 deletions ci/parse_sarif.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import json
import os
from dataclasses import dataclass

GATE_FAIL_THRESHOLD = float(os.getenv("GATE_FAIL_THRESHOLD", "8.0"))
GATE_WARN_THRESHOLD = float(os.getenv("GATE_WARN_THRESHOLD", "5.0"))

@dataclass
class EvaluationResult:
gate_failed: bool
Expand Down Expand Up @@ -34,6 +38,6 @@ def evaluate(sarif_paths):
max_score = max(max_score, float(score))

return EvaluationResult(
gate_failed=max_score >= 8,
gate_warn=5 <= max_score < 8,
gate_failed=max_score >= GATE_FAIL_THRESHOLD,
gate_warn=GATE_WARN_THRESHOLD <= max_score < GATE_FAIL_THRESHOLD,
)
2 changes: 1 addition & 1 deletion ci/sast_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
SEMGREP_CONFIG_RULESETS = os.getenv(
"SEMGREP_CONFIG_RULESETS",
" semgrep-rules/generic semgrep-rules/problem-based-packs semgrep-rules/bash "
" semgrep-rules/java auto semgrep-rules/yaml semgrep-rules/package_managers p/default "
" semgrep-rules/java auto semgrep-rules/yaml semgrep-rules/package_managers p/default"
).split()
OPENGREP_EXCLUDE = os.getenv(
"OPENGREP_EXCLUDE",
Expand Down
7 changes: 3 additions & 4 deletions ci/sca_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
import logging
import json
from parse_sarif import evaluate
from parse_sarif import evaluate, GATE_FAIL_THRESHOLD, GATE_WARN_THRESHOLD

GREEN = '\033[92m'
RED = '\033[91m'
Expand Down Expand Up @@ -114,12 +114,11 @@ def main():
if status == "PASSED":
logger.info(f"[{name}]: {GREEN}PASSED{RESET}")
elif status == "WARNING":
logger.warning(f"[{name}]: {YELLOW}WARNING (findings between 5.0 and 8.0){RESET}")
logger.warning(f"[{name}]: {YELLOW}WARNING (findings between {GATE_WARN_THRESHOLD} and {GATE_FAIL_THRESHOLD}){RESET}")
elif status == "ERROR":
logger.error(f"[{name}]: {RED}ERROR (tool did not run correctly){RESET}")
else:
logger.error(f"[{name}]: {RED}FAILED (CVSS >= 8.0 found){RESET}")
logger.info(f"{BOLD}=========================================={RESET}\n")
logger.error(f"[{name}]: {RED}FAILED (CVSS >= {GATE_FAIL_THRESHOLD} found){RESET}")

# Exit with non-zero code if any tool failed the gate
if gate_failed:
Expand Down
23 changes: 21 additions & 2 deletions ci/setup-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ OSV_SCANNER_SHA256="${OSV_SCANNER_SHA256:-15314940c10d26af9c6649f150b8a47c1262e8
OPENGREP_VERSION="${OPENGREP_VERSION:-v1.25.0}"
OPENGREP_SHA256="${OPENGREP_SHA256:-9ac4aebb47ba3f7b0d8fc641ac8749cb6c2f253f616131a67d9631e00d4bea33}"

# renovate: datasource=github-tags depName=semgrep/semgrep-rules
# renovate: datasource=git-refs depName=https://github.com/semgrep/semgrep-rules
SEMGREP_RULES_REF="${SEMGREP_RULES_REF:-40b8c63f75dc7c22c8a77482d73bfb864b146f7e}"
SEMGREP_RULES_DIR="semgrep-rules"

# renovate: datasource=github-release-attachments depName=hadolint/hadolint
HADOLINT_VERSION="${HADOLINT_VERSION:-v2.14.0}"
HADOLINT_SHA256="${HADOLINT_SHA256:-6bf226944684f56c84dd014e8b979d27425c0148f61b3bd99bcc6f39e9dc5a47}"

# renovate: datasource=github-release-attachments depName=gitleaks/gitleaks
GITLEAKS_VERSION="${GITLEAKS_VERSION:-v8.30.1}"
GITLEAKS_SHA256="${GITLEAKS_SHA256:-551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb}"

# renovate: datasource=npm depName=@cyclonedx/cyclonedx-npm
CYCLONEDX_NPM_VERSION="${CYCLONEDX_NPM_VERSION:-6.0.0}"

Expand Down Expand Up @@ -128,14 +132,29 @@ if should_install "hadolint"; then
echo "Hadolint installed OK"
fi

# --- Gitleaks -----------------------------------------------------------
if should_install "gitleaks"; then
echo "[setup-tools] Installing Gitleaks ${GITLEAKS_VERSION}"
GITLEAKS_TARBALL="gitleaks_${GITLEAKS_VERSION#v}_linux_x64.tar.gz"
download_and_verify \
"https://github.com/gitleaks/gitleaks/releases/download/${GITLEAKS_VERSION}/${GITLEAKS_TARBALL}" \
"${TMP_DIR}/${GITLEAKS_TARBALL}" \
"${GITLEAKS_SHA256}"
sudo tar -xzf "${TMP_DIR}/${GITLEAKS_TARBALL}" -C /usr/local/bin gitleaks
gitleaks version
echo "Gitleaks installed OK"
fi

# --- SBOM generation ----------------------------------------------------
case "$SBOM_ECOSYSTEM" in
maven)
echo "Generating SBOM for Maven project"
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -q
mvn -B -ntp dependency:resolve -q
mvn -B -ntp org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom -q
;;
npm)
echo "Generating SBOM for NPM project"
npm ci
npx --yes "@cyclonedx/cyclonedx-npm@${CYCLONEDX_NPM_VERSION}" --output-file target/bom.json
;;
none)
Expand Down
Loading
Loading