Skip to content

dependency-cve-monitor #100

dependency-cve-monitor

dependency-cve-monitor #100

name: "dependency-cve-monitor"
on:
schedule:
- cron: '0 9 * * *'
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
submit-dependencies:
if: github.repository == 'aws/aws-sdk-java-v2'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: advanced-security/maven-dependency-submission-action@b275d12641ac2d2108b2cbb7598b154ad2f2cee8 # v5
with:
maven-args: >-
-DtransitiveExcludes=*:*
-DclasspathScope=runtime
-pl !build-tools,!release-scripts,!archetypes,!test/test-utils,!test/sdk-benchmarks,!test/http-client-tests,!test/http-client-benchmarks,!test/s3-benchmarks,!test/protocol-tests-core,!test/ruleset-testing-core,!test/protocol-tests,!test/service-test-utils,!test/codegen-generated-classes-test,!test/sdk-standard-benchmarks,!test/module-path-tests,!test/tests-coverage-reporting,!test/stability-tests,!test/sdk-native-image-test,!test/auth-tests,!test/service-client-backward-compat-test,!test/bundle-logging-bridge-binding-test,!test/v2-migration-tests,!test/bundle-shading-tests,!test/crt-unavailable-tests,!test/architecture-tests,!test/s3-tests
notify-alerts:
if: github.repository == 'aws/aws-sdk-java-v2'
needs: submit-dependencies
runs-on: ubuntu-latest
timeout-minutes: 5
permissions: {}
steps:
- name: Wait for alert processing
run: sleep 60
- name: Check and notify actionable alerts
env:
SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK_URL }}
GH_TOKEN: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }}
JQ_FILTER_B64: "Wy5bXSB8IHNlbGVjdCguY3JlYXRlZF9hdCA+ICRjdXRvZmYgYW5kIC5zZWN1cml0eV92dWxuZXJhYmlsaXR5LmZpcnN0X3BhdGNoZWRfdmVyc2lvbiAhPSBudWxsKV0KfCBncm91cF9ieSguc2VjdXJpdHlfdnVsbmVyYWJpbGl0eS5wYWNrYWdlLm5hbWUpCnwgbWFwKHsKICAgIHBrZzogLlswXS5zZWN1cml0eV92dWxuZXJhYmlsaXR5LnBhY2thZ2UubmFtZSwKICAgIGZpeDogLlswXS5zZWN1cml0eV92dWxuZXJhYmlsaXR5LmZpcnN0X3BhdGNoZWRfdmVyc2lvbi5pZGVudGlmaWVyLAogICAgbWFuaWZlc3RzOiAoWy5bXS5kZXBlbmRlbmN5Lm1hbmlmZXN0X3BhdGhdIHwgdW5pcXVlIHwgbWFwKGdzdWIoIi9wb20ueG1sJCI7ICIiKSB8IGdzdWIoIi4qLyI7ICIiKSkpLAogICAgY3ZlczogKGdyb3VwX2J5KC5zZWN1cml0eV9hZHZpc29yeS5jdmVfaWQgLy8gLnNlY3VyaXR5X2Fkdmlzb3J5Lmdoc2FfaWQpIHwgbWFwKHsKICAgICAgY3ZlOiAoLlswXS5zZWN1cml0eV9hZHZpc29yeS5jdmVfaWQgLy8gLlswXS5zZWN1cml0eV9hZHZpc29yeS5naHNhX2lkKSwKICAgICAgZ2hzYTogLlswXS5zZWN1cml0eV9hZHZpc29yeS5naHNhX2lkLAogICAgICBzZXZlcml0eTogKC5bMF0uc2VjdXJpdHlfYWR2aXNvcnkuc2V2ZXJpdHkgfCBhc2NpaV91cGNhc2UpLAogICAgICBzdW1tYXJ5OiAoLlswXS5zZWN1cml0eV9hZHZpc29yeS5zdW1tYXJ5IHwgc3BsaXQoIi4gIikgfCBmaXJzdCB8IGlmIGxlbmd0aCA+IDE1MCB0aGVuIC5bOjE0N10gKyAiLi4uIiBlbHNlIC4gZW5kKQogICAgfSkpCiAgfSkKfCBpZiBsZW5ndGggPT0gMCB0aGVuIGVtcHR5CiAgZWxzZQogICAgIjp3YXJuaW5nOiAqXChtYXAoLmN2ZXMgfCBsZW5ndGgpIHwgYWRkKSBDVkUocykgZGV0ZWN0ZWQgdmlhIDxodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1zZGstamF2YS12Mi9zZWN1cml0eS9kZXBlbmRhYm90fERlcGVuZGFib3QgYWxlcnRzPipcblxuIiArCiAgICAobWFwKAogICAgICAiYFwoLnBrZylgIChcKC5tYW5pZmVzdHMgfCBqb2luKCIsICIpKSkgXHUyMTkyIFVwZ3JhZGUgdG8gYFwoLmZpeClgXG4iICsKICAgICAgKC5jdmVzIHwgbWFwKCIgIFx1MjAyMiA8aHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvXCguZ2hzYSl8XCguY3ZlKT4gKFwoLnNldmVyaXR5KSlcbiAgICBcKC5zdW1tYXJ5KSIpIHwgam9pbigiXG4iKSkKICAgICkgfCBqb2luKCJcblxuIikpICsKICAgICJcblxuKkFjdGlvbiBuZWVkZWQqOiBVcGdyYWRlIHRoZSBhZmZlY3RlZCBkZXBlbmRlbmN5IHZlcnNpb25zIHRvIHRoZSBmaXggdmVyc2lvbnMgbGlzdGVkIGFib3ZlLiIKICBlbmQK"
shell: bash
run: |
ONE_DAY_AGO=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-24H +%Y-%m-%dT%H:%M:%SZ)
ALERTS=$(gh api repos/${{ github.repository }}/dependabot/alerts?state=open\&per_page=100)
if [ $? -ne 0 ]; then
echo "Failed to fetch alerts"
exit 1
fi
echo "$JQ_FILTER_B64" | base64 -d > /tmp/filter.jq
MESSAGE=$(echo "$ALERTS" | jq -r --arg cutoff "$ONE_DAY_AGO" -f /tmp/filter.jq)
if [ -z "$MESSAGE" ]; then
echo "No new actionable alerts in the last 24 hours."
exit 0
fi
echo "Sending Slack notification..."
echo "$MESSAGE"
PAYLOAD=$(jq -n --arg text "$MESSAGE" '{text: $text}')
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD"
echo ""
echo "Slack notification sent."
check-upstream-advisories:
if: github.repository == 'aws/aws-sdk-java-v2'
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: read
steps:
- uses: actions/checkout@v6
with:
sparse-checkout: pom.xml
sparse-checkout-cone-mode: false
- name: Check for new CVEs in upstream dependencies
env:
GH_TOKEN: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK_URL }}
shell: bash
run: |
set -euo pipefail
ALERTS=""
NOW=$(date -u +%s)
# Check for CVEs published in the last 24 hours
ONE_DAY_AGO=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-24H +%Y-%m-%dT%H:%M:%SZ)
# ============================================================
# SDK versions from pom.xml
# ============================================================
NETTY_VERSION=$(grep -oP '(?<=<netty.version>)[^<]+' pom.xml | sed 's/.Final//')
NETTY_PATCH=$(echo "$NETTY_VERSION" | grep -oP '\d+$')
NETTY_MAJOR_MINOR=$(echo "$NETTY_VERSION" | grep -oP '^\d+\.\d+')
JACKSON_VERSION=$(grep -oP '(?<=<jackson.version>)[^<]+' pom.xml)
JACKSON_DATABIND_VERSION=$(grep -oP '(?<=<jackson.databind.version>)[^<]+' pom.xml)
HTTPCLIENT5_VERSION=$(grep -oP '(?<=<httpcomponents.client5.version>)[^<]+' pom.xml)
HTTPCORE5_VERSION=$(grep -oP '(?<=<httpcomponents.core5.version>)[^<]+' pom.xml)
echo "SDK versions:"
echo " Netty: $NETTY_VERSION"
echo " Jackson: $JACKSON_VERSION (databind: $JACKSON_DATABIND_VERSION)"
echo " httpclient5: $HTTPCLIENT5_VERSION"
echo " httpcore5: $HTTPCORE5_VERSION"
# ============================================================
# NETTY: Check repo-level security advisories
# ============================================================
echo ""
echo "=== Checking Netty repo advisories ==="
SDK_NETTY_PACKAGES=(
"io.netty:netty-codec-http"
"io.netty:netty-codec-http2"
"io.netty:netty-codec"
"io.netty:netty-transport"
"io.netty:netty-transport-native-epoll"
"io.netty:netty-transport-classes-epoll"
"io.netty:netty-common"
"io.netty:netty-buffer"
"io.netty:netty-handler"
"io.netty:netty-resolver"
"io.netty:netty-resolver-dns"
)
NETTY_ADVISORIES=$(gh api "repos/netty/netty/security-advisories?sort=published&direction=desc&per_page=30")
NETTY_NEW=$(echo "$NETTY_ADVISORIES" | jq --arg cutoff "$ONE_DAY_AGO" --arg major_minor "$NETTY_MAJOR_MINOR" --arg sdk_patch "$NETTY_PATCH" \
--argjson sdk_packages "$(printf '%s\n' "${SDK_NETTY_PACKAGES[@]}" | jq -R . | jq -s .)" \
'[.[] |
select(.published_at > $cutoff) |
select([.vulnerabilities[].package.name] | any(. as $pkg | $sdk_packages | index($pkg))) |
select(.vulnerabilities[] |
select(.package.name as $pkg | $sdk_packages | index($pkg)) |
(.patched_versions | capture("(?<v>" + $major_minor + "\\.(?<p>[0-9]+))") | (.p | tonumber) > ($sdk_patch | tonumber))
) |
{ghsa_id, severity, summary,
patched: ([.vulnerabilities[] | select(.package.name as $pkg | $sdk_packages | index($pkg)) | .patched_versions | capture("(?<v>" + $major_minor + "\\.(?<p>[0-9]+))").v] | max),
packages: [.vulnerabilities[] | select(.package.name as $pkg | $sdk_packages | index($pkg)) | .package.name] | unique}
]')
NETTY_COUNT=$(echo "$NETTY_NEW" | jq 'length')
echo "Found $NETTY_COUNT new Netty advisories affecting SDK (published in last 24h)"
if [ "$NETTY_COUNT" -gt 0 ]; then
NETTY_MSG=$(echo "$NETTY_NEW" | jq -r '
(map(.patched) | max) as $max_patched |
"⚠️ *" + (length | tostring) + " Netty CVE(s) detected via <https://github.com/netty/netty/security/advisories|upstream repo advisory>*\n\n" +
(map(" • <https://github.com/netty/netty/security/advisories/\(.ghsa_id)|\(.ghsa_id)> (\(.severity | ascii_upcase)) — \(.packages | join(", "))\n \(.summary | split(". ") | first | if length > 150 then .[:147] + "..." else . end)") | join("\n")) +
"\n\n*Action needed*: Upgrade Netty from `'"$NETTY_VERSION"'.Final` → `" + $max_patched + ".Final`"
')
ALERTS="${ALERTS}${NETTY_MSG}\n\n"
fi
# ============================================================
# JACKSON: Check repo-level security advisories
# ============================================================
echo ""
echo "=== Checking Jackson repo advisories ==="
JACKSON_ALERTS=""
for REPO in "FasterXML/jackson-databind" "FasterXML/jackson-core" "FasterXML/jackson-dataformats-binary"; do
PKG_NAME=$(echo "$REPO" | sed 's|FasterXML/||')
if [ "$PKG_NAME" = "jackson-databind" ]; then
SDK_VER="$JACKSON_DATABIND_VERSION"
else
SDK_VER="$JACKSON_VERSION"
fi
SDK_MINOR=$(echo "$SDK_VER" | grep -oP '^\d+\.\d+')
SDK_PATCH=$(echo "$SDK_VER" | grep -oP '\d+$')
ADVISORIES=$(gh api "repos/$REPO/security-advisories?sort=published&direction=desc&per_page=10")
NEW_ADVISORIES=$(echo "$ADVISORIES" | jq --arg cutoff "$ONE_DAY_AGO" --arg sdk_minor "$SDK_MINOR" --arg sdk_patch "$SDK_PATCH" --arg sdk_ver "$SDK_VER" \
'[.[] |
select(.published_at > $cutoff) |
# Find vulnerabilities that match our version line AND where SDK is in the affected range
select([.vulnerabilities[] |
select(.package.name | startswith("com.fasterxml.jackson")) |
select(.patched_versions != null and .patched_versions != "") |
select(.vulnerable_version_range | test($sdk_minor)) |
select(
(.patched_versions // "") |
if . == "" then
false
else
capture("(?<v>" + $sdk_minor + "\\.(?<p>[0-9]+))") |
(.p | tonumber) > ($sdk_patch | tonumber)
end
)
] | length > 0) |
{
ghsa_id,
severity,
summary,
patched: ([.vulnerabilities[] |
select(.package.name | startswith("com.fasterxml.jackson")) |
select(.patched_versions != null and .patched_versions != "") |
select(.vulnerable_version_range | test($sdk_minor)) |
.patched_versions // "" |
capture("(?<v>" + $sdk_minor + "\\.(?<p>[0-9]+))").v // null
] | map(select(. != null)) | max // "unknown")
}
]')
COUNT=$(echo "$NEW_ADVISORIES" | jq 'length')
echo " $REPO: $COUNT new advisories (last 24h, affecting SDK)"
if [ "$COUNT" -gt 0 ]; then
MAX_PATCHED=$(echo "$NEW_ADVISORIES" | jq -r '[.[].patched] | map(select(. != "unknown")) | max // "latest"')
JACKSON_MSG=$(echo "$NEW_ADVISORIES" | jq -r --arg repo "$REPO" --arg max_patched "$MAX_PATCHED" '
"⚠️ *" + (length | tostring) + " '"$PKG_NAME"' CVE(s) detected via <https://github.com/" + $repo + "/security/advisories|upstream repo advisory>*\n\n" +
(map(" • <https://github.com/" + $repo + "/security/advisories/\(.ghsa_id)|\(.ghsa_id)> (\(.severity | ascii_upcase)) — \(.summary | split(". ") | first | if length > 150 then .[:147] + "..." else . end)") | join("\n")) +
"\n\n*Action needed*: Upgrade '"$PKG_NAME"' from `'"$SDK_VER"'` → `" + $max_patched + "`"
')
ALERTS="${ALERTS}${JACKSON_MSG}\n\n"
fi
done
# ============================================================
# APACHE: Check NVD for new CVEs, get details from cve.org
# ============================================================
echo ""
echo "=== Checking Apache httpcomponents via NVD ==="
NVD_RESULTS=$(curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=httpcomponents&pubStartDate=${ONE_DAY_AGO}&pubEndDate=$(date -u +%Y-%m-%dT%H:%M:%S.000)&resultsPerPage=10")
APACHE_CVES=$(echo "$NVD_RESULTS" | jq -r '.vulnerabilities[]?.cve.id // empty')
if [ -z "$APACHE_CVES" ]; then
echo " No new Apache httpcomponents CVEs in last 24h"
else
APACHE_ALERTS=""
APACHE_ACTION=""
while IFS= read -r CVE_ID; do
echo " Checking $CVE_ID..."
# Get structured data from cve.org
CVE_DATA=$(curl -s "https://cveawg.mitre.org/api/cve/$CVE_ID")
# Extract affected package and version
PACKAGE=$(echo "$CVE_DATA" | jq -r '.containers.cna.affected[0].packageName // .containers.cna.affected[0].product // "unknown"')
# Check if this affects SDK's versions
AFFECTS_SDK=false
FIX_VERSION=""
DESCRIPTION_FULL=$(echo "$CVE_DATA" | jq -r '.containers.cna.descriptions[0].value // ""')
DESCRIPTION=$(echo "$DESCRIPTION_FULL" | tr '\n' ' ' | sed 's/ */ /g' | sed 's/\. /.\n/' | head -1)
if [ ${#DESCRIPTION} -gt 150 ]; then
DESCRIPTION="${DESCRIPTION:0:147}..."
fi
# Try to extract fix version from description
FIX_FROM_DESC=$(echo "$DESCRIPTION_FULL" | grep -oP 'upgrade to (?:at least )?version \K[0-9.]+' || echo "")
# Determine SDK current version based on package
SDK_CURRENT=""
if echo "$PACKAGE" | grep -q "httpclient5"; then
SDK_CURRENT="$HTTPCLIENT5_VERSION"
elif echo "$PACKAGE" | grep -q "httpcore5"; then
SDK_CURRENT="$HTTPCORE5_VERSION"
else
continue
fi
# Check ALL version entries — SDK is affected if its version <= lessThanOrEqual or < lessThan
VERSIONS_COUNT=$(echo "$CVE_DATA" | jq '.containers.cna.affected[0].versions | length')
SDK_MAJOR_MINOR_APACHE=$(echo "$SDK_CURRENT" | sed 's/\.[0-9]*$//')
for i in $(seq 0 $((VERSIONS_COUNT - 1))); do
LESS_THAN=$(echo "$CVE_DATA" | jq -r ".containers.cna.affected[0].versions[$i].lessThan // \"\"")
LESS_THAN_EQ=$(echo "$CVE_DATA" | jq -r ".containers.cna.affected[0].versions[$i].lessThanOrEqual // \"\"")
VER_BOUND="${LESS_THAN_EQ:-$LESS_THAN}"
# Skip entries for different version lines (alpha/beta entries that don't match our major.minor)
if echo "$VER_BOUND" | grep -q "alpha\|beta"; then
continue
fi
# Skip if the bound doesn't start with our major.minor
if ! echo "$VER_BOUND" | grep -q "^${SDK_MAJOR_MINOR_APACHE}\."; then
continue
fi
if [ -n "$LESS_THAN" ]; then
# Affected if SDK_CURRENT < LESS_THAN
if [ "$(printf '%s\n' "$SDK_CURRENT" "$LESS_THAN" | sort -V | head -1)" = "$SDK_CURRENT" ] && [ "$SDK_CURRENT" != "$LESS_THAN" ]; then
AFFECTS_SDK=true
FIX_VERSION="${FIX_FROM_DESC:-$LESS_THAN}"
break
fi
elif [ -n "$LESS_THAN_EQ" ]; then
# Affected if SDK_CURRENT <= LESS_THAN_EQ
if [ "$(printf '%s\n' "$SDK_CURRENT" "$LESS_THAN_EQ" | sort -V | head -1)" = "$SDK_CURRENT" ]; then
AFFECTS_SDK=true
FIX_VERSION="${FIX_FROM_DESC:-"> $LESS_THAN_EQ"}"
break
fi
fi
done
if [ "$AFFECTS_SDK" = true ]; then
APACHE_ALERTS="${APACHE_ALERTS} • <https://nvd.nist.gov/vuln/detail/$CVE_ID|$CVE_ID> — $PACKAGE\n $DESCRIPTION\n"
APACHE_ACTION="*Action needed*: Upgrade \`$PACKAGE\` from \`$SDK_CURRENT\` → \`$FIX_VERSION\`"
fi
done <<< "$APACHE_CVES"
if [ -n "$APACHE_ALERTS" ]; then
APACHE_COUNT=$(echo -e "$APACHE_ALERTS" | grep -c "•")
APACHE_MSG="⚠️ *${APACHE_COUNT} Apache HttpComponents CVE(s) detected via <https://nvd.nist.gov/vuln/search|NVD>*\n\n${APACHE_ALERTS}\n${APACHE_ACTION}"
ALERTS="${ALERTS}${APACHE_MSG}\n\n"
fi
fi
# ============================================================
# Send Slack notification if any alerts found
# ============================================================
echo ""
if [ -z "$ALERTS" ]; then
echo "No new CVEs found across all upstream sources."
exit 0
fi
echo "Sending Slack notification..."
FINAL_MESSAGE=$(echo -e "$ALERTS")
echo "$FINAL_MESSAGE"
if [ -n "$SLACK_WEBHOOK_URL" ]; then
PAYLOAD=$(jq -n --arg text "$FINAL_MESSAGE" '{text: $text}')
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD"
echo ""
echo "Slack notification sent."
else
echo "No SLACK_WEBHOOK_URL configured. Skipping notification."
fi