Skip to content

chore: add Trivy security scanning and fix non-root container users#3082

Open
tac0turtle wants to merge 11 commits intomainfrom
marko/add-trivy-scan
Open

chore: add Trivy security scanning and fix non-root container users#3082
tac0turtle wants to merge 11 commits intomainfrom
marko/add-trivy-scan

Conversation

@tac0turtle
Copy link
Contributor

@tac0turtle tac0turtle commented Feb 18, 2026

Add make trivy-scan (filesystem + image) via dockerized Trivy. Fix DS-0002 (HIGH): run evm, testapp, and local-da containers as non-root ev-node user, consistent with the grpc Dockerfile.

Overview

add container users

Summary by CodeRabbit

  • Breaking Changes

    • Selected containers now run as a non-root user; existing bind mounts or volumes with root-owned files may require ownership adjustments. Volume names/mappings for the evm stack were updated and may need adjustment.
  • New Features

    • Automated Trivy security scans for repository filesystem and configured images with progress output.
  • Documentation

    • Migration guide added for running containers as non-root (includes Docker Compose & Kubernetes).

Add `make trivy-scan` (filesystem + image) via dockerized Trivy.
Fix DS-0002 (HIGH): run evm, testapp, and local-da containers as
non-root `ev-node` user, consistent with the grpc Dockerfile.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@tac0turtle tac0turtle marked this pull request as ready for review February 18, 2026 14:43
@tac0turtle tac0turtle requested a review from chatton February 18, 2026 14:43
@tac0turtle tac0turtle requested a review from auricom February 18, 2026 14:43
@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedMar 16, 2026, 2:37 PM

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tac0turtle, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the project's security by introducing automated vulnerability scanning with Trivy and hardening several Docker containers to run with reduced privileges. These changes aim to proactively identify and mitigate security risks, ensuring a more robust and secure application environment.

Highlights

  • Security Scanning: Integrated Trivy security scanning into the build process, allowing for both filesystem and Docker image vulnerability, misconfiguration, and secret scanning via new make trivy-scan targets.
  • Container Security Hardening: Addressed the DS-0002 (HIGH) vulnerability by configuring the evm, testapp, and local-da Docker containers to run as a dedicated non-root ev-node user, enhancing security posture.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Makefile
    • Included the new scripts/security.mk makefile.
  • apps/evm/Dockerfile
    • Created a new ev-node user and group with UID/GID 1000.
    • Updated the working directory from /root to /home/ev-node.
    • Modified the chmod command to also chown the /home/ev-node directory to ev-node.
    • Set the container to run as the ev-node user.
  • apps/testapp/Dockerfile
    • Created a new ev-node user and group with UID/GID 1000.
    • Updated the working directory from /apps to /home/ev-node.
    • Changed ownership of /home/ev-node to ev-node.
    • Set the container to run as the ev-node user.
  • scripts/security.mk
    • Added a new makefile for Trivy security scanning.
    • Defined variables for Trivy image, severity, cache volume, and images to scan.
    • Implemented trivy-scan target to execute both filesystem and image scans.
    • Created trivy-scan-fs target for scanning the repository filesystem for vulnerabilities, misconfigurations, and secrets.
    • Created trivy-scan-image target for scanning built Docker images.
  • tools/local-da/Dockerfile
    • Created a new ev-node user and group with UID/GID 1000.
    • Updated the working directory from /root to /home/ev-node.
    • Changed ownership of /home/ev-node to ev-node.
    • Set the container to run as the ev-node user.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link
Contributor

claude bot commented Feb 18, 2026

Claude finished @alpe's task in 2m 10s —— View job


Code Review

  • Gather context and understand the PR
  • Review Dockerfiles (evm, testapp, grpc, local-da)
  • Review security scanning configuration (.just/security.just)
  • Review docker-compose files
  • Review documentation and changelog
  • Review CI workflow changes
  • Compile and post final review

Good security improvement overall — non-root containers and integrated Trivy scanning are both solid additions. Several issues need attention before merging.

Critical Issues

1. Bare binary names in ENTRYPOINT won't work in scratch images

apps/testapp/Dockerfile:27 and apps/grpc/Dockerfile:33 use bare binary names:

# testapp/Dockerfile
ENTRYPOINT ["testapp"]

# grpc/Dockerfile
ENTRYPOINT ["evgrpc"]

Scratch images have no shell and no PATH — Docker exec-form ENTRYPOINT does work (the kernel resolves absolute paths), but only when the binary is in the exec search path. In a scratch image there is no dynamic linker either — so this depends on Docker's own resolution. The safe, explicit, and portable form is always a full path. local-da correctly uses ENTRYPOINT ["/usr/bin/local-da"] — testapp and grpc should follow the same pattern.

Fix testapp →
Fix grpc →

2. UID mismatch between Dockerfiles and documentation

All Dockerfiles now use USER 10001:10001 (numeric only, no named user), but CHANGELOG.md:46 and docs/guides/migrate-docker-nonroot.md:3 say uid/gid 1000 and reference a named ev-node user. Users following the migration guide will chown to the wrong UID, causing permission failures at startup.

# What docs say:
sudo chown -R 1000:1000 /path/to/data
docker exec <container-name> id
# Expected: uid=1000(ev-node) gid=1000(ev-node)

# What Dockerfiles actually do:
USER 10001:10001

The changelog entry in the PR's own base changelog (v1.0.0-rc.5) also says uid 1000. Both the docs and the changelog need to be corrected to 10001.

Fix docs and changelog →

Major Issues

3. Trivy image pinned to latest — non-reproducible scans (.just/security.just:3)

trivy_image := "aquasec/trivy:latest"

Trivy's vulnerability DB updates every ~6 hours. Using latest means the same image scan can produce different results on different days, making it impossible to distinguish "new vulnerability found" from "Trivy updated". Pin to a specific version, and make it overridable for easy bumping:

trivy_image := env("TRIVY_IMAGE", "aquasec/trivy:0.62.0")

The CI workflow already does this correctly — the GitHub Actions step pins aquasec/[email protected]. The local tooling should follow the same discipline.

4. Migration guide tells users to docker start after pulling (docs/guides/migrate-docker-nonroot.md:42-43)

docker pull <image>
docker start <container-name>   # BUG: starts old container, not new image

docker start restarts an existing container, which is bound to its original image ID at creation time. Pulling a new image doesn't change what an existing container runs — the container must be recreated. This should be:

docker pull <image>
docker rm <container-name>
docker run ... <image> ...

Or for compose users: docker compose up -d --force-recreate.

5. service_started instead of service_healthy for local-da (apps/grpc/docker-compose.yml:41)

evolve-grpc now depends on local-da with condition: service_started rather than service_healthy. This is a step backwards — evolve-grpc can start connecting to port 7980 before local-da is actually ready to accept connections. The previous service_healthy with a healthcheck was the correct approach. Either restore the healthcheck on local-da or ensure evolve-grpc has robust retry logic on startup.

Minor Issues

6. Missing ENV HOME in testapp and grpc Dockerfiles

apps/evm/Dockerfile correctly sets ENV HOME=/home/ev-node before USER 10001:10001. The testapp and grpc Dockerfiles are missing this — in a scratch image, $HOME may be unset or default to /, which can cause issues if any library or binary references $HOME at runtime.

7. Redundant -e TRIVY_SEVERITY in trivy_run (.just/security.just:8)

trivy_run := "docker run --rm -v " + trivy_cache_volume + ":/root/.cache/ -e TRIVY_SEVERITY=" + trivy_severity

Both scan targets already pass --severity {{trivy_severity}} explicitly as a CLI flag, which takes precedence over the env var. The -e TRIVY_SEVERITY=... portion is a no-op and can be removed.

8. Volume name inconsistency across compose files

apps/evm/docker-compose.yml defines volumes ev-node-evm-data and ev-reth-data, while execution/evm/docker/docker-compose.yml and execution/evm/docker/docker-compose-full-node.yml use reth. This is likely intentional (they're separate stacks) but could confuse operators who switch between them.

Positive Highlights

  • evm Dockerfile is well-structured: multi-stage build with module cache mounts, static binary (CGO_ENABLED=0 -ldflags "-s -w" -trimpath), scratch final image with busybox for shell support, explicit ENV HOME, correct full path in ENTRYPOINT via busybox wrapper.
  • local-da Dockerfile is clean and minimal — correct ENTRYPOINT with full path.
  • CI Trivy integration in .github/workflows/docker-build-push.yml is solid: pinned action version, ignore-unfixed: true, exit-code: "1", appropriate severity filter.
  • Container hardening in compose files (no-new-privileges, cap_drop: ALL, read_only: true, tmpfs: /tmp) is thorough and consistent.
  • --exit-code 1 in both trivy-scan-fs and trivy-scan-image ensures CI fails on findings.

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.20%. Comparing base (e4e35bf) to head (cbb0185).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3082   +/-   ##
=======================================
  Coverage   60.20%   60.20%           
=======================================
  Files         115      115           
  Lines       11902    11902           
=======================================
  Hits         7166     7166           
  Misses       3925     3925           
  Partials      811      811           
Flag Coverage Δ
combined 60.20% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Trivy security scanning capabilities and enhances container security by configuring evm, testapp, and local-da containers to run as a non-root ev-node user. The addition of scripts/security.mk provides a convenient way to perform filesystem and image scans. The changes to the Dockerfiles for evm, testapp, and local-da correctly implement the non-root user principle, which is a significant security improvement. There are minor opportunities for consistency and clarity in the Dockerfile user creation and the default image scanning configuration.


WORKDIR /root
RUN addgroup -g 1000 ev-node && \
adduser -u 1000 -G ev-node -s /bin/sh -D ev-node
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The adduser command uses the -D flag, which prevents the creation of a home directory. However, the subsequent WORKDIR /home/ev-node implies that /home/ev-node is intended to be the user's home directory. For consistency with apps/testapp/Dockerfile and clearer intent, it's better to allow adduser to create the home directory by removing the -D flag, or explicitly create it if -D is strictly necessary for other reasons. Removing -D is the most straightforward approach to align with the WORKDIR and chown commands.

    adduser -u 1000 -G ev-node -s /bin/sh ev-node

TRIVY_CACHE_VOLUME := trivy-cache

# Docker images to scan (space-separated, override or extend as needed)
SCAN_IMAGES ?= evstack:local-dev
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SCAN_IMAGES variable defaults to evstack:local-dev. While the comment indicates it can be overridden, having a single specific image as the default might lead to other relevant images being missed during scans if the user doesn't explicitly configure this variable. Consider making this variable empty by default or providing a more generic placeholder, encouraging users to define the images they intend to scan, or adding a clear example of how to extend it for multiple images.

tac0turtle and others added 2 commits February 18, 2026 15:55
Covers bind mounts, named volumes, Kubernetes init containers,
fsGroup, and docker-compose. Links from the changelog entry.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@alpe
Copy link
Contributor

alpe commented Feb 19, 2026

Good call to improve the Dockerfiles. The test fail due to RO volumes now.
init container: init evm-single: exit code 1: Error: error validating config: could not create directory "/root/.evm-single/config": mkdir /root/.evm-single: permission denied

# Conflicts:
#	CHANGELOG.md
#	Makefile
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Justfile-based Trivy security workflow and filesystem/image scan targets; converts multiple runtime images and compose services to run as non-root (adjusting volumes/mounts and workdirs); adds a migration guide and sidebar entry; records a BREAKING note in CHANGELOG.

Changes

Cohort / File(s) Summary
Security workflow
.just/security.just, justfile
Add Trivy scan Just recipes and variables (trivy_image, trivy_severity, trivy_cache_volume, scan_images, trivy_run) and top-level trivy-scan target importing .just/security.just.
Runtime Dockerfiles
apps/evm/Dockerfile, apps/testapp/Dockerfile, apps/grpc/Dockerfile, tools/local-da/Dockerfile
Switch build bases to Go 1.26 bookworm, adopt module cache mounts, produce static binaries, change final images to scratch, copy CA certs, set HOME/WORKDIR to /home/ev-node, and run container as a non-root user (USER set).
Compose / service hardening
apps/evm/docker-compose.yml, apps/grpc/docker-compose.yml, apps/evm/docker-compose.*, execution/evm/.../docker-compose*.yml
Rename/replace persistent volumes, update mount targets to non-root paths, and add container hardening (security_opt no-new-privileges, cap_drop: ALL, read_only: true, tmpfs /tmp).
Node data / volume mappings
apps/evm/docker-compose.yml
Rename volumes and update service mounts to new data directories (e.g., ev-node-evm-data, ev-reth-data) and adjust mount target paths.
Docs & changelog
docs/guides/migrate-docker-nonroot.md, docs/.vitepress/config.ts, CHANGELOG.md
Add migration guide for non-root runtime, add sidebar entry, and add a BREAKING note in CHANGELOG describing the runtime user change.
Misc / other build changes
apps/testapp/Dockerfile, apps/grpc/Dockerfile, tools/local-da/Dockerfile
Refactor multi-stage builds, use -trimpath and -ldflags "-s -w", output to /out/*, and copy built artifacts into scratch-based final images.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Just as Just runner
    participant Docker as Docker daemon
    participant Trivy as Trivy (Docker)
    participant FS as Repo FS
    participant Registry as Image registry

    Dev->>Just: run `just trivy-scan`
    Just->>Just: invoke `trivy-scan-fs` and `trivy-scan-image`
    Just->>Trivy: start FS scan (docker run using `trivy_image`)
    Trivy->>FS: scan codebase (vuln, misconfig, secret)
    Trivy-->>Just: return FS scan results
    Just->>Docker: list images from `scan_images`
    loop per image
      Just->>Trivy: run image scan (with `trivy_cache_volume`)
      Trivy->>Docker: analyze image layers
      Trivy->>Registry: fetch layers if needed
      Trivy-->>Just: return image scan results
    end
    Just-->>Dev: print progress and completion messages
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • auricom

Poem

🐰 I hop the docks with lantern bright,
Sniffing layers through the night.
Trivy peeks in every nest,
Non-root burrows get their rest.
A tidy warren—now all is right.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description partially addresses requirements but is minimal. It mentions the two main objectives (Trivy scanning and non-root users) and references DS-0002, but lacks detailed explanation of rationale, background, or specific context. Expand the description to include more context about DS-0002, the reasoning behind each change, and potential impacts on existing deployments or workflows.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main changes: adding Trivy security scanning and fixing non-root container users, which directly corresponds to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch marko/add-trivy-scan
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.just/security.just:
- Around line 11-34: The Trivy targets trivy-scan-fs and trivy-scan-image
currently do not fail CI when findings exist; update both command invocations
(the fs command inside target trivy-scan-fs and the image command inside target
trivy-scan-image) to include an exit code flag (e.g., add --exit-code 1) so
Trivy returns non-zero on findings; optionally expose a variable (e.g.,
trivy_exit_code with default 1) and use {{trivy_exit_code}} in both invocations
so the behavior is configurable while ensuring CI fails on detected issues.
- Line 3: The Trivy image is pinned to "latest" via the trivy_image variable
which allows non-reproducible scans; change trivy_image to a specific release
tag or digest (e.g., aquasec/trivy:0.48.0 or the image@sha256:...) to lock the
tool version, and update any scan invocations (where Trivy is run) to optionally
include --skip-db-update for PR/gate scans while reserving DB updates for
scheduled jobs; update the trivy_image variable and adjust the scan invocation
logic that references trivy_image to implement these changes.

In `@apps/evm/Dockerfile`:
- Around line 23-30: The Dockerfile switches to the non-root user (USER ev-node)
but never sets HOME, so entrypoint.sh (which defaults to $HOME/.evm) may still
resolve to /root; add an ENV HOME=/home/ev-node before the USER ev-node
instruction so $HOME points to /home/ev-node at runtime, and keep the existing
WORKDIR /home/ev-node and chown/chmod steps to ensure ownership/permissions for
entrypoint.sh and the home directory.

In `@CHANGELOG.md`:
- Line 27: Update the changelog entry that starts with "**BREAKING:** Docker
images for `evm`, `testapp`, and `local-da` now run as non-root user `ev-node`
(uid 1000)..." to append the PR reference (e.g., "(`#1234`)") consistent with
neighboring entries; edit the line to include the PR link or number so the entry
matches the format used elsewhere for traceability.

In `@docs/guides/migrate-docker-nonroot.md`:
- Around line 39-44: The step currently pulls the new image but incorrectly uses
docker start on the existing container (so the old image stays in use); replace
the docker start <container-name> instruction with commands that remove and
recreate the container so it runs the new image (e.g., docker rm
<container-name> followed by docker run ... <image> ...), or instruct users to
use docker-compose recreate / docker-compose up -d --force-recreate when using
compose; ensure the docs explicitly state that the container must be recreated
to pick up the new image.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 153f9ac0-dce3-4e35-8776-18168486bb43

📥 Commits

Reviewing files that changed from the base of the PR and between 2c75e9e and 62e68bd.

📒 Files selected for processing (8)
  • .just/security.just
  • CHANGELOG.md
  • apps/evm/Dockerfile
  • apps/testapp/Dockerfile
  • docs/.vitepress/config.ts
  • docs/guides/migrate-docker-nonroot.md
  • justfile
  • tools/local-da/Dockerfile

@@ -0,0 +1,35 @@
# Security scanning with Trivy (https://trivy.dev)

trivy_image := "aquasec/trivy:latest"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Trivy container image latest tag best practices CI/CD reproducibility

💡 Result:

Recommendation: don’t use :latest for Trivy in CI/CD if you care about reproducibility

latest (and other floating tags) can be retagged to different image contents over time, which breaks determinism. Docker explicitly recommends pinning by digest when you need fully repeatable builds/scans. [1]

Best practices for reproducible Trivy container-image scanning

  1. Pin the Trivy tool version (and ideally the image digest)

    • Prefer an explicit Trivy release version over latest.
    • If you use GitHub Actions, pin the action and the Trivy version it installs (the action supports version: latest or version: vX.Y.Z; don’t use latest for reproducibility). [2]
    • Avoid “canary” images/builds for production pipelines. [3]
  2. Control the vulnerability database lifecycle (this is a major source of “same image, different results”)

    • Trivy’s vulnerability DB is updated frequently (published every ~6 hours), so scan results can change even when the target image is unchanged. [4]
    • For reproducible runs, cache the DB between CI jobs and/or runs (Trivy explicitly calls out CI caching benefits). [4]
    • For “frozen” results (e.g., audits), run with --skip-db-update to prevent pulling a newer DB during the scan. [4]
  3. If you need strict repeatability across networks/regions, self-host or mirror the Trivy DB

    • Trivy distributes DBs as OCI artifacts (notably via GHCR), and its “air-gapped/offline” guidance is designed around controlling that dependency. [5]
    • In locked-down enterprise CI, mirror/self-host the DB and point Trivy at your controlled source (so the exact DB used is explicit and reviewable). [5]
  4. Separate “reproducible security gate” from “continuously updated security signal”

    • Common pattern:
      • PR gate: pinned Trivy version + cached/frozen DB (stable pass/fail).
      • Scheduled job (daily): same Trivy version (or intentionally updated), DB updates enabled to detect newly disclosed CVEs.

Sources: Docker pinning/digest guidance for determinism [1]; Trivy Action supports explicit Trivy version pinning [2]; Trivy warns about canary builds [3]; Trivy DB update frequency + skip-update + caching [4]; Trivy DB distribution via GHCR / offline control concepts [5].


🏁 Script executed:

# Examine the .just/security.just file to understand context
cat -n .just/security.just

Repository: evstack/ev-node

Length of output: 1566


🏁 Script executed:

# Check if there are other references to trivy_image or container images in the project
rg "trivy_image|docker|container|image" --type-list | head -20
fd -t f "*.just" -exec rg "trivy_image" {} +

Repository: evstack/ev-node

Length of output: 693


Pin Trivy version and consider database update strategy.

Line 3 uses :latest, which allows the Trivy vulnerability database to update automatically (~every 6 hours), causing identical image scans to produce different results. Pin an explicit Trivy version (and optionally the image digest) for reproducible security scans.

The project already caches the Trivy database via trivy_cache_volume, which helps. For further reproducibility, consider:

  • Pinning to a specific Trivy release (e.g., aquasec/trivy:0.48.0) or digest
  • Using --skip-db-update in PR/gate scans to freeze results; enable DB updates only in scheduled/continuous jobs
🔧 Suggested fix
-trivy_image := "aquasec/trivy:latest"
+trivy_image := env("TRIVY_IMAGE", "aquasec/trivy:0.48.0")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
trivy_image := "aquasec/trivy:latest"
trivy_image := env("TRIVY_IMAGE", "aquasec/trivy:0.48.0")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.just/security.just at line 3, The Trivy image is pinned to "latest" via the
trivy_image variable which allows non-reproducible scans; change trivy_image to
a specific release tag or digest (e.g., aquasec/trivy:0.48.0 or the
image@sha256:...) to lock the tool version, and update any scan invocations
(where Trivy is run) to optionally include --skip-db-update for PR/gate scans
while reserving DB updates for scheduled jobs; update the trivy_image variable
and adjust the scan invocation logic that references trivy_image to implement
these changes.

Comment on lines +39 to +44
### 3. Pull the new image and restart

```bash
docker pull <image>
docker start <container-name>
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

docker pull image docker start container does it use new image automatically

💡 Result:

No.

  • docker pull repo:tag only updates your local image (it may move the repo:tag label to a new image ID). Existing containers keep pointing to the image ID they were created from, and that never changes. (forums.docker.com)
  • docker start <container> starts an already-created container, so it cannot switch to a newer image. (stackoverflow.com)

To use the newly pulled image, you must recreate the container (e.g., docker rm <container> then docker run ... repo:tag), or force pulling when creating a new container with docker run --pull=always .... (docs.docker.com)

Citations:


Step 3 must recreate the container to use the newly pulled image.

The current step pulls a new image but then runs docker start on the existing container, which continues to use the old image. Containers retain references to the specific image ID they were created with; pulling only updates the local tag label. To apply the new image, the container must be recreated with docker rm <container-name> followed by docker run (or equivalent docker-compose recreate), otherwise the migration appears successful but the old image remains active.

🔧 Suggested fix
 ### 3. Pull the new image and restart
 
 ```bash
 docker pull <image>
-docker start <container-name>
+# Recreate container to use the newly pulled image
+docker rm <container-name>
+docker run ... <image> ...
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/guides/migrate-docker-nonroot.md around lines 39 - 44, The step
currently pulls the new image but incorrectly uses docker start on the existing
container (so the old image stays in use); replace the docker start
instruction with commands that remove and recreate the
container so it runs the new image (e.g., docker rm followed by
docker run ... ...), or instruct users to use docker-compose recreate /
docker-compose up -d --force-recreate when using compose; ensure the docs
explicitly state that the container must be recreated to pick up the new image.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

@auricom auricom self-assigned this Mar 5, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/evm/docker-compose.yml (1)

81-83: Align volume names across docker-compose files for consistency.

The volume naming convention changed in apps/evm/docker-compose.yml from reth to ev-reth-data and ev-node-evm-data, but execution/evm/docker/docker-compose.yml and execution/evm/docker/docker-compose-full-node.yml still use the older reth naming. This inconsistency may cause confusion when switching between configurations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/evm/docker-compose.yml` around lines 81 - 83, The volumes in this
compose file use non-canonical names (ev-node-evm-data, ev-reth-data); change
them to the canonical volume names used in the other EVM compose files (replace
ev-reth-data and ev-node-evm-data with the exact volume identifiers used in
execution/evm/docker/docker-compose.yml and
execution/evm/docker/docker-compose-full-node.yml), and update any service
volume mounts in this file that reference ev-reth-data or ev-node-evm-data to
use those canonical names so all compose files are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/evm/Dockerfile`:
- Around line 22-23: The Dockerfile ADDUSER invocation is missing the Alpine
non-interactive flag; update the adduser command (the line calling adduser -u
1000 -G ev-node -s /bin/sh ev-node) to include the -D flag so the user is
created without an interactive password prompt (matching other Dockerfiles like
apps/grpc/Dockerfile and tools/local-da/Dockerfile) to prevent the Docker build
from hanging.

---

Nitpick comments:
In `@apps/evm/docker-compose.yml`:
- Around line 81-83: The volumes in this compose file use non-canonical names
(ev-node-evm-data, ev-reth-data); change them to the canonical volume names used
in the other EVM compose files (replace ev-reth-data and ev-node-evm-data with
the exact volume identifiers used in execution/evm/docker/docker-compose.yml and
execution/evm/docker/docker-compose-full-node.yml), and update any service
volume mounts in this file that reference ev-reth-data or ev-node-evm-data to
use those canonical names so all compose files are consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e52ddfe0-630d-41da-bfb7-e2f1fb4e0c0a

📥 Commits

Reviewing files that changed from the base of the PR and between 62e68bd and 86a4655.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • apps/evm/Dockerfile
  • apps/evm/docker-compose.yml

@auricom auricom force-pushed the marko/add-trivy-scan branch from 86a4655 to 8cb45b0 Compare March 5, 2026 16:44
@auricom
Copy link
Contributor

auricom commented Mar 6, 2026

it seems tastora is hardcoding /var/evstack as home folder for test @chatton can we make it optional or rely on default $HOME value now that we have non root image

alpe added 2 commits March 16, 2026 10:49
* main:
  fix(syncer): include in-flight and pending-cache work in PendingCount (#3162)
  chore: re-add replaces for development (#3161)
  build(deps): Bump quinn-proto from 0.11.13 to 0.11.14 in the cargo group across 1 directory (#3155)
  build(deps): bump execution/evm (#3160)
  build(deps): bump ev-node (#3159)
  chore: remove replaces from ev-node (#3158)
  chore: fix some function names in comment (#3156)
  fix: persist snapshot once for avoiding badger vlog (#3153)
  build(deps): Bump docker/setup-buildx-action from 3 to 4 (#3148)
  build(deps): Bump the all-go group across 5 directories with 8 updates (#3147)
  build(deps): Bump docker/login-action from 3 to 4 (#3149)
  build(deps): Bump docker/build-push-action from 6 to 7 (#3151)
  build(deps): Bump actions/setup-go from 6.2.0 to 6.3.0 (#3150)
  chore: prep evm rc.5 (#3145)
  build(deps): bump ev-node (#3144)
  feat(block): Event-Driven DA Follower with WebSocket Subscriptions (#3131)
  build(deps): Bump dompurify from 3.2.6 to 3.3.2 in /docs in the npm_and_yarn group across 1 directory (#3140)
  feat(benchmarking): adding gas burner test (#3115)
  chore: minor deduplication (#3139)
  refactor(store,cache)!: optimize cache restore as O(1) (#3134)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
.just/security.just (2)

8-8: Remove redundant TRIVY_SEVERITY environment variable.

The trivy_run command sets TRIVY_SEVERITY via -e, but both scan targets also pass --severity {{trivy_severity}} explicitly. Since CLI flags take precedence over environment variables in Trivy, the env var is ineffective and adds noise.

♻️ Suggested simplification
-trivy_run := "docker run --rm -v " + trivy_cache_volume + ":/root/.cache/ -e TRIVY_SEVERITY=" + trivy_severity
+trivy_run := "docker run --rm -v " + trivy_cache_volume + ":/root/.cache/"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.just/security.just at line 8, The trivy_run string sets the TRIVY_SEVERITY
environment variable which is redundant because the scan commands already pass
--severity {{trivy_severity}}; remove the `-e TRIVY_SEVERITY=` portion from the
trivy_run construction to avoid a no-op env var and reduce noise (update the
trivy_run variable that currently concatenates trivy_cache_volume and
TRIVY_SEVERITY, leaving the rest of the command and the existing --severity
flags in the scan targets unchanged).

28-36: Consider scanning all images before failing.

With Just's default shell settings (set -e), the loop exits on the first image that has vulnerabilities, leaving remaining images unscanned. If comprehensive reporting is desired, consider capturing exit codes and failing at the end.

♻️ Optional: Scan all images, then fail
 trivy-scan-image:
     `@echo` "--> Scanning Docker images with Trivy"
-    `@for` img in {{scan_images}}; do \
+    `@exit_code`=0; \
+    for img in {{scan_images}}; do \
         echo "--> Scanning image: $img"; \
         {{trivy_run}} \
             -v /var/run/docker.sock:/var/run/docker.sock \
             {{trivy_image}} \
             image --severity {{trivy_severity}} \
             --exit-code 1 \
-            $img; \
-    done
+            $img || exit_code=1; \
+    done; \
+    exit $exit_code
     `@echo` "--> Image scan complete"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.just/security.just around lines 28 - 36, The loop over scan_images
currently aborts on the first failing trivy run due to Just's set -e; update the
loop that iterates over {{scan_images}} to run each trivy invocation but capture
its exit code (e.g., store $? in a variable or append to a list) and continue
scanning remaining images, then after the for loop check any recorded non-zero
exit codes and call exit 1 if any failures occurred; refer to the existing loop
and the variables {{trivy_run}}, {{trivy_image}}, and {{trivy_severity}} when
making this change.
apps/testapp/Dockerfile (1)

16-24: Consider adding ENV HOME=/home/ev-node for consistency.

Other Dockerfiles in this PR (e.g., apps/evm/Dockerfile line 22) explicitly set ENV HOME=/home/ev-node. Without this, $HOME may be undefined or default to / in a scratch image, which could cause issues if testapp or any library it uses references $HOME.

Suggested fix
 FROM scratch
 
+ENV HOME=/home/ev-node
+
 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
 COPY --from=builder --chown=10001:10001 /home/ev-node /home/ev-node
 COPY --from=builder /out/testapp /usr/bin/testapp
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/testapp/Dockerfile` around lines 16 - 24, The Dockerfile for testapp is
missing an explicit HOME environment, which can be undefined in a scratch base
and break code that reads $HOME; add an ENV HOME=/home/ev-node declaration (for
example directly after WORKDIR /home/ev-node or before USER 10001:10001) so the
runtime process and any libraries see the correct home directory for the
unprivileged user.
apps/grpc/Dockerfile (1)

19-26: Consider adding ENV HOME=/home/ev-node for consistency.

The apps/evm/Dockerfile sets ENV HOME=/home/ev-node, but this file doesn't. If evgrpc or any library references $HOME, it may behave unexpectedly.

Suggested fix
 FROM scratch
 
+ENV HOME=/home/ev-node
+
 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/grpc/Dockerfile` around lines 19 - 26, Add an explicit HOME environment
variable to the Docker image so runtimes or libraries that reference $HOME
behave consistently: set ENV HOME=/home/ev-node in the Dockerfile (near WORKDIR
/home/ev-node or before USER 10001:10001) so the evgrpc binary and any libraries
see the same home directory as in apps/evm/Dockerfile and when running as USER
10001:10001.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/evm/Dockerfile`:
- Around line 24-28: The Dockerfile copies /out/home-ev-node from the build
stage ("build-env") but that path is never created, causing the build to fail;
fix by ensuring the build stage creates and populates /out/home-ev-node (or at
minimum creates the directory and sets ownership) before the final-stage COPY
--from=build-env /out/home-ev-node /home/ev-node is executed — update the
build-env stage to mkdir -p /out/home-ev-node and place the intended files (and
chown to 10001:10001) or remove/adjust the COPY referencing /out/home-ev-node if
that artifact is not needed.

In `@apps/grpc/docker-compose.yml`:
- Around line 39-41: The change replaced the depends_on condition for local-da
from service_healthy to service_started, which can cause evolve-grpc to start
before local-da is accepting connections on port 7980; restore a proper
readiness check by adding a lightweight Docker healthcheck to the local-da
service that probes the DA port (e.g., curl/tcp check on 7980) and then revert
the depends_on entry for evolve-grpc back to condition: service_healthy so
Docker waits for readiness, or alternatively implement retry/backoff connection
logic in evolve-grpc's startup (where it connects to the DA service) to retry
until the DA responds.

In `@apps/grpc/Dockerfile`:
- Around line 32-33: The Dockerfile uses ENTRYPOINT ["evgrpc"] which relies on
PATH resolution that doesn't exist in scratch images; update the ENTRYPOINT to
the binary's absolute path (e.g., ENTRYPOINT ["/usr/local/bin/evgrpc"]) so the
container can locate and execute evgrpc reliably, and keep CMD ["start"] as-is;
locate the ENTRYPOINT line in the Dockerfile and replace the bare "evgrpc" with
the full filesystem path to the built binary.

In `@CHANGELOG.md`:
- Line 46: Update the CHANGELOG.md entry to correct the UID and wording: change
the uid from 1000 to 10001, remove the reference to a named user "ev-node"
(since the Dockerfiles only set USER 10001:10001) and explicitly state that
containers run as numeric UID 10001 (GID 10001) so users will chown volumes to
10001:10001; ensure this matches the Dockerfiles that use the USER 10001:10001
directive.

---

Nitpick comments:
In @.just/security.just:
- Line 8: The trivy_run string sets the TRIVY_SEVERITY environment variable
which is redundant because the scan commands already pass --severity
{{trivy_severity}}; remove the `-e TRIVY_SEVERITY=` portion from the trivy_run
construction to avoid a no-op env var and reduce noise (update the trivy_run
variable that currently concatenates trivy_cache_volume and TRIVY_SEVERITY,
leaving the rest of the command and the existing --severity flags in the scan
targets unchanged).
- Around line 28-36: The loop over scan_images currently aborts on the first
failing trivy run due to Just's set -e; update the loop that iterates over
{{scan_images}} to run each trivy invocation but capture its exit code (e.g.,
store $? in a variable or append to a list) and continue scanning remaining
images, then after the for loop check any recorded non-zero exit codes and call
exit 1 if any failures occurred; refer to the existing loop and the variables
{{trivy_run}}, {{trivy_image}}, and {{trivy_severity}} when making this change.

In `@apps/grpc/Dockerfile`:
- Around line 19-26: Add an explicit HOME environment variable to the Docker
image so runtimes or libraries that reference $HOME behave consistently: set ENV
HOME=/home/ev-node in the Dockerfile (near WORKDIR /home/ev-node or before USER
10001:10001) so the evgrpc binary and any libraries see the same home directory
as in apps/evm/Dockerfile and when running as USER 10001:10001.

In `@apps/testapp/Dockerfile`:
- Around line 16-24: The Dockerfile for testapp is missing an explicit HOME
environment, which can be undefined in a scratch base and break code that reads
$HOME; add an ENV HOME=/home/ev-node declaration (for example directly after
WORKDIR /home/ev-node or before USER 10001:10001) so the runtime process and any
libraries see the correct home directory for the unprivileged user.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01b53a0b-c034-452f-b4e4-bb715c0f8d39

📥 Commits

Reviewing files that changed from the base of the PR and between 517f2f4 and e48a5ef.

📒 Files selected for processing (10)
  • .just/security.just
  • CHANGELOG.md
  • apps/evm/Dockerfile
  • apps/evm/docker-compose.yml
  • apps/grpc/Dockerfile
  • apps/grpc/docker-compose.yml
  • apps/testapp/Dockerfile
  • execution/evm/docker/docker-compose-full-node.yml
  • execution/evm/docker/docker-compose.yml
  • tools/local-da/Dockerfile

Comment on lines 39 to +41
depends_on:
local-da:
condition: service_healthy
condition: service_started
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Removing healthcheck may cause startup race conditions.

Changing from service_healthy to service_started means evolve-grpc will start as soon as the local-da container starts, not when it's actually ready to accept connections on port 7980. This could cause transient connection failures on startup.

Consider adding a lightweight healthcheck back to local-da, or ensure evolve-grpc has retry logic for connecting to the DA service.

💡 Suggested healthcheck for local-da
     tmpfs:
       - /tmp
+    healthcheck:
+      test: ["CMD-SHELL", "nc -z localhost 7980 || exit 1"]
+      interval: 5s
+      timeout: 3s
+      retries: 5

Then revert the depends_on condition:

     depends_on:
       local-da:
-        condition: service_started
+        condition: service_healthy
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/grpc/docker-compose.yml` around lines 39 - 41, The change replaced the
depends_on condition for local-da from service_healthy to service_started, which
can cause evolve-grpc to start before local-da is accepting connections on port
7980; restore a proper readiness check by adding a lightweight Docker
healthcheck to the local-da service that probes the DA port (e.g., curl/tcp
check on 7980) and then revert the depends_on entry for evolve-grpc back to
condition: service_healthy so Docker waits for readiness, or alternatively
implement retry/backoff connection logic in evolve-grpc's startup (where it
connects to the DA service) to retry until the DA responds.

Comment on lines 32 to 33
ENTRYPOINT ["evgrpc"]

# Default command
CMD ["start"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use full path in ENTRYPOINT for scratch images.

In a scratch image, there's no shell and no default PATH. The entrypoint ["evgrpc"] relies on Docker's search behavior, which may not find the binary at /usr/local/bin/evgrpc. Use the full path for reliability.

Suggested fix
-ENTRYPOINT ["evgrpc"]
+ENTRYPOINT ["/usr/local/bin/evgrpc"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ENTRYPOINT ["evgrpc"]
# Default command
CMD ["start"]
ENTRYPOINT ["/usr/local/bin/evgrpc"]
CMD ["start"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/grpc/Dockerfile` around lines 32 - 33, The Dockerfile uses ENTRYPOINT
["evgrpc"] which relies on PATH resolution that doesn't exist in scratch images;
update the ENTRYPOINT to the binary's absolute path (e.g., ENTRYPOINT
["/usr/local/bin/evgrpc"]) so the container can locate and execute evgrpc
reliably, and keep CMD ["start"] as-is; locate the ENTRYPOINT line in the
Dockerfile and replace the bare "evgrpc" with the full filesystem path to the
built binary.

@alpe alpe added this to the v1.1.0 milestone Mar 16, 2026
tags: ghcr.io/${{ github.repository_owner }}/${{ matrix.app.name }}:${{ inputs.image-tag }}

- name: Scan pushed image with Trivy
uses: aquasecurity/[email protected]

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Build Docker Images' step
Uses Step
uses 'aquasecurity/trivy-action' with ref '0.33.1', not a pinned commit hash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants