Skip to content
Merged
Changes from all commits
Commits
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
280 changes: 279 additions & 1 deletion .github/workflows/dependency-cve-monitor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
env:
SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK_URL }}
GH_TOKEN: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }}
JQ_FILTER_B64: "Wy5bXSB8IHNlbGVjdCguY3JlYXRlZF9hdCA+ICRjdXRvZmYgYW5kIC5zZWN1cml0eV92dWxuZXJhYmlsaXR5LmZpcnN0X3BhdGNoZWRfdmVyc2lvbiAhPSBudWxsKV0KfCBncm91cF9ieSguc2VjdXJpdHlfdnVsbmVyYWJpbGl0eS5wYWNrYWdlLm5hbWUpCnwgbWFwKHsKICAgIHBrZzogLlswXS5zZWN1cml0eV92dWxuZXJhYmlsaXR5LnBhY2thZ2UubmFtZSwKICAgIGZpeDogLlswXS5zZWN1cml0eV92dWxuZXJhYmlsaXR5LmZpcnN0X3BhdGNoZWRfdmVyc2lvbi5pZGVudGlmaWVyLAogICAgbWFuaWZlc3RzOiAoWy5bXS5kZXBlbmRlbmN5Lm1hbmlmZXN0X3BhdGhdIHwgdW5pcXVlIHwgbWFwKGdzdWIoIi9wb20ueG1sJCI7ICIiKSB8IGdzdWIoIi4qLyI7ICIiKSkpLAogICAgY3ZlczogKGdyb3VwX2J5KC5zZWN1cml0eV9hZHZpc29yeS5jdmVfaWQgLy8gLnNlY3VyaXR5X2Fkdmlzb3J5Lmdoc2FfaWQpIHwgbWFwKHsKICAgICAgY3ZlOiAoLlswXS5zZWN1cml0eV9hZHZpc29yeS5jdmVfaWQgLy8gLlswXS5zZWN1cml0eV9hZHZpc29yeS5naHNhX2lkKSwKICAgICAgZ2hzYTogLlswXS5zZWN1cml0eV9hZHZpc29yeS5naHNhX2lkLAogICAgICBzZXZlcml0eTogKC5bMF0uc2VjdXJpdHlfYWR2aXNvcnkuc2V2ZXJpdHkgfCBhc2NpaV91cGNhc2UpCiAgICB9KSkKICB9KQp8IGlmIGxlbmd0aCA9PSAwIHRoZW4gZW1wdHkKICBlbHNlCiAgICAiOndhcm5pbmc6ICpcKG1hcCguY3ZlcyB8IGxlbmd0aCkgfCBhZGQpIG5ldyBDVkUocykgZGV0ZWN0ZWQgaW4gU0RLIGRlcGVuZGVuY2llcyB3aXRoIGZpeGVzIGF2YWlsYWJsZSpcblxuIiArCiAgICAobWFwKAogICAgICAiYFwoLnBrZylgIChcKC5tYW5pZmVzdHMgfCBqb2luKCIsICIpKSkgXHUyMTkyIFVwZ3JhZGUgdG8gYFwoLmZpeClgXG4iICsKICAgICAgKC5jdmVzIHwgbWFwKCIgIFx1MjAyMiA8aHR0cHM6Ly9naXRodWIuY29tL2Fkdmlzb3JpZXMvXCguZ2hzYSl8XCguY3ZlKT4gKFwoLnNldmVyaXR5KSkiKSB8IGpvaW4oIlxuIikpCiAgICApIHwgam9pbigiXG5cbiIpKSArCiAgICAiXG5cbipBY3Rpb24gbmVlZGVkKjogVXBncmFkZSB0aGUgYWZmZWN0ZWQgZGVwZW5kZW5jeSB2ZXJzaW9ucyB0byB0aGUgZml4IHZlcnNpb25zIGxpc3RlZCBhYm92ZS4iCiAgZW5kCg=="
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)
Expand Down Expand Up @@ -66,3 +66,281 @@ jobs:

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
Loading