Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
445c9b8
pom: Update avro to 1.11.4 #TASK-7908
juanfeSanahuja Sep 16, 2025
5614a68
Merge branch 'develop' into TASK-7908
juanfeSanahuja Sep 17, 2025
c6e01f8
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
7a36b75
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
44eaf4d
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
63b02c8
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
784c542
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
bb460d9
Compare vulnerabilities workflow #TASK-7908
juanfeSanahuja Sep 17, 2025
4fb86ca
Added clean to build command #TASK-7908
juanfeSanahuja Sep 17, 2025
17e2e68
Added clean to build command #TASK-7908
juanfeSanahuja Sep 17, 2025
d143998
Added zookeper version 3.9.3 #TASK-7908
juanfeSanahuja Sep 17, 2025
e6d0193
Added report pdf
juanfeSanahuja Sep 18, 2025
958908d
Restore solr version to 8.8.2
juanfeSanahuja Sep 18, 2025
809a737
Added compare-vulnerabilities in PR #TASK-7908
juanfeSanahuja Sep 18, 2025
23cfb51
Change solr.version to 8.11.4 #TASK-7908
juanfeSanahuja Sep 18, 2025
091ea3f
Restore solr version to 8.8.2
juanfeSanahuja Sep 19, 2025
c5565cf
Change solr.version to 8.11.4 #TASK-7908
juanfeSanahuja Sep 19, 2025
a745de9
Restore solr version to 8.8.2 and add webhook to slack
juanfeSanahuja Sep 19, 2025
82e1b90
Change solr.version to 8.11.4 #TASK-7908
juanfeSanahuja Sep 19, 2025
3fee460
Restore solr version to 8.8.2 and add webhook to slack
juanfeSanahuja Sep 19, 2025
723e3fd
Change solr.version to 8.11.4 #TASK-7908
juanfeSanahuja Sep 19, 2025
47ef888
Test release v2 alpha vuln-diff
juanfeSanahuja Sep 23, 2025
59e1812
Test release v2 alpha vuln-diff
juanfeSanahuja Sep 23, 2025
e444e43
Test release v2 alpha vuln-diff
juanfeSanahuja Sep 25, 2025
793b192
Test release v2 alpha vuln-diff
juanfeSanahuja Sep 25, 2025
3faf0d5
change solr to 8.8.2
juanfeSanahuja Oct 1, 2025
40386f9
Add jdk in vuln diff
juanfeSanahuja Oct 3, 2025
a84aeb5
Change solr version to 8.11.4
juanfeSanahuja Oct 3, 2025
769493d
Change comment
juanfeSanahuja Oct 3, 2025
bae627c
Vulnerability Diff fase 1
juanfeSanahuja Oct 6, 2025
a32aeff
Refactor PR and vuln-diff
juanfeSanahuja Oct 7, 2025
2b9cefb
Update html logo vuln-diff #TASK-7908
juanfeSanahuja Oct 7, 2025
4a0ce9d
Prepare reusable Compare vulnerabilities #TASK-7908
juanfeSanahuja Oct 13, 2025
1727a95
Added purpeteer cache
juanfeSanahuja Oct 14, 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
55 changes: 55 additions & 0 deletions .github/workflows/compare-vulnerabilities-PR.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: PR – Vulnerability guard
run-name: 'Compare vulnerabilities between (Base) ${{ github.event.pull_request.base.ref }} and (Head) ${{ github.event.pull_request.head.ref }} (${{ github.event.pull_request.head.sha }}) by @${{ github.actor }}'
on:
pull_request:
types: [ opened, synchronize, reopened ]

permissions:
contents: read
pull-requests: write # needed to create/update PR comments

jobs:
compare-branches:
runs-on: ${{ vars.UBUNTU_VERSION }}

steps:
# 1) Checkout head branch only
- name: Checkout PR head
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
fetch-tags: true

- name: Set up JDK 8
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '8'
cache: 'maven'

#
# - name: Ensure base is available
# run: |
# git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }} --tags --prune

# 3) Run the action
- name: Vulnerability Diff (Syft+Grype)
uses: sec-open/[email protected]
with:
base_ref: ${{ github.event.pull_request.base.ref }}
head_ref: ${{ github.event.pull_request.head.sha }}
github_token: ${{ secrets.GITHUB_TOKEN }}
slack_webhook_url: ${{ secrets.SLACK_SECURITY_WEBHOOK_URL }}
html_logo_url: "https://zettagenomics.com/wp-content/uploads/2022/10/Zetta-reversed-out-full-logo-dark-background.png"

# write_summary: "true"
# upload_artifact: "true"
# min_severity: "LOW"
# report_html: "true"
# report_pdf: "true"
# pr_comment: "true"
# artifact_name: "vulnerability-diff-${{ github.event.inputs.branch_a }}-vs-${{ github.event.inputs.branch_b }}"
# pr_comment_marker: "<!-- vuln-diff-action:comment -->"

# title_logo_url: "https://zettagenomics.com/wp-content/uploads/2022/10/Zetta-reversed-out-full-logo-dark-background.png"
306 changes: 54 additions & 252 deletions .github/workflows/compare-vulnerabilities.yml
Original file line number Diff line number Diff line change
@@ -1,273 +1,75 @@
name: Compare vulnerabilities (Syft SBOM -> Grype) between two branches (robust)

name: Compare vulnerabilities (Syft SBOM -> Grype) between two branches
run-name: 'Compare vulnerabilities between ${{ inputs.branch_a }} (base) and ${{ inputs.branch_b }} (head) by @${{ github.actor }}'
on:
workflow_call:
inputs:
branch_a:
type: string
description: 'Base branch (e.g. develop)'
required: true
branch_b:
type: string
description: 'Head branch (e.g. TASK-1234)'
required: true
secrets:
SLACK_SECURITY_WEBHOOK_URL:
required: false

workflow_dispatch:
inputs:
branch_a:
description: 'First branch to compare (e.g. main)'
description: 'Base branch (e.g. develop)'
required: true
default: 'develop'
branch_b:
description: 'Second branch to compare (e.g. feature/fix-branch)'
description: 'Head branch (e.g. TASK-1234)'
required: true
default: ''

jobs:
compare-sbom-grype:
runs-on: ubuntu-latest
env:
REPORT_DIR: reports
steps:
- name: Prepare workspace
run: |
set -euo pipefail
mkdir -p "${REPORT_DIR}"

- name: Checkout branch A
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch_a }}
path: branchA
fetch-depth: 0
compare-branches:
runs-on: ${{ vars.UBUNTU_VERSION }}

- name: Checkout branch B
steps:
- name: Checkout head branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch_b }}
path: branchB
fetch-depth: 0
fetch-tags: true
- name: Restore Puppeteer cache
uses: actions/cache@v4
with:
path: ~/.cache/puppeteer
key: puppeteer-${{ runner.os }}

- name: Install dependencies (jq, unzip)
run: |
sudo apt-get update
sudo apt-get install -y jq unzip

- name: Install Syft
run: |
set -euo pipefail
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sudo sh -s -- -b /usr/local/bin
syft version

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

- name: Generate SBOM for branch A (CycloneDX JSON)
run: |
set -euo pipefail
syft dir:./branchA -o cyclonedx-json="${REPORT_DIR}/branchA-sbom.cdx.json"

- name: Generate SBOM for branch B (CycloneDX JSON)
run: |
set -euo pipefail
syft dir:./branchB -o cyclonedx-json="${REPORT_DIR}/branchB-sbom.cdx.json"

- name: Scan SBOM with Grype (branch A)
run: |
set -euo pipefail
grype sbom:"${REPORT_DIR}/branchA-sbom.cdx.json" -o json > "${REPORT_DIR}/branchA-grype.json"

- name: Scan SBOM with Grype (branch B)
run: |
set -euo pipefail
grype sbom:"${REPORT_DIR}/branchB-sbom.cdx.json" -o json > "${REPORT_DIR}/branchB-grype.json"

- name: "debug - show match counts and sample (for troubleshooting)"
id: debug
run: |
set -euo pipefail
echo "---- branchA summary ----"
if [ -f "${REPORT_DIR}/branchA-grype.json" ]; then
jq '.matches | length' "${REPORT_DIR}/branchA-grype.json" || true
jq '.matches[0:5]' "${REPORT_DIR}/branchA-grype.json" || true
else
echo "branchA-grype.json missing"
fi
echo "---- branchB summary ----"
if [ -f "${REPORT_DIR}/branchB-grype.json" ]; then
jq '.matches | length' "${REPORT_DIR}/branchB-grype.json" || true
jq '.matches[0:5]' "${REPORT_DIR}/branchB-grype.json" || true
else
echo "branchB-grype.json missing"
fi

- name: Generate comparison report (table + full MD)
id: gen_report
run: |
set -euo pipefail
A_BRANCH="${{ github.event.inputs.branch_a }}"
B_BRANCH="${{ github.event.inputs.branch_b }}"
A_GRYPE="${REPORT_DIR}/branchA-grype.json"
B_GRYPE="${REPORT_DIR}/branchB-grype.json"
OUT="${REPORT_DIR}/comparison-report.md"
mkdir -p "$(dirname "$OUT")"

# comprueba que los JSON existen
if [ ! -s "$A_GRYPE" ]; then
echo "ERROR: ${A_GRYPE} not found or empty" >&2
exit 1
fi
if [ ! -s "$B_GRYPE" ]; then
echo "ERROR: ${B_GRYPE} not found or empty" >&2
exit 1
fi

# --- extraer entradas formateadas: ID|pkg:version|SEVERITY ---
jq -r '[ .matches[]? as $m |
($m.vulnerability.id // "-") as $id |
( if ($m.artifact | type) == "object"
then (($m.artifact.name // $m.artifact.id // "-") + ":" + ($m.artifact.version // "-"))
else (($m.artifact // "-") + ":" + "-")
end) as $pv |
(($id + "|" + $pv + "|" + (($m.vulnerability.severity // "") | ascii_upcase)))
] | .[]' "$A_GRYPE" | sort -u > /tmp/a_entries.txt || true

jq -r '[ .matches[]? as $m |
($m.vulnerability.id // "-") as $id |
( if ($m.artifact | type) == "object"
then (($m.artifact.name // $m.artifact.id // "-") + ":" + ($m.artifact.version // "-"))
else (($m.artifact // "-") + ":" + "-")
end) as $pv |
(($id + "|" + $pv + "|" + (($m.vulnerability.severity // "") | ascii_upcase)))
] | .[]' "$B_GRYPE" | sort -u > /tmp/b_entries.txt || true

# union de entradas (unique)
cat /tmp/a_entries.txt /tmp/b_entries.txt | sort -u > /tmp/all_entries.txt || true

# --- crear archivo ordenado por severidad desc (rank) y luego por ID ---
awk -F'|' '
BEGIN {
map["CRITICAL"]=5; map["HIGH"]=4; map["MEDIUM"]=3; map["LOW"]=2; map["UNKNOWN"]=1;
}
{
id=$1; pv=$2; sev=toupper($3);
rank = (sev in map ? map[sev] : 0);
# output: rank|sev|id|pv
printf("%d|%s|%s|%s\n", rank, sev, id, pv);
}
' /tmp/all_entries.txt | sort -t'|' -k1,1nr -k3,3 | cut -d'|' -f2- > /tmp/all_sorted.txt || true

# --- START MD FILE ---
echo "# Vulnerability comparison: ${A_BRANCH} **vs** ${B_BRANCH}" > "${OUT}"
echo "" >> "${OUT}"

# --- TABLE requested: Severity | VulnerabilityID | package:version | branches ---
echo "| Severity | VulnerabilityID | package:version | branches |" >> "${OUT}"
echo "|---|---|---|---|" >> "${OUT}"

if [ -s /tmp/all_sorted.txt ]; then
while IFS= read -r line; do
# line format: SEV|ID|PKG:VER
sev=$(echo "$line" | awk -F'|' '{print $1}')
id=$(echo "$line" | awk -F'|' '{print $2}')
pv=$(echo "$line" | awk -F'|' '{print $3}')

inA=0; inB=0
# membership checks use original a_entries/b_entries (ID|pkg|sev)
entry="${id}|${pv}|${sev}"
if grep -Fxq "$entry" /tmp/a_entries.txt 2>/dev/null; then inA=1; fi
if grep -Fxq "$entry" /tmp/b_entries.txt 2>/dev/null; then inB=1; fi

if [ "$inA" -eq 1 ] && [ "$inB" -eq 1 ]; then
branches="**BOTH**"
elif [ "$inA" -eq 1 ]; then
branches="${A_BRANCH}"
else
branches="${B_BRANCH}"
fi

echo "| ${sev} | ${id} | ${pv} | ${branches} |" >> "${OUT}"
done < /tmp/all_sorted.txt
else
echo "| - | - | - | - |" >> "${OUT}"
echo "" >> "${OUT}"
fi

echo "" >> "${OUT}"
# --- Totals y resto del MD (se mantienen para contexto) ---
totalA=$(jq -r '[ .matches[]?.vulnerability?.id ] | unique | length' "${A_GRYPE}" 2>/dev/null || echo 0)
totalB=$(jq -r '[ .matches[]?.vulnerability?.id ] | unique | length' "${B_GRYPE}" 2>/dev/null || echo 0)
echo "- **Total unique vulnerability IDs**: ${totalA} (${A_BRANCH}) | ${totalB} (${B_BRANCH})" >> "${OUT}"
echo "" >> "${OUT}"

# tabla de severidad (como antes)
echo "| Severity | ${A_BRANCH} | ${B_BRANCH} |" >> "${OUT}"
echo "|---:|---:|---:|" >> "${OUT}"
for sev in CRITICAL HIGH MEDIUM LOW UNKNOWN; do
ca=$(jq --arg s "$sev" '[ .matches[]?.vulnerability? | select((.severity // "") | ascii_upcase == $s) | .id ] | unique | length' "${A_GRYPE}" 2>/dev/null || echo 0)
cb=$(jq --arg s "$sev" '[ .matches[]?.vulnerability? | select((.severity // "") | ascii_upcase == $s) | .id ] | unique | length' "${B_GRYPE}" 2>/dev/null || echo 0)
echo "| $sev | $ca | $cb |" >> "${OUT}"
done

echo "" >> "${OUT}"
echo "----" >> "${OUT}"
echo "Artifacts included:" >> "${OUT}"
echo "- ${REPORT_DIR}/branchA-sbom.cdx.json" >> "${OUT}"
echo "- ${REPORT_DIR}/branchB-sbom.cdx.json" >> "${OUT}"
echo "- ${REPORT_DIR}/branchA-grype.json" >> "${OUT}"
echo "- ${REPORT_DIR}/branchB-grype.json" >> "${OUT}"
echo "- ${REPORT_DIR}/comparison-report.md (this file)" >> "${OUT}"


- name: Create ZIP of reports
run: |
set -euo pipefail
cd "${REPORT_DIR}"
zip -r comparison-artifacts.zip . || true
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '8'
cache: 'maven'
# 3) Run the action
- name: Vulnerability Diff (Syft+Grype)
uses: sec-open/[email protected]
with:
base_ref: ${{ github.event.inputs.branch_a }} # pass 'develop'
head_ref: ${{ github.event.inputs.branch_b }} # pass 'TASK-7908'
html_logo_url: "https://zettagenomics.com/wp-content/uploads/2022/10/Zetta-reversed-out-full-logo-dark-background.png"

- name: "Publish table to GitHub Actions summary (only the table, sorted)"
- name: Save Puppeteer cache
if: always()
run: |
set -euo pipefail
SUMMARY="$GITHUB_STEP_SUMMARY"
A_BRANCH="${{ github.event.inputs.branch_a }}"
B_BRANCH="${{ github.event.inputs.branch_b }}"

# Si no hay sorted file, intenta generarlo a partir de /tmp/all_entries.txt
if [ ! -s /tmp/all_sorted.txt ] && [ -s /tmp/all_entries.txt ]; then
awk -F'|' '
BEGIN { map["CRITICAL"]=5; map["HIGH"]=4; map["MEDIUM"]=3; map["LOW"]=2; map["UNKNOWN"]=1; }
{
id=$1; pv=$2; sev=toupper($3);
rank = (sev in map ? map[sev] : 0);
printf("%d|%s|%s|%s\n", rank, sev, id, pv);
}
' /tmp/all_entries.txt | sort -t'|' -k1,1nr -k3,3 | cut -d'|' -f2- > /tmp/all_sorted.txt || true
fi

# Header table for summary
echo "| Severity | VulnerabilityID | package:version | branches |" >> "$SUMMARY"
echo "|---|---|---|---|" >> "$SUMMARY"

if [ -s /tmp/all_sorted.txt ]; then
while IFS= read -r line; do
sev=$(echo "$line" | awk -F'|' '{print $1}')
id=$(echo "$line" | awk -F'|' '{print $2}')
pv=$(echo "$line" | awk -F'|' '{print $3}')

inA=0; inB=0
entry="${id}|${pv}|${sev}"
if grep -Fxq "$entry" /tmp/a_entries.txt 2>/dev/null; then inA=1; fi
if grep -Fxq "$entry" /tmp/b_entries.txt 2>/dev/null; then inB=1; fi
uses: actions/cache@v4
with:
path: ~/.cache/puppeteer
key: puppeteer-${{ runner.os }}

if [ "$inA" -eq 1 ] && [ "$inB" -eq 1 ]; then
branches="**BOTH**"
elif [ "$inA" -eq 1 ]; then
branches="${A_BRANCH}"
else
branches="${B_BRANCH}"
fi

echo "| ${sev} | ${id} | ${pv} | ${branches} |" >> "$SUMMARY"
done < /tmp/all_sorted.txt
else
echo "| - | - | - | - |" >> "$SUMMARY"
fi
# build_command: ""
# write_summary: "true"
# upload_artifact: "true"
# artifact_name: "vulnerability-diff-${{ github.event.inputs.branch_a }}-vs-${{ github.event.inputs.branch_b }}"
# report_html: "true"
# report_pdf: "true"
# min_severity: "LOW"
# title_logo_url: "https://zettagenomics.com/wp-content/uploads/2022/10/Zetta-reversed-out-full-logo-dark-background.png"

- name: Upload artifacts (reports)
uses: actions/upload-artifact@v4
with:
name: vuln-comparison-${{ github.run_id }}-${{ github.event.inputs.branch_a }}-vs-${{ github.event.inputs.branch_b }}-$(date +%s)
path: ${{ env.REPORT_DIR }}
Loading
Loading