Skip to content

fix(frontend): remediate frontend + extension audit findings #6084

fix(frontend): remediate frontend + extension audit findings

fix(frontend): remediate frontend + extension audit findings #6084

Workflow file for this run

name: SBOM
on:
push:
branches: [ main ]
pull_request:
workflow_dispatch:
permissions:
contents: read
packages: read
concurrency:
group: sbom-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-sbom:
runs-on: ubuntu-latest
timeout-minutes: 20
env:
# Populated in "Gather SBOM files" step
SBOM_FILES: ""
SBOM_COUNT: "0"
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Generate Python SBOM (CycloneDX)
run: |
set -euo pipefail
repo_root="${GITHUB_WORKSPACE:-$PWD}"
# Install CycloneDX Python tooling. Newer releases expose the `cyclonedx-py` CLI,
# older ones expose `cyclonedx-bom`. We support both.
python -m pip install -q cyclonedx-bom
gen_from_requirements() {
local req="$1"
local req_dir req_file out_file
local pyproject_args=()
req_dir="$(dirname "$req")"
req_file="$(basename "$req")"
out_file="$repo_root/sbom-python.cdx.json"
if [ -f "$repo_root/pyproject.toml" ]; then
pyproject_args=(--pyproject "$repo_root/pyproject.toml")
fi
echo "Generating Python SBOM from ${req}"
# Try modern CLI first: cyclonedx-py requirements <file>
if command -v cyclonedx-py >/dev/null 2>&1; then
echo "Using cyclonedx-py"
if (cd "$req_dir" && cyclonedx-py requirements "$req_file" "${pyproject_args[@]}" -o "$out_file"); then
return 0
fi
echo "cyclonedx-py positional requirements failed; trying input flag form"
if (cd "$req_dir" && cyclonedx-py requirements -i "$req_file" "${pyproject_args[@]}" -o "$out_file"); then
return 0
fi
echo "cyclonedx-py input flag requirements failed; trying stdin form"
if (cd "$req_dir" && cyclonedx-py requirements - -o "$out_file" < "$req_file"); then
return 0
fi
echo "cyclonedx-py failed; will try fallbacks"
fi
# Module invocation for modern CLI (avoid depending on CLI binary layout)
if python -c "import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('cyclonedx_py') else 1)" >/dev/null 2>&1; then
echo "Using python -m cyclonedx_py"
if (cd "$req_dir" && python -m cyclonedx_py requirements "$req_file" -o "$out_file"); then
return 0
fi
echo "python -m cyclonedx_py positional requirements failed; trying stdin form"
if (cd "$req_dir" && python -m cyclonedx_py requirements - -o "$out_file" < "$req_file"); then
return 0
fi
echo "python -m cyclonedx_py failed; trying legacy CLI"
fi
# Legacy CLI: cyclonedx-bom -r -i <file>
if command -v cyclonedx-bom >/dev/null 2>&1; then
echo "Using cyclonedx-bom (legacy)"
if (cd "$req_dir" && cyclonedx-bom -r -i "$req_file" -o "$out_file"); then
return 0
fi
echo "cyclonedx-bom failed; trying legacy module"
fi
# Legacy module invocation
if python -c "import importlib.util, sys; sys.exit(0 if importlib.util.find_spec('cyclonedx_bom') else 1)" >/dev/null 2>&1; then
echo "Using python -m cyclonedx_bom (legacy)"
if (cd "$req_dir" && python -m cyclonedx_bom -r -i "$req_file" -o "$out_file"); then
return 0
fi
fi
echo "All Python SBOM generation strategies failed" >&2
return 1
}
gen_requirements_from_pyproject() {
local pyproject="$1"
local generated_req
generated_req="$(mktemp)"
python - "$pyproject" "$generated_req" <<'PY'
import sys
import tomllib
from pathlib import Path
pyproject = Path(sys.argv[1])
generated_req = Path(sys.argv[2])
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
dependencies = data.get("project", {}).get("dependencies") or []
dependencies = [str(dependency).strip() for dependency in dependencies if str(dependency).strip()]
if not dependencies:
raise SystemExit(f"No [project].dependencies entries found in {pyproject}")
generated_req.write_text("\n".join(dependencies) + "\n", encoding="utf-8")
print(f"Wrote {len(dependencies)} requirements from {pyproject} to {generated_req}")
PY
if gen_from_requirements "$generated_req"; then
rm -f "$generated_req"
return 0
fi
rm -f "$generated_req"
return 1
}
if [ -f "$repo_root/tldw_Server_API/requirements.txt" ]; then
gen_from_requirements "$repo_root/tldw_Server_API/requirements.txt"
elif [ -f "$repo_root/requirements.txt" ]; then
gen_from_requirements "$repo_root/requirements.txt"
elif [ -f "$repo_root/pyproject.toml" ]; then
gen_requirements_from_pyproject "$repo_root/pyproject.toml"
else
echo "No Python dependency source found; cannot generate SBOM" >&2
exit 1
fi
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '20'
- name: Generate Node SBOM (CycloneDX NPM)
run: |
if [ -f apps/package-lock.json ]; then \
(cd apps && npx -y @cyclonedx/cyclonedx-npm --output-file ../sbom-node.cdx.json); \
elif [ -f apps/tldw-frontend/package-lock.json ]; then \
(cd apps/tldw-frontend && npx -y @cyclonedx/cyclonedx-npm --output-file ../../sbom-node.cdx.json); \
else \
echo "No package-lock.json found; skipping Node SBOM"; \
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd
- name: Authenticate to GHCR
if: ${{ hashFiles('sbom-python.cdx.json') != '' && hashFiles('sbom-node.cdx.json') != '' }}
run: |
echo "${{ github.token }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
- name: Resolve CycloneDX CLI digest
if: ${{ hashFiles('sbom-python.cdx.json') != '' && hashFiles('sbom-node.cdx.json') != '' }}
run: |
set -euo pipefail
ref="ghcr.io/cyclonedx/cyclonedx-cli:0.30.0"
echo "Resolving digest for ${ref}"
# Prefer buildx imagetools; fallback to manifest inspect if needed
if docker buildx imagetools inspect "$ref" >/dev/null 2>&1; then \
digest=$(docker buildx imagetools inspect "$ref" | awk '/^Digest:/ {print $2; exit}'); \
else \
digest=$(docker manifest inspect "$ref" | jq -r '.manifests[0].digest' || true); \
fi
if [ -z "${digest:-}" ] || ! echo "$digest" | grep -Eq '^sha256:[0-9a-f]{64}$'; then \
echo "Failed to resolve digest for $ref"; \
exit 1; \
fi
echo "CDX_CLI_DIGEST=$digest" >> "$GITHUB_ENV"
echo "Resolved digest: $digest"
- name: Merge SBOMs (CycloneDX CLI)
run: |
set -euo pipefail
if [ -f sbom-python.cdx.json ] && [ -f sbom-node.cdx.json ]; then \
docker run --rm -v "$PWD":/work -w /work "ghcr.io/cyclonedx/cyclonedx-cli@${CDX_CLI_DIGEST}" \
merge --input-files sbom-python.cdx.json sbom-node.cdx.json --output-file sbom.cdx.json; \
elif [ -f sbom-python.cdx.json ]; then \
cp sbom-python.cdx.json sbom.cdx.json; \
elif [ -f sbom-node.cdx.json ]; then \
cp sbom-node.cdx.json sbom.cdx.json; \
else \
echo "No SBOMs generated"; \
exit 1; \
fi
- name: Upload SBOM artifact
uses: actions/upload-artifact@v7
with:
name: sbom-cyclonedx
path: sbom.cdx.json
- name: Validate SBOM (CycloneDX CLI - pinned digest)
if: ${{ hashFiles('sbom.cdx.json') != '' && hashFiles('sbom-python.cdx.json') != '' && hashFiles('sbom-node.cdx.json') != '' }}
continue-on-error: true
run: |
docker run --rm -v "$PWD":/work -w /work "ghcr.io/cyclonedx/cyclonedx-cli@${CDX_CLI_DIGEST}" \
validate --input-file sbom.cdx.json