Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
8fc8c13
sbom configuration
koppuravuris Sep 17, 2025
f5cde73
sonar security warning fix new
koppuravuris Sep 17, 2025
6ee9ef8
Merge pull request #526 from NHSDigital/APM-6390
koppuravuris Sep 18, 2025
6301787
Added new SBOM config for generation all 3 reports
saptarshimandal1 Oct 2, 2025
08d643e
Merge pull request #527 from NHSDigital/feature/APM-6390
saptarshimandal1 Oct 2, 2025
e7ae503
Renamed SBOM readme
CLJ2006 Oct 8, 2025
4dd1844
Merge pull request #528 from NHSDigital/APM-6390--Rename-SBPM-readme
CLJ2006 Oct 8, 2025
108396a
Updated the ansible playbook to use the navtive docker cli commands
sathiya-nhs Nov 19, 2025
9abc559
Merge pull request #530 from NHSDigital/APIM-Pipeline-fix
sathiya-nhs Nov 19, 2025
dab5418
Updated the python and dependencies to latest stable versions to supp…
sathiya-nhs Nov 21, 2025
29bd81e
Update python version in github actions
sathiya-nhs Nov 21, 2025
cff0f2b
Updated python version in azure pipeline
sathiya-nhs Nov 21, 2025
b618997
Updated python version in azure pipeline
sathiya-nhs Nov 21, 2025
9ab493a
Updated zure pipeline with tool cache path env var
sathiya-nhs Nov 21, 2025
66b7cf9
Updated the pipeline to tun ansible tests with python 3.13.7
sathiya-nhs Nov 21, 2025
a894491
Updated the pipeline to tun ansible tests with python 3.13
sathiya-nhs Nov 21, 2025
d2c8990
Removed ansible deprecated plugin
sathiya-nhs Nov 21, 2025
b66ad9c
Updated pydantic regex pattern and ansbile pull task
sathiya-nhs Nov 21, 2025
7ca0430
Updated pydantic regex pattern in all references
sathiya-nhs Nov 21, 2025
61caee3
Restored the project to use pydantic 1.10.15 for v1 support
sathiya-nhs Nov 21, 2025
70bbd18
Added code to load the var.file for ansible integration tests
sathiya-nhs Nov 21, 2025
86f0d04
Added code to load the var.file for ansible integration tests
sathiya-nhs Nov 21, 2025
4950921
Added code to load the var.file for ansible integration tests
sathiya-nhs Nov 21, 2025
5e6f895
Testing
sathiya-nhs Nov 21, 2025
6b2da39
Make file and build-prereqs pipeline updated to use python 3.13
sathiya-nhs Nov 21, 2025
ddd999f
Set LD_LIBRARY_PATH for utils build and deploy pipeline
sathiya-nhs Nov 21, 2025
17a9ab3
Set LD_LIBRARY_PATH for utils build and deploy pipeline
sathiya-nhs Nov 21, 2025
ce6a14b
Revert LD_LIBRARY_PATH changes
sathiya-nhs Nov 21, 2025
dac7aab
Debug the python path
sathiya-nhs Nov 21, 2025
22873f4
Debug the python path
sathiya-nhs Nov 21, 2025
3e64e3a
Added a additional step to query and set the python tool cache
sathiya-nhs Nov 24, 2025
28f6d88
Added a additional step to query and set the python tool cache
sathiya-nhs Nov 24, 2025
b1e6a2b
Added a check after the python runtime to update library path
sathiya-nhs Nov 24, 2025
e01eeca
Added a check after the python runtime to update library path
sathiya-nhs Nov 24, 2025
3cbd9d3
Added a check after the python runtime to update library path
sathiya-nhs Nov 24, 2025
f97bbc3
Updated the changes to the deploy pipeline
sathiya-nhs Nov 24, 2025
dcacfcb
Updated the changes to the deploy pipeline
sathiya-nhs Nov 24, 2025
5a22065
Ansible environment varible converted to bool from str
sathiya-nhs Nov 24, 2025
21e4624
Ping var evalution has been updated to latest ansible commands
sathiya-nhs Nov 24, 2025
30d26ea
Update when condition for deploy manifest playbook
sathiya-nhs Nov 24, 2025
1747137
Updated urllib3 to 2.5.0
sathiya-nhs Nov 24, 2025
df65928
Updated ansible module to remove unsafetext function
sathiya-nhs Nov 24, 2025
33feeed
Ansible unsafetext module updated
sathiya-nhs Nov 24, 2025
9ee39d8
Updated the default fallback python to 3.13
sathiya-nhs Nov 25, 2025
2fd014f
Setting deploy pipeline runtime python to 3.13
sathiya-nhs Nov 25, 2025
5887212
SBOM testing
sathiya-nhs Nov 25, 2025
a7084f8
SBOM testing- dummy
sathiya-nhs Nov 25, 2025
35b709c
Parameter the deploy pipeline runtime python
sathiya-nhs Nov 25, 2025
0bd84f4
Setting sbom runner to ubuntu-22.04
sathiya-nhs Nov 25, 2025
05c9494
Added debug step to SBPM workflow
sathiya-nhs Nov 25, 2025
6ac6c19
Added debug step to SBPM workflow
sathiya-nhs Nov 25, 2025
72ea82d
Added debug step to SBPM workflow
sathiya-nhs Nov 25, 2025
85bbfdb
Added debug step to SBPM workflow
sathiya-nhs Nov 25, 2025
f57feb6
Updated cryptography to 44.0.1
sathiya-nhs Nov 25, 2025
45b267d
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
eb0b4b6
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
5cb3a3c
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
247417a
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
4e6cda8
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
a11397a
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
687e00a
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
b4e55ec
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
88e26ef
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
f24aed2
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
22b1b0b
Add sbom-test workflow
sathiya-nhs Nov 25, 2025
ae9f51d
Set default python to 3.13 for deploy pipeline
sathiya-nhs Nov 26, 2025
3c4bd3d
Rolling back the test SBOM changes
sathiya-nhs Nov 26, 2025
96c8fc9
Updated the python version patch query
sathiya-nhs Dec 3, 2025
5dbaa1d
Merge pull request #531 from NHSDigital/APIM-6679-Python-Update
sathiya-nhs Dec 9, 2025
e805f6d
Set pr cleanup pipeline to use python 3.13
sathiya-nhs Dec 9, 2025
3221b2a
Merge branch 'master' into APIM-6679-Python-Update
sathiya-nhs Dec 9, 2025
090f7af
Add click to project dependency
sathiya-nhs Dec 9, 2025
854bdec
Merge branch 'APIM-6679-Python-Update' of https://github.com/NHSDigit…
sathiya-nhs Dec 9, 2025
89977a1
Update ansible playbook
sathiya-nhs Dec 9, 2025
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
58 changes: 58 additions & 0 deletions .github/SBOM-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SBOM & Vulnerability Scanning Automation

This repository uses GitHub Actions to automatically generate a Software Bill of Materials (SBOM), scan for vulnerabilities, and produce package inventory reports.

All reports are named with the repository name for easy identification.

## Features

SBOM Generation: Uses Syft to generate an SPDX JSON SBOM.
SBOM Merging: Merges SBOMs for multiple tools if needed.
SBOM to CSV: Converts SBOM JSON to a CSV report.
Vulnerability Scanning: Uses Grype to scan the SBOM for vulnerabilities and outputs a CSV report.
Package Inventory: Extracts a simple package list (name, type, version) as a CSV.
Artifacts: All reports are uploaded as workflow artifacts with the repository name in the filename.

## Workflow Overview

The main workflow is defined in .github/workflows/sbom.yml

## Scripts

scripts/create-sbom.sh
Generates an SBOM for the repo and for specified tools, merging them as needed.
scripts/update-sbom.py
Merges additional SBOMs into the main SBOM.
.github/scripts/sbom_json_to_csv.py
Converts the SBOM JSON to a detailed CSV report.
.github/scripts/grype_json_to_csv.py
Converts Grype’s vulnerability scan JSON output to a CSV report.
Output columns: REPO, NAME, INSTALLED, FIXED-IN, TYPE, VULNERABILITY, SEVERITY
.github/scripts/sbom_packages_to_csv.py
Extracts a simple package inventory from the SBOM.
Output columns: name, type, version

## Example Reports

Vulnerability Report
grype-report-[RepoName].csv
REPO,NAME,INSTALLED,FIXED-IN,TYPE,VULNERABILITY,SEVERITY
my-repo,Flask,2.1.2,,library,CVE-2022-12345,High
...

Package Inventory
sbom-packages-[RepoName].csv
name,type,version
Flask,library,2.1.2
Jinja2,library,3.1.2
...

## Usage

Push to main branch or run the workflow manually.
Download artifacts from the workflow run summary.

## Customization

Add more tools to scripts/create-sbom.sh as needed.
Modify scripts to adjust report formats or add more metadata.
28 changes: 28 additions & 0 deletions .github/scripts/grype_json_to_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json
import csv
import sys

input_file = sys.argv[1] if len(sys.argv) > 1 else "grype-report.json"
output_file = sys.argv[2] if len(sys.argv) > 2 else "grype-report.csv"

with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)

columns = ["NAME", "INSTALLED", "FIXED-IN", "TYPE", "VULNERABILITY", "SEVERITY"]

with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=columns)
writer.writeheader()
for match in data.get("matches", []):
pkg = match.get("artifact", {})
vuln = match.get("vulnerability", {})
row = {
"NAME": pkg.get("name", ""),
"INSTALLED": pkg.get("version", ""),
"FIXED-IN": vuln.get("fix", {}).get("versions", [""])[0] if vuln.get("fix", {}).get("versions") else "",
"TYPE": pkg.get("type", ""),
"VULNERABILITY": vuln.get("id", ""),
"SEVERITY": vuln.get("severity", ""),
}
writer.writerow(row)
print(f"CSV export complete: {output_file}")
78 changes: 78 additions & 0 deletions .github/scripts/sbom_json_to_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import json
import csv
import sys
# from pathlib import Path
from tabulate import tabulate

input_file = sys.argv[1] if len(sys.argv) > 1 else "sbom.json"
output_file = sys.argv[2] if len(sys.argv) > 2 else "sbom.csv"

with open(input_file, "r", encoding="utf-8") as f:
sbom = json.load(f)

packages = sbom.get("packages", [])

columns = [
"name",
"versionInfo",
"type",
"supplier",
"downloadLocation",
"licenseConcluded",
"licenseDeclared",
"externalRefs"
]


def get_type(pkg):
spdxid = pkg.get("SPDXID", "")
if "-" in spdxid:
parts = spdxid.split("-")
if len(parts) > 2:
return parts[2]
refs = pkg.get("externalRefs", [])
for ref in refs:
if ref.get("referenceType") == "purl":
return ref.get("referenceLocator", "").split("/")[0]
return ""


def get_external_refs(pkg):
refs = pkg.get("externalRefs", [])
return ";".join([ref.get("referenceLocator", "") for ref in refs])


with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=columns)
writer.writeheader()
for pkg in packages:
row = {
"name": pkg.get("name", ""),
"versionInfo": pkg.get("versionInfo", ""),
"type": get_type(pkg),
"supplier": pkg.get("supplier", ""),
"downloadLocation": pkg.get("downloadLocation", ""),
"licenseConcluded": pkg.get("licenseConcluded", ""),
"licenseDeclared": pkg.get("licenseDeclared", ""),
"externalRefs": get_external_refs(pkg)
}
writer.writerow(row)

print(f"CSV export complete: {output_file}")


with open("sbom_table.txt", "w", encoding="utf-8") as f:
table = []
for pkg in packages:
row = [
pkg.get("name", ""),
pkg.get("versionInfo", ""),
get_type(pkg),
pkg.get("supplier", ""),
pkg.get("downloadLocation", ""),
pkg.get("licenseConcluded", ""),
pkg.get("licenseDeclared", ""),
get_external_refs(pkg)
]
table.append(row)
f.write(tabulate(table, columns, tablefmt="grid"))
28 changes: 28 additions & 0 deletions .github/scripts/sbom_packages_to_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json
import csv
import sys
import os

input_file = sys.argv[1] if len(sys.argv) > 1 else "sbom.json"
repo_name = sys.argv[2] if len(sys.argv) > 2 else os.getenv("GITHUB_REPOSITORY", "unknown-repo").split("/")[-1]
output_file = f"sbom-packages-{repo_name}.csv"

with open(input_file, "r", encoding="utf-8") as f:
sbom = json.load(f)

packages = sbom.get("packages", [])

columns = ["name", "type", "version"]

with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=columns)
writer.writeheader()
for pkg in packages:
row = {
"name": pkg.get("name", ""),
"type": pkg.get("type", ""),
"version": pkg.get("versionInfo", "")
}
writer.writerow(row)

print(f"Package list CSV generated: {output_file}")
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
with:
fetch-depth: 0 # This causes all history to be fetched, which is required for calculate-version to function

- name: Install Python 3.8
- name: Install Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.8
python-version: 3.13

- name: Install poetry
run: pip install poetry
Expand Down
110 changes: 110 additions & 0 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: SBOM Vulnerability Scanning

on:
workflow_dispatch:
inputs:
environment:
description: "Run SBOM check"
required: true
type: choice
options:
- yes
- no

env:
SYFT_VERSION: "1.27.1"
TF_VERSION: "1.12.2"

jobs:
deploy:
name: Software Bill of Materials
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd

- uses: terraform-linters/setup-tflint@ae78205cfffec9e8d93fd2b3115c7e9d3166d4b6
name: Setup TFLint

- name: Set architecture variable
id: os-arch
run: |
case "${{ runner.arch }}" in
X64) ARCH="amd64" ;;
ARM64) ARCH="arm64" ;;
esac
echo "arch=${ARCH}" >> $GITHUB_OUTPUT

- name: Download and setup Syft
run: |
DOWNLOAD_URL="https://github.com/anchore/syft/releases/download/v${{ env.SYFT_VERSION }}/syft_${{ env.SYFT_VERSION }}_linux_${{ steps.os-arch.outputs.arch }}.tar.gz"
echo "Downloading: ${DOWNLOAD_URL}"

curl -L -o syft.tar.gz "${DOWNLOAD_URL}"
tar -xzf syft.tar.gz
chmod +x syft

# Add to PATH for subsequent steps
echo "$(pwd)" >> $GITHUB_PATH

- name: Create SBOM
run: bash scripts/create-sbom.sh terraform python tflint

- name: Convert SBOM JSON to CSV
run: |
pip install --upgrade pip
pip install tabulate
REPO_NAME=$(basename $GITHUB_REPOSITORY)
python .github/scripts/sbom_json_to_csv.py sbom.json SBOM_${REPO_NAME}.csv

- name: Upload SBOM CSV as artifact
uses: actions/upload-artifact@v4
with:
name: sbom-csv
path: SBOM_${{ github.event.repository.name }}.csv

- name: Install Grype
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

- name: Scan SBOM for Vulnerabilities (JSON)
run: |
grype sbom:sbom.json -o json > grype-report.json



- name: Convert Grype JSON to CSV
run: |
pip install --upgrade pip
REPO_NAME=$(basename $GITHUB_REPOSITORY)
python .github/scripts/grype_json_to_csv.py grype-report.json grype-report-${REPO_NAME}.csv


- name: Upload Vulnerability Report
uses: actions/upload-artifact@v4
with:
name: grype-report
path: grype-report-${{ github.event.repository.name }}.csv

- name: Generate Package Inventory CSV
run: |
pip install --upgrade pip
REPO_NAME=$(basename $GITHUB_REPOSITORY)
python .github/scripts/sbom_packages_to_csv.py sbom.json $REPO_NAME

- name: Upload Package Inventory CSV
uses: actions/upload-artifact@v4
with:
name: sbom-packages
path: sbom-packages-${{ github.event.repository.name }}.csv
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
# api-management-utils
Scripts and utilities used across API managment platform and services


################################################################################
########### Python upgrade to 3.13 ###########
########### Utils Repo has been updated to python 3.13 ###########
################################################################################

########### We are continuing to support python 3.8/9(which are currently out of support) until January 26th 2026 ###########
########### After the deadline your pipelines will fail if you are using python version 3.8/9 ###########

Python upgrade related changes
###############################
Projects using Python versions older than 3.13 and extending their pipeline with the utils repository must update their pipelines to ensure compatibility with the latest changes.
For detailed guidance, please refer to the APIM FAQ page:
https://nhsd-confluence.digital.nhs.uk/spaces/APM/pages/1226682275/Pipeline+Queries

Note: Projects running Python version 3.13 or later do not need any pipeline modifications.



## Scripts
* `template.py` - cli for basic jinja templating
* `test_pull_request_deployments.py` - cli for testing utils against other repositories
Expand Down
4 changes: 2 additions & 2 deletions ansible/collections/ansible_collections/nhsd/apigee/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ update-schema:
@PYTHONPATH=../../../ poetry run python scripts/update_schema.py

unit-test:
@poetry run ansible-test units --python=3.8
@poetry run ansible-test units --python=3.13

integration-test:
@poetry run ansible-test integration --python=3.8
@poetry run ansible-test integration --python=3.13

test: unit-test integration-test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import typing

from ansible_collections.nhsd.apigee.plugins.module_utils import constants
from ansible.utils.unsafe_proxy import AnsibleUnsafeText


def exclude_keys(dict_, keys_to_ignore):
Expand All @@ -19,7 +20,7 @@ def delta(before, after, keys_to_ignore=None):
before,
after,
ignore_order=True,
ignore_type_in_groups=[(ansible.utils.unsafe_proxy.AnsibleUnsafeText,str)],
ignore_type_in_groups=[(AnsibleUnsafeText,str)],
exclude_paths=[f"root['{key}']" for key in keys_to_ignore],
).to_json()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
# since products can only be created if they reference proxies
# that exist. This safety check can be removed when the
# manifest manages proxies too.
when: item.name | regex_search('^' + SERVICE_NAME + '-' + PULL_REQUEST | default(APIGEE_ENVIRONMENT))
when: (item.name is match('^' ~ SERVICE_NAME ~ '-' ~ (PULL_REQUEST | default(APIGEE_ENVIRONMENT))))

# - name: deploy apigee specs
# nhsd.apigee.deploy_spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
- name: validate test manifest files
include_role:
name: validate_manifest
vars_from: "{{ DIST_DIR }}/vars.yml"
loop: "{{ DIST_DIRS }}"
loop_control:
loop_var: DIST_DIR
Loading