Skip to content

Commit 76cfd2a

Browse files
committed
build common node
1 parent a68181c commit 76cfd2a

21 files changed

Lines changed: 481 additions & 55 deletions

File tree

.github/workflows/build_all_images.yml

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@ jobs:
1717
discover_folders:
1818
runs-on: ubuntu-latest
1919
outputs:
20+
base_node_folders: ${{ steps.find-folders.outputs.base_node }}
2021
language_folders: ${{ steps.find-folders.outputs.languages }}
2122
project_folders: ${{ steps.find-folders.outputs.projects }}
2223
steps:
2324
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
2425

2526
- id: find-folders
2627
run: |
28+
base_node_folders=$(find src/base_node -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
2729
language_folders=$(find src/languages -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
2830
project_folders=$(find src/projects -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
29-
echo "languages=$language_folders" >> "$GITHUB_OUTPUT"
30-
echo "projects=$project_folders" >> "$GITHUB_OUTPUT"
31+
{
32+
echo "base_node=$base_node_folders"
33+
echo "languages=$language_folders"
34+
echo "projects=$project_folders"
35+
} >> "$GITHUB_OUTPUT"
3136
package_base_docker_image:
3237
uses: ./.github/workflows/build_multi_arch_image.yml
3338
with:
@@ -36,9 +41,25 @@ jobs:
3641
container_name: base
3742
base_folder: "."
3843
NO_CACHE: ${{ inputs.NO_CACHE }}
44+
package_base_node_images:
45+
needs:
46+
- package_base_docker_image
47+
- discover_folders
48+
strategy:
49+
fail-fast: false
50+
matrix:
51+
container_name: ${{ fromJson(needs.discover_folders.outputs.base_node_folders) }}
52+
uses: ./.github/workflows/build_multi_arch_image.yml
53+
with:
54+
tag_latest: ${{ inputs.tag_latest }}
55+
docker_tag: ${{ inputs.docker_tag }}
56+
container_name: ${{ matrix.container_name }}
57+
base_folder: "base_node"
58+
NO_CACHE: ${{ inputs.NO_CACHE }}
3959
package_language_docker_images:
4060
needs:
4161
- package_base_docker_image
62+
- package_base_node_images
4263
- discover_folders
4364
strategy:
4465
fail-fast: false
@@ -54,7 +75,6 @@ jobs:
5475
package_project_docker_images:
5576
needs:
5677
- package_language_docker_images
57-
5878
- discover_folders
5979
strategy:
6080
fail-fast: false

.github/workflows/build_multi_arch_image.yml

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,6 @@ jobs:
7272
run: |
7373
echo "Building image..."
7474
make build-image
75-
76-
echo "Creating combined trivy ignore file"
77-
# create combined trivy ignore file for use in trivy scan, combining common and specific ignore files if they exist
78-
combined="src/${BASE_FOLDER}/${CONTAINER_NAME}/.trivyignore_combined.yaml"
79-
common="src/common/.trivyignore.yaml"
80-
specific="src/${BASE_FOLDER}/${CONTAINER_NAME}/.trivyignore.yaml"
81-
echo "vulnerabilities:" > "$combined"
82-
if [ -f "$common" ]; then sed -n '2,$p' "$common" >> "$combined"; fi
83-
if [ -f "$specific" ]; then sed -n '2,$p' "$specific" >> "$combined"; fi
84-
echo "Combined trivy ignore file created at $combined"
85-
8675
env:
8776
ARCHITECTURE: '${{ matrix.arch }}'
8877
CONTAINER_NAME: '${{ inputs.container_name }}'
@@ -92,41 +81,33 @@ jobs:
9281
BASE_FOLDER: "${{ inputs.base_folder }}"
9382
NO_CACHE: '${{ inputs.NO_CACHE }}'
9483
- name: Check docker vulnerabilities - json output
95-
uses: aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284
96-
with:
97-
scan-type: "image"
98-
image-ref: "ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}:${{ inputs.docker_tag }}-${{ matrix.arch }}"
99-
severity: "CRITICAL,HIGH"
100-
scanners: "vuln"
101-
vuln-type: "os,library"
102-
format: "json"
103-
output: "scan_results_docker.json"
104-
exit-code: "0"
105-
trivy-config: src/${{ inputs.base_folder }}/${{ inputs.container_name }}/trivy.yaml
84+
run: |
85+
make scan-image-json
86+
env:
87+
CONTAINER_NAME: '${{ inputs.container_name }}'
88+
BASE_FOLDER: "${{ inputs.base_folder }}"
89+
IMAGE_TAG: "${{ inputs.docker_tag }}-${{ matrix.arch }}"
90+
EXIT_CODE: 0
10691
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
10792
name: Upload scan results
10893
with:
10994
name: "scan_results_docker_${{ inputs.container_name }}_${{ matrix.arch }}.json"
110-
path: scan_results_docker.json
111-
- name: Check docker vulnerabilities - table output
112-
uses: aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284
113-
with:
114-
scan-type: "image"
115-
image-ref: "ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.container_name }}:${{ inputs.docker_tag }}-${{ matrix.arch }}"
116-
severity: "CRITICAL,HIGH"
117-
scanners: "vuln"
118-
vuln-type: "os,library"
119-
format: "table"
120-
output: "scan_results_docker.txt"
121-
exit-code: "1"
122-
trivy-config: src/${{ inputs.base_folder }}/${{ inputs.container_name }}/trivy.yaml
95+
path: .out/scan_results_docker.json
96+
- name: Check docker vulnerabilities - json output
97+
run: |
98+
make scan-image-json
99+
env:
100+
CONTAINER_NAME: '${{ inputs.container_name }}'
101+
BASE_FOLDER: "${{ inputs.base_folder }}"
102+
IMAGE_TAG: "${{ inputs.docker_tag }}-${{ matrix.arch }}"
103+
EXIT_CODE: "1"
123104

124105
- name: Show docker vulnerability output
125106
if: always()
126107
run: |
127108
echo "Scan output for ghcr.io/nhsdigital/eps-devcontainers/base:${DOCKER_TAG}-${ARCHITECTURE}"
128-
if [ -f scan_results_docker.txt ]; then
129-
cat scan_results_docker.txt
109+
if [ -f .out/scan_results_docker.txt ]; then
110+
cat .out/scan_results_docker.txt
130111
fi
131112
env:
132113
ARCHITECTURE: '${{ matrix.arch }}'

Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,23 @@ scan-image: guard-CONTAINER_NAME guard-BASE_FOLDER
4444
@combined="src/$${BASE_FOLDER}/$${CONTAINER_NAME}/.trivyignore_combined.yaml"; \
4545
common="src/common/.trivyignore.yaml"; \
4646
specific="src/$${BASE_FOLDER}/$${CONTAINER_NAME}/.trivyignore.yaml"; \
47+
exit_code="$${EXIT_CODE:-1}"; \
4748
echo "vulnerabilities:" > "$$combined"; \
4849
if [ -f "$$common" ]; then sed -n '2,$$p' "$$common" >> "$$combined"; fi; \
4950
if [ -f "$$specific" ]; then sed -n '2,$$p' "$$specific" >> "$$combined"; fi
5051
trivy image \
5152
--severity HIGH,CRITICAL \
5253
--config src/${BASE_FOLDER}/${CONTAINER_NAME}/trivy.yaml \
5354
--scanners vuln \
54-
--exit-code 1 \
55-
--format table "${CONTAINER_PREFIX}$${CONTAINER_NAME}:$${IMAGE_TAG}"
55+
--exit-code "$$exit_code" \
56+
--format table \
57+
--output .out/scan_results_docker.txt "${CONTAINER_PREFIX}$${CONTAINER_NAME}:$${IMAGE_TAG}"
5658

5759
scan-image-json: guard-CONTAINER_NAME guard-BASE_FOLDER guard-IMAGE_TAG
5860
@combined="src/$${BASE_FOLDER}/$${CONTAINER_NAME}/.trivyignore_combined.yaml"; \
5961
common="src/common/.trivyignore.yaml"; \
6062
specific="src/$${BASE_FOLDER}/$${CONTAINER_NAME}/.trivyignore.yaml"; \
63+
exit_code="$${EXIT_CODE:-1}"; \
6164
echo "vulnerabilities:" > "$$combined"; \
6265
if [ -f "$$common" ]; then sed -n '2,$$p' "$$common" >> "$$combined"; fi; \
6366
if [ -f "$$specific" ]; then sed -n '2,$$p' "$$specific" >> "$$combined"; fi
@@ -66,7 +69,7 @@ scan-image-json: guard-CONTAINER_NAME guard-BASE_FOLDER guard-IMAGE_TAG
6669
--severity HIGH,CRITICAL \
6770
--config src/${BASE_FOLDER}/${CONTAINER_NAME}/trivy.yaml \
6871
--scanners vuln \
69-
--exit-code 1 \
72+
--exit-code "$$exit_code" \
7073
--format json \
7174
--output .out/scan_results_docker.json "${CONTAINER_PREFIX}$${CONTAINER_NAME}:$${IMAGE_TAG}"
7275

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,17 @@ It is important that
132132
- the first step copies .tool-versions from /home/vscode to $HOME/.tool-versions
133133

134134
# Project structure
135-
We have 4 types of dev container. These are defined under src
135+
We have 5 types of dev container. These are defined under src
136136

137137
`base` - this is the base image that all others are based on.
138-
`languages` - this installs specific versions of node and python.
138+
`base_node` - images that install node - most language projects rely on one of these
139+
`languages` - this installs specific versions of python - normally based off a node image
139140
`projects` - this is used for projects where more customization is needed than just a base language image.
140141
`githubactions` - this just takes an existing image and remaps vscode user to be 1001 so it can be used by github actions.
141142

142143
Each image to be built contains a .devcontainer folder that defines how the devcontainer should be built. At a minimum, this should contain a devcontainer.json file. See https://containers.dev/implementors/json_reference/ for options for this
143144

144-
Images under languages should point to a dockerfile under src/common that is based off the base image. This also runs `.devcontainer/scripts/root_install.sh` and `.devcontainer/scripts/vscode_install.sh` as vscode user as part of the build. These files should be in the language specific folder.
145+
Images under languages should point to a dockerfile under src/common or src/common_node_24 that is based off the base or node image. This also runs `.devcontainer/scripts/root_install.sh` and `.devcontainer/scripts/vscode_install.sh` as vscode user as part of the build. These files should be in the language specific folder.
145146

146147
We use trivy to scan for vulnerabilities in the built docker images. Known vulnerabilities in the base image are in `src/common/.trivyignore.yaml`. Vulnerabilities in specific images are in `.trivyignore.yaml` file in each images folder. These are combined before running a scan to exclude all known vulnerabilities
147148

@@ -180,6 +181,14 @@ CONTAINER_NAME=base \
180181
IMAGE_TAG=local-build \
181182
make build-image
182183
```
184+
Base node 24 image
185+
```
186+
CONTAINER_NAME=node_24 \
187+
BASE_VERSION_TAG=local-build \
188+
BASE_FOLDER=base_node \
189+
IMAGE_TAG=local-build \
190+
make build-image
191+
```
183192
Language images
184193
```
185194
CONTAINER_NAME=node_24_python_3_14 \
@@ -212,6 +221,13 @@ CONTAINER_NAME=base \
212221
IMAGE_TAG=local-build \
213222
make scan-image
214223
```
224+
Base node 24 image
225+
```
226+
CONTAINER_NAME=node_24 \
227+
BASE_FOLDER=languages \
228+
IMAGE_TAG=local-build \
229+
make scan-image
230+
```
215231
Language images
216232
```
217233
CONTAINER_NAME=node_24_python_3_12 \
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodejs 24.13.0
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
3+
{
4+
"name": "EPS Devcontainer node_24",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"build": {
7+
"dockerfile": "../../../common/Dockerfile",
8+
"args": {
9+
"CONTAINER_NAME": "eps_devcontainer_${localEnv:CONTAINER_NAME}",
10+
"MULTI_ARCH_TAG": "${localEnv:MULTI_ARCH_TAG}",
11+
"BASE_VERSION_TAG": "${localEnv:BASE_VERSION_TAG}",
12+
"IMAGE_TAG": "${localEnv:IMAGE_TAG}"
13+
},
14+
"context": "."
15+
},
16+
"features": {}
17+
}
18+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
export DEBIAN_FRONTEND=noninteractive
4+
5+
# clean up
6+
apt-get clean
7+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
5+
asdf install
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
vulnerabilities:
2+
- id: CVE-2026-25547
3+
statement: "brace-expansion: brace-expansion: Denial of Service via unbounded brace range expansion"
4+
purls:
5+
- "pkg:npm/%40isaacs/brace-expansion@5.0.0"
6+
expired_at: 2026-08-12
7+
- id: CVE-2025-64756
8+
statement: "glob: glob: Command Injection Vulnerability via Malicious Filenames"
9+
purls:
10+
- "pkg:npm/glob@10.4.5"
11+
- "pkg:npm/glob@11.0.3"
12+
expired_at: 2026-08-12
13+
- id: CVE-2026-23745
14+
statement: "node-tar: tar: node-tar: Arbitrary file overwrite and symlink poisoning via unsanitized linkpaths in archives"
15+
purls:
16+
- "pkg:npm/tar@7.5.1"
17+
expired_at: 2026-08-12
18+
- id: CVE-2026-23950
19+
statement: "node-tar: tar: node-tar: Arbitrary file overwrite via Unicode path collision race condition"
20+
purls:
21+
- "pkg:npm/tar@7.5.1"
22+
expired_at: 2026-08-12
23+
- id: CVE-2026-24842
24+
statement: "node-tar: tar: node-tar: Arbitrary file creation via path traversal bypass in hardlink security check"
25+
purls:
26+
- "pkg:npm/tar@7.5.1"
27+
expired_at: 2026-08-12

src/base_node/node_24/trivy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ignorefile: "src/languages/node_24_python_3_12/.trivyignore_combined.yaml"

0 commit comments

Comments
 (0)