Skip to content

Commit ef0c6aa

Browse files
authored
Merge pull request #88 from NHSDigital/APM-7202-Github-bestpractices
Apm 7202 GitHub bestpractices
2 parents dacb87f + dc1f79e commit ef0c6aa

7 files changed

Lines changed: 180 additions & 30 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: "Scan secrets"
2+
description: "Scan secrets"
3+
runs:
4+
using: "composite"
5+
steps:
6+
- name: "Scan secrets"
7+
shell: bash
8+
run: |
9+
# Please do not change this `check=whole-history` setting, as new patterns may be added or history may be rewritten.
10+
check=whole-history ./scripts/githooks/scan-secrets.sh

.github/dependabot.yml

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,50 @@ updates:
77
- package-ecosystem: "pip"
88
directory: "/"
99
schedule:
10-
interval: "daily"
10+
interval: "weekly"
1111
target-branch: "master"
12-
labels: ["dependencies", "python", "poetry"]
12+
labels: [ "dependencies", "python", "poetry" ]
1313
open-pull-requests-limit: 10
1414
ignore:
1515
- dependency-name: "*"
16-
update-types: ["version-update:semver-major"]
16+
update-types: [ "version-update:semver-major" ]
1717

1818
# ---------------------------
1919
# NodeJS (root)
2020
# ---------------------------
2121
- package-ecosystem: "npm"
2222
directory: "/"
2323
schedule:
24-
interval: "daily"
24+
interval: "weekly"
2525
target-branch: "master"
26-
labels: ["dependencies", "npm"]
26+
labels: [ "dependencies", "npm" ]
2727
open-pull-requests-limit: 10
2828
ignore:
2929
- dependency-name: "*"
30-
update-types: ["version-update:semver-major"]
30+
update-types: [ "version-update:semver-major" ]
3131

3232
# ---------------------------
3333
# NodeJS (sandbox/)
3434
# ---------------------------
3535
- package-ecosystem: "npm"
3636
directory: "/sandbox"
3737
schedule:
38-
interval: "daily"
38+
interval: "weekly"
3939
target-branch: "master"
40-
labels: ["dependencies", "npm", "sandbox"]
40+
labels: [ "dependencies", "npm", "sandbox" ]
4141
open-pull-requests-limit: 10
4242
ignore:
4343
- dependency-name: "*"
44-
update-types: ["version-update:semver-major"]
44+
update-types: [ "version-update:semver-major" ]
4545

4646
# ---------------------------
4747
# GitHub Actions
4848
# ---------------------------
4949
- package-ecosystem: "github-actions"
5050
directory: "/"
5151
schedule:
52-
interval: "daily"
52+
interval: "weekly"
5353
target-branch: "master"
54-
labels: ["dependencies", "github-actions"]
54+
labels: [ "dependencies", "github-actions" ]
55+
cooldown:
56+
default-days: 7

.github/workflows/continuous-integration.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ jobs:
88
runs-on: ubuntu-22.04
99
steps:
1010
- name: Checkout
11-
uses: actions/checkout@v2
11+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1212
with:
13-
fetch-depth: 0 # This causes all history to be fetched, which is required for calculate-version to function
13+
fetch-depth: 0 # This causes all history to be fetched, which is required for calculate-version to function
1414

1515
- name: Install Python 3.9
16-
uses: actions/setup-python@v5
16+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
1717
with:
1818
python-version: 3.9
1919

@@ -30,33 +30,35 @@ jobs:
3030
run: pip install "poetry<2.0.0"
3131

3232
- name: Cache poetry packages
33-
uses: actions/cache@v4
33+
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
3434
with:
3535
path: ~/.cache/pypoetry
36-
key: ${{ runner.os }}-build-cache-poetry-packages-${{ hashFiles('**/poetry.lock') }}
36+
key: ${{ runner.os }}-build-cache-poetry-packages-${{
37+
hashFiles('**/poetry.lock') }}
3738

3839
- name: Cache node modules
39-
uses: actions/cache@v4
40+
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
4041
with:
4142
path: ~/.npm
42-
key: ${{ runner.os }}-build-cache-npm-packages-${{ hashFiles('**/package-lock.json') }}
43+
key: ${{ runner.os }}-build-cache-npm-packages-${{
44+
hashFiles('**/package-lock.json') }}
4345

4446
- name: Install repo
4547
run: make install
4648

4749
- name: Set SPEC_VERSION env var
48-
run: echo ::set-env name=SPEC_VERSION::$(poetry run python scripts/calculate_version.py)
50+
run: echo ::set-env name=SPEC_VERSION::$(poetry run python
51+
scripts/calculate_version.py)
4952
env:
5053
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
5154

5255
- name: Create release (master only)
5356
id: create-release
5457
if: github.ref == 'refs/heads/master'
55-
uses: actions/create-release@v1
58+
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1.1.4
5659
continue-on-error: true
5760
env:
5861
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5962
with:
6063
tag_name: ${{ env.SPEC_VERSION }}
6164
release_name: ${{ env.SPEC_VERSION }}
62-

.github/workflows/sbom.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ jobs:
2424
contents: write
2525
steps:
2626
- name: Checkout
27-
uses: actions/checkout@v5
27+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2828

2929
- name: Setup Python 3.13
30-
uses: actions/setup-python@v5
30+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
3131
with:
3232
python-version: "3.13"
3333

@@ -69,7 +69,7 @@ jobs:
6969
python .github/scripts/sbom_json_to_csv.py sbom.json SBOM_${REPO_NAME}.csv
7070
7171
- name: Upload SBOM CSV as artifact
72-
uses: actions/upload-artifact@v4
72+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
7373
with:
7474
name: sbom-csv
7575
path: SBOM_${{ github.event.repository.name }}.csv
@@ -81,18 +81,15 @@ jobs:
8181
- name: Scan SBOM for Vulnerabilities (JSON)
8282
run: |
8383
grype sbom:sbom.json -o json > grype-report.json
84-
85-
8684
8785
- name: Convert Grype JSON to CSV
8886
run: |
8987
pip install --upgrade pip
9088
REPO_NAME=$(basename $GITHUB_REPOSITORY)
9189
python .github/scripts/grype_json_to_csv.py grype-report.json grype-report-${REPO_NAME}.csv
9290
93-
9491
- name: Upload Vulnerability Report
95-
uses: actions/upload-artifact@v4
92+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
9693
with:
9794
name: grype-report
9895
path: grype-report-${{ github.event.repository.name }}.csv
@@ -104,7 +101,7 @@ jobs:
104101
python .github/scripts/sbom_packages_to_csv.py sbom.json $REPO_NAME
105102
106103
- name: Upload Package Inventory CSV
107-
uses: actions/upload-artifact@v4
104+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
108105
with:
109106
name: sbom-packages
110-
path: sbom-packages-${{ github.event.repository.name }}.csv
107+
path: sbom-packages-${{ github.event.repository.name }}.csv

scripts/config/gitleaks.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# SEE: https://github.com/gitleaks/gitleaks/#configuration
2+
3+
[extend]
4+
useDefault = true # SEE: https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml
5+
6+
[[rules]]
7+
description = "IPv4"
8+
id = "ipv4"
9+
regex = '''[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'''
10+
11+
[rules.allowlist]
12+
regexTarget = "match"
13+
regexes = [
14+
# Exclude the private network IPv4 addresses as well as the DNS servers for Google and OpenDNS
15+
'''(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|172\.(1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3}|192\.168\.[0-9]{1,3}\.[0-9]{1,3}|0\.0\.0\.0|255\.255\.255\.255|8\.8\.8\.8|8\.8\.4\.4|208\.67\.222\.222|208\.67\.220\.220)''',
16+
]
17+
18+
[allowlist]
19+
paths = ['''.terraform.lock.hcl''', '''poetry.lock''', '''yarn.lock''']

scripts/config/pre-commit.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
repos:
2+
- repo: local
3+
hooks:
4+
- id: scan-secrets
5+
name: Scan secrets
6+
entry: ./scripts/githooks/scan-secrets.sh
7+
args: [ "check=staged-changes" ]
8+
language: script
9+
pass_filenames: false

scripts/githooks/scan-secrets.sh

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/bin/bash
2+
3+
# WARNING: Please, DO NOT edit this file! It is maintained in the Repository Template (https://github.com/nhs-england-tools/repository-template). Raise a PR instead.
4+
5+
set -euo pipefail
6+
7+
# Pre-commit git hook to scan for secrets hard-coded in the codebase. This is a
8+
# gitleaks command wrapper. It will run gitleaks natively if it is installed,
9+
# otherwise it will run it in a Docker container.
10+
#
11+
# Usage:
12+
# $ [options] ./scan-secrets.sh
13+
#
14+
# Options:
15+
# check={whole-history,last-commit,staged-changes} # Type of the check to run, default is 'staged-changes'
16+
# FORCE_USE_DOCKER=true # If set to true the command is run in a Docker container, default is 'false'
17+
# VERBOSE=true # Show all the executed commands, default is 'false'
18+
#
19+
# Exit codes:
20+
# 0 - No leaks present
21+
# 1 - Leaks or error encountered
22+
# 126 - Unknown flag
23+
24+
# ==============================================================================
25+
26+
function main() {
27+
28+
cd "$(git rev-parse --show-toplevel)"
29+
30+
if command -v gitleaks > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then
31+
dir="$PWD"
32+
cmd="$(get-cmd-to-run)" run-gitleaks-natively
33+
else
34+
dir="/workdir"
35+
cmd="$(get-cmd-to-run)" run-gitleaks-in-docker
36+
fi
37+
}
38+
39+
# Get Gitleaks command to execute and configuration.
40+
# Arguments (provided as environment variables):
41+
# dir=[project's top-level directory]
42+
function get-cmd-to-run() {
43+
44+
check=${check:-staged-changes}
45+
case $check in
46+
"whole-history")
47+
cmd="detect --source $dir --verbose --redact"
48+
;;
49+
"last-commit")
50+
cmd="detect --source $dir --verbose --redact --log-opts -1"
51+
;;
52+
"staged-changes")
53+
cmd="protect --source $dir --verbose --staged"
54+
;;
55+
esac
56+
# Include base line file if it exists
57+
if [ -f "$dir/scripts/config/.gitleaks-baseline.json" ]; then
58+
cmd="$cmd --baseline-path $dir/scripts/config/.gitleaks-baseline.json"
59+
fi
60+
# Include the config file
61+
cmd="$cmd --config $dir/scripts/config/gitleaks.toml"
62+
63+
echo "$cmd"
64+
}
65+
66+
# Run Gitleaks natively.
67+
# Arguments (provided as environment variables):
68+
# cmd=[command to run]
69+
function run-gitleaks-natively() {
70+
71+
# shellcheck disable=SC2086
72+
gitleaks $cmd
73+
}
74+
75+
# Run Gitleaks in a Docker container.
76+
# Arguments (provided as environment variables):
77+
# cmd=[command to run]
78+
# dir=[directory to mount as a volume]
79+
function run-gitleaks-in-docker() {
80+
81+
# shellcheck disable=SC1091
82+
source ./scripts/docker/docker.lib.sh
83+
84+
# shellcheck disable=SC2155
85+
local image=$(name=ghcr.io/gitleaks/gitleaks docker-get-image-version-and-pull)
86+
# shellcheck disable=SC2086
87+
docker run --rm --platform linux/amd64 \
88+
--volume "$PWD:$dir" \
89+
--workdir $dir \
90+
"$image" \
91+
$cmd
92+
}
93+
94+
# ==============================================================================
95+
96+
function is-arg-true() {
97+
98+
if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then
99+
return 0
100+
else
101+
return 1
102+
fi
103+
}
104+
105+
# ==============================================================================
106+
107+
is-arg-true "${VERBOSE:-false}" && set -x
108+
109+
main "$@"
110+
111+
exit 0

0 commit comments

Comments
 (0)