From 7d55310dfb7de1af81c9ebc2dbf8b0f0234c03f9 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:15:33 -0700 Subject: [PATCH 01/21] dockerize evsrestapi --- .dockerignore | 20 +++++++++++++++++ .gitignore | 1 + Dockerfile | 40 +++++++-------------------------- Makefile | 62 ++++++++++++++++++++++++++++++++++++++++++++++----- README.md | 14 ++++++++++++ build.gradle | 3 +++ 6 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..58b2245d6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +.git +.github +.gradle +.agents +.settings +.vscode +BOOT-INF +WEB-INF +bin +config +docker +gradle +node_modules +src +build/* +!build/libs/ +!build/libs/evsrestapi-*.war +log +report.html +report-docker.html \ No newline at end of file diff --git a/.gitignore b/.gitignore index 44f7caa08..2e9b32199 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ gradle.lockfile UnitTestData/* src/main/main.iml src/test/test.iml +report-docker.html diff --git a/Dockerfile b/Dockerfile index 884787262..0a571b28c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,14 @@ -# Base image -FROM centos:7 -MAINTAINER Frankie Parks +FROM eclipse-temurin:17-jre-jammy -ENV TZ=America/New_York -RUN ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" > /etc/timezone +WORKDIR /app -# Update all packages installed for security -RUN yum makecache && yum update -y +RUN groupadd --system evsapi \ + && useradd --system --gid evsapi --home-dir /app --shell /usr/sbin/nologin evsapi -# Create a user and group used to launch processes -# The user ID 1000 is the default for the first "regular" user on Fedora/RHEL, -# so there is a high chance that this ID will be equal to the current user -# making it easier to use volumes (no permission issues) -RUN groupadd -r evsapi -g 1000 && useradd -u 1000 -r -g evsapi -m -d /opt/evsapi -s /sbin/nologin -c "EVSAPI user" evsapi && \ - chmod 755 /opt/evsapi +COPY build/libs/evsrestapi-*.war /app/evsrestapi.war -# Set the working directory to evsapi' user home directory -WORKDIR /opt/evsapi - -# Install necessary packages -RUN yum -y install java-1.8.0-openjdk-devel unzip && yum clean all - -# Set ENV variable for EVS_SERVER_PORT -ENV EVS_SERVER_PORT="5830" -ENV JAVA_OPTIONS="-Xmx2048m -XX:PermSize=1024m -XX:MaxPermSize=512m" - -# Add file files to image -ADD build/distributions/evsrestapi-1.0.0-SNAPSHOT.zip /opt/evsapi/ -RUN unzip evsrestapi-1.0.0-SNAPSHOT.zip -RUN ln -s evsrestapi-1.0.0-SNAPSHOT evsrestapi - -RUN ls -l - -EXPOSE 5830 USER evsapi -CMD java -jar ./evsrestapi/lib/evsrestapi.war +EXPOSE 8082 + +ENTRYPOINT ["java", "-jar", "/app/evsrestapi.war"] diff --git a/Makefile b/Makefile index 301efb10c..6a2499f25 100644 --- a/Makefile +++ b/Makefile @@ -14,23 +14,75 @@ GIT_COMMIT ?= $(shell echo `git log | grep -m1 -oE '[^ ]+$'`) GIT_COMMITTED_AT ?= $(shell echo `git log -1 --format=%ct`) GIT_BRANCH ?= FULL_VERSION := v$(APP_VERSION)-g$(GIT_VERSION) +DOCKER_TAG := $(shell grep "^version =" build.gradle | sed 's/version = //; s/"//g; s/.RELEASE//') +DOCKER_IMAGE ?= $(SERVICE):$(DOCKER_TAG) +ES_PORT ?= 9201 +ES_SCHEME ?= http +GRAPH_DB_PORT ?= 3030 +GRAPH_DB ?= NCIT2 +DOCKER_PORT ?= 8082 +DOCKER_ES_HOST ?= host.docker.internal +DOCKER_GRAPH_DB_HOST ?= host.docker.internal -.PHONY: build +GRADLEW ?= ./gradlew + +ifeq ($(OS),Windows_NT) +DOCKER ?= docker.exe +else +DOCKER ?= docker +endif + +.PHONY: build docker scandocker rundocker # consider also "docker save..." and "docker load..." to avoid registry. clean: - ./gradlew clean + $(GRADLEW) clean # Build the library without tests # On Windows use: git config core.eol lf build: - ./gradlew clean spotlessApply build spotbugsMain spotbugsTest -x test -x zipFile + $(GRADLEW) clean spotlessApply build spotbugsMain spotbugsTest -x test -x zipFile run: build java -Dspring.profiles.active=local -jar build/libs/evsrestapi*.war +# Build the application image from the executable Spring Boot JAR. +docker: build + $(DOCKER) build --tag "$(DOCKER_IMAGE)" . + +# Report all HIGH and CRITICAL image vulnerabilities with their installed and fixed versions. +# The complete HTML report is written to report-docker.html. +scandocker: docker + trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --format table + trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" + +# Run against Jena/Fuseki and OpenSearch services exposed on the Docker host. +# Override DOCKER_ES_HOST, DOCKER_GRAPH_DB_HOST, ports, or any forwarded setting as needed. +rundocker: docker + $(DOCKER) run --rm --name "$(SERVICE)" -p "$(DOCKER_PORT):8082" \ + -e SPRING_PROFILES_ACTIVE=local \ + -e EVS_SERVER_PORT=8082 \ + -e ES_HOST="$(DOCKER_ES_HOST)" \ + -e ES_PORT="$(ES_PORT)" \ + -e ES_SCHEME="$(ES_SCHEME)" \ + -e GRAPH_DB_HOST="$(DOCKER_GRAPH_DB_HOST)" \ + -e GRAPH_DB_PORT="$(GRAPH_DB_PORT)" \ + -e GRAPH_DB="$(GRAPH_DB)" \ + -e NCI_EVS_ADMIN_KEY \ + -e CONFIG_BASE_URI \ + -e MAIL_HOST \ + -e MAIL_PORT \ + -e MAIL_USER \ + -e MAIL_PASSWORD \ + -e MAIL_AUTH \ + -e MAIL_TLS \ + -e MAIL_RECIPIENT \ + -e RECAPTCHA_KEY \ + -e RECAPTCHA_SECRET \ + "$(DOCKER_IMAGE)" + test: - ./gradlew spotlessCheck -x test + $(GRADLEW) spotlessCheck -x test releasetag: git tag -a "${VERSION}-RC-`/bin/date +%Y-%m-%d`" -m "Release ${VERSION}-RC-`/bin/date +%Y-%m-%d`" @@ -55,7 +107,7 @@ devreset: build ./src/main/bin/devreset.sh ../data/UnitTestData > log 2>&1 & scan: - ./gradlew dependencies --write-locks + $(GRADLEW) dependencies --write-locks trivy fs gradle.lockfile --format template -o report.html --template "@config/trivy/html.tpl" grep CRITICAL report.html /bin/rm -rf gradle/dependency-locks diff --git a/README.md b/README.md index d9593fff0..8f74b95cf 100644 --- a/README.md +++ b/README.md @@ -83,3 +83,17 @@ Information on the build and deployment process for the EVSRESTAPI project ### Run application from command line * Run with `java -Xmx4096 -Dspring.profiles.active=local -jar build/libs/evsrestapi*.jar` + +### Build, scan, and run the application image + +* `make docker` builds the application and creates `evsrestapi:`. +* The image starts the executable WAR, which runs the REST API entry point; the executable JAR is reserved for loader and reindex operations. +* `make scandocker` scans that image with Trivy and writes `report-docker.html`. +* `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, and uses `host.docker.internal` to reach them from the container. +* Override the service hosts or published port when necessary, for example: + + ```bash + make rundocker DOCKER_ES_HOST=host.docker.internal DOCKER_GRAPH_DB_HOST=host.docker.internal DOCKER_PORT=8082 + ``` + + The existing `ES_PORT`, `ES_SCHEME`, `GRAPH_DB_PORT`, and `GRAPH_DB` settings are passed through to the container. Email, reCAPTCHA, and other applicable local configuration environment variables are also forwarded. diff --git a/build.gradle b/build.gradle index aecc086ec..8674f31a0 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,9 @@ ext['log4j2.version'] = '2.25.4' ext['commons-lang3.version'] = '3.18.0' // Override Boot BOM Jackson version to fix CVE-2026-54512 / CVE-2026-54513 (HIGH) ext['jackson-bom.version'] = '2.21.4' +// Override Boot BOM Spring Framework version to fix CVE-2026-41850 (spring-expression), +// CVE-2026-41842 / CVE-2026-41845 (spring-webmvc) (HIGH) +ext['spring-framework.version'] = '6.2.19' // Extra Properties ext { From d13b2ed1fad01e18be4c98eedae67b6c97e2cba9 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:18:50 -0700 Subject: [PATCH 02/21] fix make scan --- Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6a2499f25..7b505caf9 100644 --- a/Makefile +++ b/Makefile @@ -106,9 +106,11 @@ version: devreset: build ./src/main/bin/devreset.sh ../data/UnitTestData > log 2>&1 & +# Report all HIGH and CRITICAL dependency vulnerabilities with installed and fixed versions. +# The complete HTML report is written to report.html. scan: - $(GRADLEW) dependencies --write-locks - trivy fs gradle.lockfile --format template -o report.html --template "@config/trivy/html.tpl" - grep CRITICAL report.html - /bin/rm -rf gradle/dependency-locks - /bin/rm gradle.lockfile + $(GRADLEW) dependencies --write-locks + trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format table + trivy fs gradle.lockfile --scanners vuln --format template -o report.html --template "@config/trivy/html.tpl" + /bin/rm -rf gradle/dependency-locks + /bin/rm gradle.lockfile From 41629d2622b2dc5585b399a986449d5fc3c82e4b Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:26:01 -0700 Subject: [PATCH 03/21] trivy fix --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 8674f31a0..ed383c7ca 100644 --- a/build.gradle +++ b/build.gradle @@ -145,6 +145,12 @@ dependencies { //Java Mail Sender dependency implementation "org.springframework.boot:spring-boot-starter-mail" + // Security override: CVE-2026-41695 + implementation "org.springframework.data:spring-data-commons:3.5.11" + + // Security override: CVE-2026-41006 and CVE-2026-41007. + implementation "org.springframework.hateoas:spring-hateoas:2.5.3" + // aspectjweaver is now managed by Spring Boot BOM (1.9.25.1 in 3.5.x) implementation "org.aspectj:aspectjweaver" implementation "org.apache.commons:commons-text:1.10.0" From b0fc348fa68aa5c3abc49b455a7015d0e4c96918 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:35:20 -0700 Subject: [PATCH 04/21] wrong fix --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ed383c7ca..3af25dada 100644 --- a/build.gradle +++ b/build.gradle @@ -146,7 +146,7 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-mail" // Security override: CVE-2026-41695 - implementation "org.springframework.data:spring-data-commons:3.5.11" + implementation "org.springframework.data:spring-data-commons:3.5.12" // Security override: CVE-2026-41006 and CVE-2026-41007. implementation "org.springframework.hateoas:spring-hateoas:2.5.3" From a3989b384f4f43482adeffc082254bae2682c687 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:38:39 -0700 Subject: [PATCH 05/21] Docker secrets management with config-tree files vs environment variables --- .gitignore | 1 + Makefile | 18 +++++++++++------- README.md | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 2e9b32199..feaf7cc11 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ UnitTestData/* src/main/main.iml src/test/test.iml report-docker.html +/.docker-secrets/ diff --git a/Makefile b/Makefile index 7b505caf9..9245da909 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ GRAPH_DB ?= NCIT2 DOCKER_PORT ?= 8082 DOCKER_ES_HOST ?= host.docker.internal DOCKER_GRAPH_DB_HOST ?= host.docker.internal +DOCKER_SECRETS_DIR ?= $(CURDIR)/.docker-secrets GRADLEW ?= ./gradlew @@ -32,7 +33,7 @@ else DOCKER ?= docker endif -.PHONY: build docker scandocker rundocker +.PHONY: build docker scandocker rundocker check-docker-secrets # consider also "docker save..." and "docker load..." to avoid registry. clean: @@ -56,10 +57,17 @@ scandocker: docker trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --format table trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" +# Require a local, ignored directory of Spring Boot config-tree secret files. +check-docker-secrets: + @test -d "$(DOCKER_SECRETS_DIR)" || (echo "ERROR: Create $(DOCKER_SECRETS_DIR) and add secret files before running rundocker." && exit 1) + # Run against Jena/Fuseki and OpenSearch services exposed on the Docker host. -# Override DOCKER_ES_HOST, DOCKER_GRAPH_DB_HOST, ports, or any forwarded setting as needed. -rundocker: docker +# Secrets are mounted read-only and imported from /run/secrets rather than passed as environment variables. +# Override DOCKER_ES_HOST, DOCKER_GRAPH_DB_HOST, ports, or the secrets directory as needed. +rundocker: docker check-docker-secrets $(DOCKER) run --rm --name "$(SERVICE)" -p "$(DOCKER_PORT):8082" \ + --mount type=bind,src="$(DOCKER_SECRETS_DIR)",dst=/run/secrets,readonly \ + -e SPRING_CONFIG_IMPORT=optional:configtree:/run/secrets/ \ -e SPRING_PROFILES_ACTIVE=local \ -e EVS_SERVER_PORT=8082 \ -e ES_HOST="$(DOCKER_ES_HOST)" \ @@ -68,17 +76,13 @@ rundocker: docker -e GRAPH_DB_HOST="$(DOCKER_GRAPH_DB_HOST)" \ -e GRAPH_DB_PORT="$(GRAPH_DB_PORT)" \ -e GRAPH_DB="$(GRAPH_DB)" \ - -e NCI_EVS_ADMIN_KEY \ -e CONFIG_BASE_URI \ -e MAIL_HOST \ -e MAIL_PORT \ - -e MAIL_USER \ - -e MAIL_PASSWORD \ -e MAIL_AUTH \ -e MAIL_TLS \ -e MAIL_RECIPIENT \ -e RECAPTCHA_KEY \ - -e RECAPTCHA_SECRET \ "$(DOCKER_IMAGE)" test: diff --git a/README.md b/README.md index 8f74b95cf..ac3ca0072 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ Information on the build and deployment process for the EVSRESTAPI project * The image starts the executable WAR, which runs the REST API entry point; the executable JAR is reserved for loader and reindex operations. * `make scandocker` scans that image with Trivy and writes `report-docker.html`. * `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, and uses `host.docker.internal` to reach them from the container. +* Before running it, create the ignored `.docker-secrets` directory. Each file is mounted read-only at `/run/secrets` and is imported by Spring Boot using its filename as the property name. Put credentials and secrets in `NCI_EVS_ADMIN_KEY`, `MAIL_USER`, `MAIL_PASSWORD`, and `RECAPTCHA_SECRET`; write each value without a trailing newline. Non-sensitive settings such as `MAIL_HOST`, `MAIL_PORT`, and `RECAPTCHA_KEY` continue to be forwarded from the host environment. +* The secret values are not passed as container environment variables, so they do not appear in `docker inspect`. Docker daemon administrators can still access a running container and must remain trusted. * Override the service hosts or published port when necessary, for example: ```bash From bacaa126654ea62867e362dc141b5aa57b1a1871 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 15:56:34 -0700 Subject: [PATCH 06/21] cache gradle in makefile --- .github/workflows/makefile.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index e53a8acf3..e935996ed 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -21,5 +21,10 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Gradle cache + uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + - name: Run Tests for PR - run: make test \ No newline at end of file + run: make test From 20b0a1f44f2525076ad1317e2561cfef9873debb Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 16:02:36 -0700 Subject: [PATCH 07/21] docker image trivy scan; remove lockfiles on failure; cache trivy --- .github/workflows/trivy-scan.yml | 119 +++++++++++++++++++------------ 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 0690cfbe5..cb308fec5 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -10,54 +10,83 @@ on: - develop-* - master - workflow_dispatch: # Allows manual runs from the GitHub UI + workflow_dispatch: # Allows manual runs from the GitHub UI jobs: trivy-scan: runs-on: ubuntu-latest - steps: - # Checkout the pull request code - - name: Checkout code - uses: actions/checkout@v4 - - # Set up Java 17 - - name: Set up Java 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' # Use Eclipse Temurin distribution for Java - - # Install Trivy if not cached - - name: Install Trivy - run: | - TRIVY_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | jq -r .tag_name) - wget https://github.com/aquasecurity/trivy/releases/download/${TRIVY_LATEST_VERSION}/trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb - sudo dpkg -i trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb - - # Run Trivy to scan the repository or container image - - name: Run Trivy scan - id: trivy-scan - run: | - ./gradlew dependencies --write-locks - trivy fs gradle.lockfile --format json --output trivy_report.json --severity HIGH,CRITICAL - /bin/rm -rf gradle/dependency-locks - /bin/rm gradle.lockfile - env: - TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db - TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db - - # Check for high or critical vulnerabilities and output if found - - name: Check and output vulnerabilities - id: check-vulnerabilities - run: | - vuln_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL")] | length' trivy_report.json) - echo "Vulnerability count: $vuln_count" - if [ "$vuln_count" -gt 0 ]; then - echo "High or Critical vulnerabilities found!" - jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL") | "\(.VulnerabilityID): \(.PkgName) - Severity: \(.Severity) - Installed Version: \(.InstalledVersion) - Fixed Version: \(.FixedVersion)"' trivy_report.json - exit 1 - else - echo "No high or critical vulnerabilities found." - fi + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle cache + uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + + - name: Generate dependency lock for scanning + run: ./gradlew dependencies --write-locks + + - name: Run Trivy scan + id: trivy-scan + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: fs + scan-ref: gradle.lockfile + scanners: vuln + severity: HIGH,CRITICAL + format: json + output: trivy_report.json + exit-code: '0' + env: + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db + + - name: Check and output vulnerabilities + id: check-vulnerabilities + run: | + vuln_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL")] | length' trivy_report.json) + echo "Vulnerability count: $vuln_count" + if [ "$vuln_count" -gt 0 ]; then + echo "High or Critical vulnerabilities found!" + jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL") | "\(.VulnerabilityID): \(.PkgName) - Severity: \(.Severity) - Installed Version: \(.InstalledVersion) - Fixed Version: \(.FixedVersion)"' trivy_report.json + exit 1 + else + echo "No high or critical vulnerabilities found." + fi + + - name: Remove generated dependency locks + if: always() + run: | + rm -rf gradle/dependency-locks + rm -f gradle.lockfile + image-scan: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle cache + uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + + - name: Build and strictly scan application image + run: make scandocker-strict + env: + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db From f7b7fa5f026b0bb1e6f7bc1d00b738422366fb52 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 16:04:35 -0700 Subject: [PATCH 08/21] add strict scans + linux parameter --- Makefile | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 9245da909..8ab88c1a8 100644 --- a/Makefile +++ b/Makefile @@ -29,11 +29,13 @@ GRADLEW ?= ./gradlew ifeq ($(OS),Windows_NT) DOCKER ?= docker.exe +DOCKER_HOST_GATEWAY_ARG := else DOCKER ?= docker +DOCKER_HOST_GATEWAY_ARG := --add-host host.docker.internal:host-gateway endif -.PHONY: build docker scandocker rundocker check-docker-secrets +.PHONY: build docker scandocker scandocker-strict rundocker check-docker-secrets scan scan-strict # consider also "docker save..." and "docker load..." to avoid registry. clean: @@ -57,6 +59,13 @@ scandocker: docker trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --format table trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" +# Produce the same report as scandocker, then fail if HIGH or CRITICAL vulnerabilities are found. +scandocker-strict: docker + @status=0; \ + trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --exit-code 1 --format table || status=$$?; \ + trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" || exit $$?; \ + exit $$status + # Require a local, ignored directory of Spring Boot config-tree secret files. check-docker-secrets: @test -d "$(DOCKER_SECRETS_DIR)" || (echo "ERROR: Create $(DOCKER_SECRETS_DIR) and add secret files before running rundocker." && exit 1) @@ -65,7 +74,7 @@ check-docker-secrets: # Secrets are mounted read-only and imported from /run/secrets rather than passed as environment variables. # Override DOCKER_ES_HOST, DOCKER_GRAPH_DB_HOST, ports, or the secrets directory as needed. rundocker: docker check-docker-secrets - $(DOCKER) run --rm --name "$(SERVICE)" -p "$(DOCKER_PORT):8082" \ + $(DOCKER) run --rm --name "$(SERVICE)" -p "$(DOCKER_PORT):8082" $(DOCKER_HOST_GATEWAY_ARG) \ --mount type=bind,src="$(DOCKER_SECRETS_DIR)",dst=/run/secrets,readonly \ -e SPRING_CONFIG_IMPORT=optional:configtree:/run/secrets/ \ -e SPRING_PROFILES_ACTIVE=local \ @@ -111,10 +120,19 @@ devreset: build ./src/main/bin/devreset.sh ../data/UnitTestData > log 2>&1 & # Report all HIGH and CRITICAL dependency vulnerabilities with installed and fixed versions. -# The complete HTML report is written to report.html. +# The complete HTML report is written to report.html. Generated dependency locks are always removed. scan: - $(GRADLEW) dependencies --write-locks - trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format table + @set -e; \ + trap 'rm -rf gradle/dependency-locks gradle.lockfile' EXIT; \ + $(GRADLEW) dependencies --write-locks; \ + trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format table; \ trivy fs gradle.lockfile --scanners vuln --format template -o report.html --template "@config/trivy/html.tpl" - /bin/rm -rf gradle/dependency-locks - /bin/rm gradle.lockfile + +# Produce the same report as scan, then fail if HIGH or CRITICAL vulnerabilities are found. +scan-strict: + @trap 'rm -rf gradle/dependency-locks gradle.lockfile' EXIT; \ + $(GRADLEW) dependencies --write-locks || exit $$?; \ + status=0; \ + trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --exit-code 1 --format table || status=$$?; \ + trivy fs gradle.lockfile --scanners vuln --format template -o report.html --template "@config/trivy/html.tpl" || exit $$?; \ + exit $$status From 8f505eaebd89a619aa2fd9c56db4e14446955e88 Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 16:04:47 -0700 Subject: [PATCH 09/21] readme edit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac3ca0072..d99861000 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,8 @@ Information on the build and deployment process for the EVSRESTAPI project * `make docker` builds the application and creates `evsrestapi:`. * The image starts the executable WAR, which runs the REST API entry point; the executable JAR is reserved for loader and reindex operations. -* `make scandocker` scans that image with Trivy and writes `report-docker.html`. -* `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, and uses `host.docker.internal` to reach them from the container. +* `make scandocker` and `make scan` print HIGH/CRITICAL vulnerability tables and write full HTML reports. Their `scandocker-strict` and `scan-strict` counterparts also fail when HIGH or CRITICAL findings exist, for use in CI. +* `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, uses `host.docker.internal` on Docker Desktop, and adds the host-gateway mapping automatically on Linux. * Before running it, create the ignored `.docker-secrets` directory. Each file is mounted read-only at `/run/secrets` and is imported by Spring Boot using its filename as the property name. Put credentials and secrets in `NCI_EVS_ADMIN_KEY`, `MAIL_USER`, `MAIL_PASSWORD`, and `RECAPTCHA_SECRET`; write each value without a trailing newline. Non-sensitive settings such as `MAIL_HOST`, `MAIL_PORT`, and `RECAPTCHA_KEY` continue to be forwarded from the host environment. * The secret values are not passed as container environment variables, so they do not appear in `docker inspect`. Docker daemon administrators can still access a running container and must remain trusted. * Override the service hosts or published port when necessary, for example: From 3674b059b61fbbce9002e2c668fd6ede7e4ce36b Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 16:13:37 -0700 Subject: [PATCH 10/21] fix image-scan trivy and gradle caching --- .github/workflows/makefile.yml | 2 -- .github/workflows/trivy-scan.yml | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index e935996ed..2f02c97f9 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -23,8 +23,6 @@ jobs: - name: Setup Gradle cache uses: gradle/actions/setup-gradle@v6 - with: - cache-provider: basic - name: Run Tests for PR run: make test diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index cb308fec5..57c7dfa57 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -28,8 +28,6 @@ jobs: - name: Setup Gradle cache uses: gradle/actions/setup-gradle@v6 - with: - cache-provider: basic - name: Generate dependency lock for scanning run: ./gradlew dependencies --write-locks @@ -82,11 +80,19 @@ jobs: - name: Setup Gradle cache uses: gradle/actions/setup-gradle@v6 - with: - cache-provider: basic - - name: Build and strictly scan application image - run: make scandocker-strict + - name: Build application image + run: make docker DOCKER_IMAGE=evsrestapi:ci + + - name: Strictly scan application image + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: image + image-ref: evsrestapi:ci + scanners: vuln + severity: HIGH,CRITICAL + format: table + exit-code: '1' env: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db From 0bbc4c1e1607902ab38f16d0039bb44a5ee40cec Mon Sep 17 00:00:00 2001 From: peter-va Date: Fri, 31 Jul 2026 16:23:12 -0700 Subject: [PATCH 11/21] output scanning results for docker image --- .github/workflows/trivy-scan.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 57c7dfa57..274c6ad68 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -84,15 +84,30 @@ jobs: - name: Build application image run: make docker DOCKER_IMAGE=evsrestapi:ci - - name: Strictly scan application image + - name: Scan application image + id: image-trivy-scan uses: aquasecurity/trivy-action@v0.36.0 with: scan-type: image image-ref: evsrestapi:ci scanners: vuln severity: HIGH,CRITICAL - format: table - exit-code: '1' + format: json + output: trivy_image_report.json + exit-code: '0' env: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db + + - name: Check and output image vulnerabilities + id: check-image-vulnerabilities + run: | + vuln_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL")] | length' trivy_image_report.json) + echo "Image vulnerability count: $vuln_count" + if [ "$vuln_count" -gt 0 ]; then + echo "High or Critical image vulnerabilities found!" + jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" or .Severity == "CRITICAL") | "\(.VulnerabilityID): \(.PkgName) - Severity: \(.Severity) - Installed Version: \(.InstalledVersion) - Fixed Version: \(.FixedVersion)"' trivy_image_report.json + exit 1 + else + echo "No high or critical image vulnerabilities found." + fi From 791b6316ec30b2c5158027f393d646997bf3150c Mon Sep 17 00:00:00 2001 From: peter-va Date: Mon, 3 Aug 2026 12:54:17 -0700 Subject: [PATCH 12/21] version mismatch --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3af25dada..51b1b4deb 100644 --- a/build.gradle +++ b/build.gradle @@ -106,7 +106,7 @@ dependencies { // Use version 2 with spring boot 3 // custom location => springdoc.swagger-ui.path=/swagger-ui/index.html - implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.17" implementation "org.springframework.boot:spring-boot-devtools" implementation "org.springframework.boot:spring-boot-starter-actuator" From 74f18570e5fbe1e5098141ffc3d4c5f5689cf088 Mon Sep 17 00:00:00 2001 From: peter-va Date: Mon, 3 Aug 2026 12:55:05 -0700 Subject: [PATCH 13/21] don't rebuild docker unless warranted by changes --- Makefile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8ab88c1a8..809a5c7ed 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,9 @@ DOCKER_PORT ?= 8082 DOCKER_ES_HOST ?= host.docker.internal DOCKER_GRAPH_DB_HOST ?= host.docker.internal DOCKER_SECRETS_DIR ?= $(CURDIR)/.docker-secrets +DOCKER_IMAGE_STAMP := build/.docker-image-$(subst :,_,$(subst /,_,$(DOCKER_IMAGE))) +DOCKER_BUILD_INPUTS := Makefile Dockerfile .dockerignore build.gradle gradle.properties gradlew $(wildcard gradle/wrapper/gradle-wrapper.properties) $(shell git ls-files --cached --others --exclude-standard src/main) +DOCKER_WAR := $(wildcard build/libs/evsrestapi-*.war) GRADLEW ?= ./gradlew @@ -49,9 +52,15 @@ build: run: build java -Dspring.profiles.active=local -jar build/libs/evsrestapi*.war -# Build the application image from the executable Spring Boot JAR. -docker: build +# Build the application image only when its runtime inputs have changed. +docker: $(DOCKER_IMAGE_STAMP) + @$(DOCKER) image inspect "$(DOCKER_IMAGE)" > /dev/null 2>&1 || { rm -f "$(DOCKER_IMAGE_STAMP)"; $(MAKE) --no-print-directory "$(DOCKER_IMAGE_STAMP)"; } + @echo "Docker image $(DOCKER_IMAGE) is up to date." + +$(DOCKER_IMAGE_STAMP): $(DOCKER_BUILD_INPUTS) $(DOCKER_WAR) + $(GRADLEW) bootWar $(DOCKER) build --tag "$(DOCKER_IMAGE)" . + @touch "$@" # Report all HIGH and CRITICAL image vulnerabilities with their installed and fixed versions. # The complete HTML report is written to report-docker.html. From 489e00a4199ae4d44a19bcd3c98906f02207cc30 Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 13:02:51 -0700 Subject: [PATCH 14/21] remove nonfunctional gradle caching --- .github/workflows/trivy-scan.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 274c6ad68..6121b15c0 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -26,9 +26,6 @@ jobs: java-version: '17' distribution: 'temurin' - - name: Setup Gradle cache - uses: gradle/actions/setup-gradle@v6 - - name: Generate dependency lock for scanning run: ./gradlew dependencies --write-locks @@ -78,9 +75,6 @@ jobs: java-version: '17' distribution: 'temurin' - - name: Setup Gradle cache - uses: gradle/actions/setup-gradle@v6 - - name: Build application image run: make docker DOCKER_IMAGE=evsrestapi:ci From cd1e0aff3e21719a931a510526d12cbca3eb892b Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 13:06:03 -0700 Subject: [PATCH 15/21] missed one --- .github/workflows/makefile.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index 2f02c97f9..002080e45 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -21,8 +21,5 @@ jobs: distribution: 'temurin' java-version: '17' - - name: Setup Gradle cache - uses: gradle/actions/setup-gradle@v6 - - name: Run Tests for PR run: make test From 29ca98f1ffa783b35a7112d7b0764fca9fa14872 Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 13:16:27 -0700 Subject: [PATCH 16/21] trivy caching doesn't save any actual time --- .github/workflows/trivy-scan.yml | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 6121b15c0..2470df421 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -29,17 +29,15 @@ jobs: - name: Generate dependency lock for scanning run: ./gradlew dependencies --write-locks + - name: Install Trivy + run: | + TRIVY_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | jq -r .tag_name) + wget https://github.com/aquasecurity/trivy/releases/download/${TRIVY_LATEST_VERSION}/trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + sudo dpkg -i trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + - name: Run Trivy scan id: trivy-scan - uses: aquasecurity/trivy-action@v0.36.0 - with: - scan-type: fs - scan-ref: gradle.lockfile - scanners: vuln - severity: HIGH,CRITICAL - format: json - output: trivy_report.json - exit-code: '0' + run: trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format json --output trivy_report.json --exit-code 0 env: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db @@ -78,17 +76,15 @@ jobs: - name: Build application image run: make docker DOCKER_IMAGE=evsrestapi:ci + - name: Install Trivy + run: | + TRIVY_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | jq -r .tag_name) + wget https://github.com/aquasecurity/trivy/releases/download/${TRIVY_LATEST_VERSION}/trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + sudo dpkg -i trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + - name: Scan application image id: image-trivy-scan - uses: aquasecurity/trivy-action@v0.36.0 - with: - scan-type: image - image-ref: evsrestapi:ci - scanners: vuln - severity: HIGH,CRITICAL - format: json - output: trivy_image_report.json - exit-code: '0' + run: trivy image evsrestapi:ci --scanners vuln --severity HIGH,CRITICAL --format json --output trivy_image_report.json --exit-code 0 env: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db From 5ad5f57a028f921c2207085666a6ce208f5be727 Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 13:18:37 -0700 Subject: [PATCH 17/21] re-add comments --- .github/workflows/trivy-scan.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 2470df421..741ac61ca 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -17,24 +17,29 @@ jobs: runs-on: ubuntu-latest steps: + # Checkout code - name: Checkout code uses: actions/checkout@v4 + # Set up Java 17 - name: Set up Java 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' + # Generate dependency lock for scanning - name: Generate dependency lock for scanning run: ./gradlew dependencies --write-locks + # Install Trivy - name: Install Trivy run: | TRIVY_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | jq -r .tag_name) wget https://github.com/aquasecurity/trivy/releases/download/${TRIVY_LATEST_VERSION}/trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb sudo dpkg -i trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + # Run Trivy scan - name: Run Trivy scan id: trivy-scan run: trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format json --output trivy_report.json --exit-code 0 @@ -42,6 +47,7 @@ jobs: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db + # Check and output vulnerabilities - name: Check and output vulnerabilities id: check-vulnerabilities run: | @@ -55,6 +61,7 @@ jobs: echo "No high or critical vulnerabilities found." fi + # Remove generated dependency locks - name: Remove generated dependency locks if: always() run: | @@ -64,24 +71,29 @@ jobs: runs-on: ubuntu-latest steps: + # Checkout code - name: Checkout code uses: actions/checkout@v4 + # Set up Java 17 - name: Set up Java 17 uses: actions/setup-java@v4 with: java-version: '17' - distribution: 'temurin' + distribution: 'temurin' # Use Eclipse Temurin distribution for Java + # Build application image - name: Build application image run: make docker DOCKER_IMAGE=evsrestapi:ci + # Install Trivy - name: Install Trivy run: | TRIVY_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | jq -r .tag_name) wget https://github.com/aquasecurity/trivy/releases/download/${TRIVY_LATEST_VERSION}/trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb sudo dpkg -i trivy_${TRIVY_LATEST_VERSION#v}_Linux-64bit.deb + # Scan application image - name: Scan application image id: image-trivy-scan run: trivy image evsrestapi:ci --scanners vuln --severity HIGH,CRITICAL --format json --output trivy_image_report.json --exit-code 0 @@ -89,6 +101,7 @@ jobs: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db + # Check and output image vulnerabilities - name: Check and output image vulnerabilities id: check-image-vulnerabilities run: | From 808491b62afcaf4cfdc4f5fbb7c5bfaacb7689ce Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 13:44:17 -0700 Subject: [PATCH 18/21] clarify make rundocker as intended use --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d99861000..483edc4ef 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Information on the build and deployment process for the EVSRESTAPI project * The image starts the executable WAR, which runs the REST API entry point; the executable JAR is reserved for loader and reindex operations. * `make scandocker` and `make scan` print HIGH/CRITICAL vulnerability tables and write full HTML reports. Their `scandocker-strict` and `scan-strict` counterparts also fail when HIGH or CRITICAL findings exist, for use in CI. * `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, uses `host.docker.internal` on Docker Desktop, and adds the host-gateway mapping automatically on Linux. +* `make rundocker` is the supported way to start the image locally. A direct `docker run` must supply equivalent Spring profile, port, service-host, and secret configuration; otherwise the image uses the default application settings rather than the local setup. * Before running it, create the ignored `.docker-secrets` directory. Each file is mounted read-only at `/run/secrets` and is imported by Spring Boot using its filename as the property name. Put credentials and secrets in `NCI_EVS_ADMIN_KEY`, `MAIL_USER`, `MAIL_PASSWORD`, and `RECAPTCHA_SECRET`; write each value without a trailing newline. Non-sensitive settings such as `MAIL_HOST`, `MAIL_PORT`, and `RECAPTCHA_KEY` continue to be forwarded from the host environment. * The secret values are not passed as container environment variables, so they do not appear in `docker inspect`. Docker daemon administrators can still access a running container and must remain trusted. * Override the service hosts or published port when necessary, for example: From 62a4dedc0d36446acc1c59501b4e4e4c70be6b5f Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 21:51:59 -0700 Subject: [PATCH 19/21] move gradle into docker and fix linux compatibility --- .dockerignore | 5 +---- .github/workflows/trivy-scan.yml | 6 ------ Dockerfile | 16 ++++++++++++++-- Makefile | 32 ++++++++++---------------------- README.md | 5 +++-- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/.dockerignore b/.dockerignore index 58b2245d6..33a608996 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,10 +11,7 @@ config docker gradle node_modules -src -build/* -!build/libs/ -!build/libs/evsrestapi-*.war +build log report.html report-docker.html \ No newline at end of file diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 741ac61ca..cc4e31a16 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -75,12 +75,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - # Set up Java 17 - - name: Set up Java 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' # Use Eclipse Temurin distribution for Java # Build application image - name: Build application image diff --git a/Dockerfile b/Dockerfile index 0a571b28c..fd7cd7ae4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,15 @@ +# Build the deployable WAR in an isolated Linux Gradle environment. +FROM gradle:8.14.2-jdk17 AS gradle-build + +WORKDIR /workspace + +COPY --chown=gradle:gradle build.gradle gradle.properties ./ +RUN gradle --no-daemon dependencies + +COPY --chown=gradle:gradle src/main ./src/main +RUN gradle --no-daemon bootWar -x test + +# Run only the packaged application as an unprivileged user. FROM eclipse-temurin:17-jre-jammy WORKDIR /app @@ -5,10 +17,10 @@ WORKDIR /app RUN groupadd --system evsapi \ && useradd --system --gid evsapi --home-dir /app --shell /usr/sbin/nologin evsapi -COPY build/libs/evsrestapi-*.war /app/evsrestapi.war +COPY --from=gradle-build --chown=evsapi:evsapi /workspace/build/libs/evsrestapi-*.war /app/evsrestapi.war USER evsapi EXPOSE 8082 -ENTRYPOINT ["java", "-jar", "/app/evsrestapi.war"] +ENTRYPOINT ["java", "-jar", "/app/evsrestapi.war"] \ No newline at end of file diff --git a/Makefile b/Makefile index 809a5c7ed..61107f539 100644 --- a/Makefile +++ b/Makefile @@ -25,8 +25,7 @@ DOCKER_ES_HOST ?= host.docker.internal DOCKER_GRAPH_DB_HOST ?= host.docker.internal DOCKER_SECRETS_DIR ?= $(CURDIR)/.docker-secrets DOCKER_IMAGE_STAMP := build/.docker-image-$(subst :,_,$(subst /,_,$(DOCKER_IMAGE))) -DOCKER_BUILD_INPUTS := Makefile Dockerfile .dockerignore build.gradle gradle.properties gradlew $(wildcard gradle/wrapper/gradle-wrapper.properties) $(shell git ls-files --cached --others --exclude-standard src/main) -DOCKER_WAR := $(wildcard build/libs/evsrestapi-*.war) +DOCKER_BUILD_INPUTS := Makefile Dockerfile .dockerignore build.gradle gradle.properties $(shell git ls-files --cached --others --exclude-standard src/main) GRADLEW ?= ./gradlew @@ -38,7 +37,7 @@ DOCKER ?= docker DOCKER_HOST_GATEWAY_ARG := --add-host host.docker.internal:host-gateway endif -.PHONY: build docker scandocker scandocker-strict rundocker check-docker-secrets scan scan-strict +.PHONY: build docker dockerpush scandocker rundocker check-docker-secrets scan # consider also "docker save..." and "docker load..." to avoid registry. clean: @@ -52,28 +51,26 @@ build: run: build java -Dspring.profiles.active=local -jar build/libs/evsrestapi*.war -# Build the application image only when its runtime inputs have changed. +# Build the application and image in an isolated Linux/AMD64 Docker build environment. docker: $(DOCKER_IMAGE_STAMP) @$(DOCKER) image inspect "$(DOCKER_IMAGE)" > /dev/null 2>&1 || { rm -f "$(DOCKER_IMAGE_STAMP)"; $(MAKE) --no-print-directory "$(DOCKER_IMAGE_STAMP)"; } @echo "Docker image $(DOCKER_IMAGE) is up to date." -$(DOCKER_IMAGE_STAMP): $(DOCKER_BUILD_INPUTS) $(DOCKER_WAR) - $(GRADLEW) bootWar - $(DOCKER) build --tag "$(DOCKER_IMAGE)" . +$(DOCKER_IMAGE_STAMP): $(DOCKER_BUILD_INPUTS) + $(DOCKER) build --platform linux/amd64 --tag "$(DOCKER_IMAGE)" . @touch "$@" +# Build and push a Linux/AMD64 image. Override DOCKER_IMAGE with a registry-qualified image name. +dockerpush: + $(DOCKER) buildx build --platform linux/amd64 --tag "$(DOCKER_IMAGE)" --push . + # Report all HIGH and CRITICAL image vulnerabilities with their installed and fixed versions. # The complete HTML report is written to report-docker.html. scandocker: docker trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --format table trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" -# Produce the same report as scandocker, then fail if HIGH or CRITICAL vulnerabilities are found. -scandocker-strict: docker - @status=0; \ - trivy image "$(DOCKER_IMAGE)" --scanners vuln --severity HIGH,CRITICAL --exit-code 1 --format table || status=$$?; \ - trivy image "$(DOCKER_IMAGE)" --scanners vuln --format template -o report-docker.html --template "@config/trivy/html.tpl" || exit $$?; \ - exit $$status + # Require a local, ignored directory of Spring Boot config-tree secret files. check-docker-secrets: @@ -136,12 +133,3 @@ scan: $(GRADLEW) dependencies --write-locks; \ trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --format table; \ trivy fs gradle.lockfile --scanners vuln --format template -o report.html --template "@config/trivy/html.tpl" - -# Produce the same report as scan, then fail if HIGH or CRITICAL vulnerabilities are found. -scan-strict: - @trap 'rm -rf gradle/dependency-locks gradle.lockfile' EXIT; \ - $(GRADLEW) dependencies --write-locks || exit $$?; \ - status=0; \ - trivy fs gradle.lockfile --scanners vuln --severity HIGH,CRITICAL --exit-code 1 --format table || status=$$?; \ - trivy fs gradle.lockfile --scanners vuln --format template -o report.html --template "@config/trivy/html.tpl" || exit $$?; \ - exit $$status diff --git a/README.md b/README.md index 483edc4ef..2ca9f2201 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,10 @@ Information on the build and deployment process for the EVSRESTAPI project ### Build, scan, and run the application image -* `make docker` builds the application and creates `evsrestapi:`. +* `make docker` builds the WAR inside a Linux/AMD64 Docker build stage and creates `evsrestapi:` without using a local Gradle installation. * The image starts the executable WAR, which runs the REST API entry point; the executable JAR is reserved for loader and reindex operations. -* `make scandocker` and `make scan` print HIGH/CRITICAL vulnerability tables and write full HTML reports. Their `scandocker-strict` and `scan-strict` counterparts also fail when HIGH or CRITICAL findings exist, for use in CI. +* `make scandocker` and `make scan` print HIGH/CRITICAL vulnerability tables and write full HTML reports. GitHub Actions enforces HIGH/CRITICAL findings in CI. +* `make dockerpush DOCKER_IMAGE=/:` builds and pushes a Linux/AMD64 image with Docker Buildx. * `make rundocker` runs the image on port 8082 using the `local` Spring profile. It assumes Jena/Fuseki and OpenSearch are already running on the host, uses `host.docker.internal` on Docker Desktop, and adds the host-gateway mapping automatically on Linux. * `make rundocker` is the supported way to start the image locally. A direct `docker run` must supply equivalent Spring profile, port, service-host, and secret configuration; otherwise the image uses the default application settings rather than the local setup. * Before running it, create the ignored `.docker-secrets` directory. Each file is mounted read-only at `/run/secrets` and is imported by Spring Boot using its filename as the property name. Put credentials and secrets in `NCI_EVS_ADMIN_KEY`, `MAIL_USER`, `MAIL_PASSWORD`, and `RECAPTCHA_SECRET`; write each value without a trailing newline. Non-sensitive settings such as `MAIL_HOST`, `MAIL_PORT`, and `RECAPTCHA_KEY` continue to be forwarded from the host environment. From 3e064f81020cc090f2fbe528fce104248ef59d5d Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 22:25:28 -0700 Subject: [PATCH 20/21] forgot a mkdir --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 61107f539..fb82bc01c 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ docker: $(DOCKER_IMAGE_STAMP) $(DOCKER_IMAGE_STAMP): $(DOCKER_BUILD_INPUTS) $(DOCKER) build --platform linux/amd64 --tag "$(DOCKER_IMAGE)" . + @mkdir -p "$(dir $@)" @touch "$@" # Build and push a Linux/AMD64 image. Override DOCKER_IMAGE with a registry-qualified image name. From 205930b5f1d0eb87b45aa5333d9aa71f34144bc5 Mon Sep 17 00:00:00 2001 From: peter-va Date: Wed, 5 Aug 2026 22:36:07 -0700 Subject: [PATCH 21/21] no reason for makefile to be in the build inputs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fb82bc01c..6c0e4c79a 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ DOCKER_ES_HOST ?= host.docker.internal DOCKER_GRAPH_DB_HOST ?= host.docker.internal DOCKER_SECRETS_DIR ?= $(CURDIR)/.docker-secrets DOCKER_IMAGE_STAMP := build/.docker-image-$(subst :,_,$(subst /,_,$(DOCKER_IMAGE))) -DOCKER_BUILD_INPUTS := Makefile Dockerfile .dockerignore build.gradle gradle.properties $(shell git ls-files --cached --others --exclude-standard src/main) +DOCKER_BUILD_INPUTS := Dockerfile .dockerignore build.gradle gradle.properties $(shell git ls-files --cached --others --exclude-standard src/main) GRADLEW ?= ./gradlew