Skip to content
Open
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
9 changes: 7 additions & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
FROM mcr.microsoft.com/devcontainers/base:ubuntu26.04

# hadolint ignore=DL3008
RUN set -eux; \
Expand All @@ -13,4 +13,9 @@ RUN set -eux; \
apt-get -y purge linux-libc-dev; \
apt-get -y autoremove --purge; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*
rm -rf /var/lib/apt/lists/*; \
# Remove Pebble, an unused ACME/TLS test server baked into the base
# image. It is not owned by apt, has no systemd unit, and is never run,
# yet its outdated Go runtime (golang.org/x/net, x/sys, stdlib) is the
# sole source of all fixable HIGH CVEs in the image.
rm -rf /usr/bin/pebble /var/lib/pebble
29 changes: 29 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {
"version": "1.0.5",
"resolved": "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a",
"integrity": "sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a"
},
"ghcr.io/devcontainers/features/common-utils:2": {
"version": "2.5.9",
"resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a",
"integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a"
},
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
"integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "1.7.1",
"resolved": "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6",
"integrity": "sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6"
},
"ghcr.io/devcontainers/features/sshd:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers/features/sshd@sha256:f5251b8e4325f68f7280973c6cd65daff414449c66f240621502d4e8e74eb7ee",
"integrity": "sha256:f5251b8e4325f68f7280973c6cd65daff414449c66f240621502d4e8e74eb7ee"
}
}
}
7 changes: 6 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@
},
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "latest"
}
},
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
},
"mounts": [
"source=claude-code-config-${devcontainerId},target=/home/vscode/.claude,type=volume"
],
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code",
"github.copilot",
"github.copilot-chat",
"ms-python.python",
Expand Down
25 changes: 19 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Keep devcontainer dependencies up to date
version: 2
updates:
# Monitor GitHub Actions
# Monitor GitHub Actions — daily, security-critical supply-chain component
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
interval: "daily"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "github-actions"
Expand All @@ -19,12 +19,12 @@ updates:
- "minor"
- "patch"

# Monitor Docker base images
# Monitor Docker base images — daily, CVEs ship in base images
- package-ecosystem: "docker"
directory: "/.devcontainer"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
interval: "daily"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "docker"
Expand All @@ -36,3 +36,16 @@ updates:
update-types:
- "minor"
- "patch"

# Monitor devcontainer features — daily, includes claude-code, node, github-cli, common-utils, sshd
- package-ecosystem: "devcontainers"
directory: "/.devcontainer"
schedule:
interval: "daily"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "devcontainers"
commit-message:
prefix: "feat"
include: "scope"
123 changes: 118 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * *'
workflow_dispatch:

permissions:
contents: read
actions: read
security-events: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -20,6 +21,9 @@ jobs:
runs-on: ubuntu-latest
name: Build and Validate
timeout-minutes: 15
permissions:
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Expand Down Expand Up @@ -58,20 +62,34 @@ jobs:
--report-path gitleaks.sarif

- name: Upload gitleaks SARIF
if: always()
if: >
always() &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
sarif_file: gitleaks.sarif
category: security-analysis/gitleaks

- name: Build Docker image
run: docker build --pull -t dev-template:latest .devcontainer/
run: |
docker build --pull -t "dev-template:${{ github.sha }}" .devcontainer/
docker save "dev-template:${{ github.sha }}" -o /tmp/dev-template.tar

- name: Upload image artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: docker-image-${{ github.sha }}
path: /tmp/dev-template.tar
retention-days: 1

test:
runs-on: ubuntu-latest
name: Test Devcontainer
needs: build
timeout-minutes: 20
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Expand All @@ -84,7 +102,7 @@ jobs:
push: never
runCmd: |
set -eu
for cmd in python3 node npm gh opencode curl jq; do
for cmd in python3 node npm gh opencode curl jq claude; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "::error::$cmd is missing"
exit 1
Expand All @@ -97,3 +115,98 @@ jobs:
opencode --version
curl --version | head -1
jq --version
claude --version

scan:
runs-on: ubuntu-latest
name: Security and SBOM Analysis
needs: build
timeout-minutes: 20
permissions:
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Download image artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: docker-image-${{ github.sha }}
path: /tmp

- name: Load Docker image
run: docker load -i /tmp/dev-template.tar

- name: Run Trivy vulnerability scanner (image)
id: trivy-image
continue-on-error: true
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: 'dev-template:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL'
ignore-unfixed: true
limit-severities-for-sarif: true
exit-code: '1'
cache: false

- name: Upload Trivy image scan results
if: >
always() &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
sarif_file: 'trivy-results.sarif'
category: 'security-analysis/trivy-image'

- name: Generate SBOM
if: always()
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
image: 'dev-template:${{ github.sha }}'
format: 'spdx-json'
output-file: 'sbom.spdx.json'

- name: Upload SBOM as artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: sbom-${{ github.sha }}
path: sbom.spdx.json
retention-days: 30

- name: Run Trivy vulnerability scanner (filesystem)
id: trivy-fs
continue-on-error: true
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL'
ignore-unfixed: true
limit-severities-for-sarif: true
exit-code: '1'
cache: false

- name: Upload Trivy filesystem scan results
if: >
always() &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
sarif_file: 'trivy-fs-results.sarif'
category: 'security-analysis/trivy-filesystem'

- name: Fail job if any Trivy scan found CRITICAL vulnerabilities
if: steps.trivy-image.outcome == 'failure' || steps.trivy-fs.outcome == 'failure'
run: |
echo "::error::Trivy found CRITICAL vulnerabilities. Review the Security tab for details."
exit 1
88 changes: 0 additions & 88 deletions .github/workflows/security.yml

This file was deleted.

Loading
Loading