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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 49 additions & 15 deletions .github/workflows/docker_security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ name: Docker Build and Security Scan
permissions:
contents: read
security-events: write
pull-requests: write
actions: read

on:
Expand Down Expand Up @@ -37,26 +36,61 @@ jobs:
file: runscripts/container/Dockerfile
load: true
tags: vecoli:latest
build-args: INSTALL_EXTRAS=1
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Login to Docker Hub
uses: docker/login-action@v3
- name: Scan image with Trivy (SARIF)
if: github.event_name != 'pull_request'
id: trivy-sarif
uses: aquasecurity/trivy-action@0.34.2
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Analyze for critical and high CVEs
id: docker-scout-cves
uses: docker/scout-action@v1
with:
command: cves,recommendations
image: vecoli:latest
sarif-file: sarif.output.json
summary: true
image-ref: vecoli:latest
format: sarif
output: trivy-results.sarif
vuln-type: os,library
ignore-unfixed: true

- name: Upload SARIF result
id: upload-sarif
if: github.event_name != 'pull_request'
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: sarif.output.json
sarif_file: trivy-results.sarif

- name: Scan image with Trivy (JUnit)
id: trivy-junit
# Can use $HOME instead of hardcoding once bug is fixed
# https://github.com/aquasecurity/trivy-action/issues/509
uses: aquasecurity/trivy-action@0.34.2
with:
image-ref: vecoli:latest
format: template
template: "@/home/runner/.local/bin/trivy-bin/contrib/junit.tpl"
output: trivy-junit.xml
vuln-type: os,library
ignore-unfixed: true

- name: Publish Trivy test report (JUnit)
uses: ctrf-io/github-test-reporter@v1
with:
report-path: trivy-junit.xml
summary-report: false
failed-report: true
pull-request: false
github-report: false
integrations-config: |
{
"junit-to-ctrf": {
"enabled": true,
"action": "convert",
"options": {
"output": "./ctrf-reports/ctrf-report.json",
"toolname": "junit-to-ctrf",
"useSuiteName": false,
"env": {
"appName": "vEcoli"
}
}
}
}
76 changes: 76 additions & 0 deletions .github/workflows/trivy_pr_comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Trivy PR Comment

permissions:
actions: read
pull-requests: write

on:
workflow_run:
workflows: ["Docker Build and Security Scan"]
types: [completed]

jobs:
comment:
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Determine PR number securely
id: get_pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
HEAD_SHA="${{ github.event.workflow_run.head_sha }}"

PR_NUM=$(gh pr list \
--state open \
--json number,headRefOid \
--jq ".[] | select(.headRefOid==\"${HEAD_SHA}\") | .number")

if [ -z "$PR_NUM" ]; then
echo "No open PR found for head SHA ${HEAD_SHA}"
exit 1
fi

echo "PR_NUMBER=$PR_NUM" >> $GITHUB_ENV

- name: Comment PR with Trivy summary link
uses: actions/github-script@v8
with:
script: |
const issue_number = Number(process.env.PR_NUMBER);
if (!issue_number) {
core.info('No PR number available.');
return;
}
const header = '## Trivy scan results';
const runUrl = context.payload.workflow_run.html_url;
const comment = `${header}\n\n:warning: **Please review the Trivy vulnerability report before merging.**\n\nThe full report is available in the workflow summary:\n${runUrl}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number,
per_page: 100,
});
const existing = comments.find((c) => {
if (!c.body) return false;
const isBot = c.user && c.user.type === 'Bot';
return isBot && c.body.startsWith(header);
});
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: comment,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number,
body: comment,
});
}
env:
PR_NUMBER: ${{ env.PR_NUMBER }}
15 changes: 13 additions & 2 deletions runscripts/container/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@ ENV UV_CACHE_DIR=/root/.cache/uv
# Silence warning about cache and sync targets being on different filesystems
ENV UV_LINK_MODE=copy

# When set to a non-empty value, install all optional dependency groups.
ARG INSTALL_EXTRAS=""

# Install the dependencies only to leverage Docker layer caching
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project
if [ -n "$INSTALL_EXTRAS" ]; then \
uv sync --frozen --no-install-project --all-extras; \
else \
uv sync --frozen --no-install-project; \
fi

# Activate the virtual environment
ENV PATH="/vEcoli/.venv/bin:$PATH"
Expand All @@ -56,7 +63,11 @@ ADD . /vEcoli

# Install the project
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen
if [ -n "$INSTALL_EXTRAS" ]; then \
uv sync --frozen --all-extras; \
else \
uv sync --frozen; \
fi

# Record Docker Image metadata in ENV variables, viewable by `docker inspect`
# and accessible to programs in the container.
Expand Down
Loading