Compare commits

...

6 Commits

View File

@@ -0,0 +1,282 @@
name: Dependabot Alert to Feishu
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch:
permissions:
contents: read
security-events: read
jobs:
notify-feishu:
runs-on: ubuntu-latest
steps:
- name: Validate webhook secret
env:
FEISHU_WEBHOOK: ${{ secrets.FEISHU_WEBHOOK }}
run: |
set -euo pipefail
if [ -z "${FEISHU_WEBHOOK:-}" ]; then
echo "FEISHU_WEBHOOK secret is not configured."
exit 1
fi
- name: Notify Feishu from event or API polling
env:
FEISHU_WEBHOOK: ${{ secrets.FEISHU_WEBHOOK }}
GITHUB_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
MAX_ITEMS="${MAX_ITEMS:-10}"
SUMMARY_MAX_LEN="${SUMMARY_MAX_LEN:-140}"
build_payload() {
local alerts_json="$1"
local alert_count critical_count high_count header_template
local summary_line summary_line_2 summary_line_3 generated_at
local stats_elements visible_items hidden_count table_rows_data
alert_count="$(echo "${alerts_json}" | jq 'length')"
critical_count="$(echo "${alerts_json}" | jq '[.[] | select((.security_advisory.severity // "") == "critical")] | length')"
high_count="$(echo "${alerts_json}" | jq '[.[] | select((.security_advisory.severity // "") == "high")] | length')"
header_template="orange"
if [ "${critical_count}" -gt 0 ]; then
header_template="red"
fi
summary_line="🚨 嗨,这里是您的 EE&CE 企业级🐮🐴在线打工播报员~"
summary_line_2="当前系统雷达已锁定一批**高风险依赖告警**,建议优先处理,不然它们可能比 KPI 先“爆炸”💥"
summary_line_3=$'📌 已为您智能筛选:\n仅展示 **未分配负责人的 High / Critical 告警**\n也就是说——没人背锅但锅已经烧起来了🔥\n\n请尽快认领处理拯救系统于水火之中 🙏'
generated_at="$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
stats_elements="$(jq -n \
--argjson total "${alert_count}" \
--argjson critical "${critical_count}" \
--argjson high "${high_count}" '
[
{
tag: "column_set",
flex_mode: "trisect",
horizontal_spacing: "small",
columns: [
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**待处理告警**\n" + ($total|tostring)}]
},
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**🔴 严重风险**\n" + ($critical|tostring)}]
},
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**🟠 高风险**\n" + ($high|tostring)}]
}
]
}
]')"
if [ "${alert_count}" -eq 0 ]; then
jq -n \
--arg title "Dependabot Security Alerts" \
--arg subtitle "${REPOSITORY}" \
--arg summary "🚨 嗨,这里是您的 EE&CE 企业级🐮🐴在线打工播报员~" \
--arg summary2 "当前没有待处理的高风险依赖告警,继续保持,今天可以安心下班。" \
--arg summary3 $'📌 已为您智能筛选:\n仅展示 **未分配负责人的 High / Critical 告警**\n当前结果为空系统暂时平稳' \
--arg generatedAt "${generated_at}" '
{
msg_type: "interactive",
card: {
schema: "2.0",
config: {wide_screen_mode: true},
header: {
template: "green",
title: {tag: "plain_text", content: $title},
subtitle: {tag: "plain_text", content: $subtitle}
},
body: {
elements: [
{tag: "markdown", content: $summary},
{tag: "markdown", content: $summary2},
{tag: "markdown", content: $summary3},
{
tag: "column_set",
flex_mode: "trisect",
horizontal_spacing: "small",
columns: [
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**待处理告警**\n0"}]
},
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**🔴 严重风险**\n0"}]
},
{
tag: "column",
width: "weighted",
weight: 1,
padding: "8px",
background_style: "grey",
elements: [{tag: "markdown", content: "**🟠 高风险**\n0"}]
}
]
},
{
tag: "div",
text: {
tag: "plain_text",
content: ("通知时间:" + $generatedAt),
text_color: "grey",
text_align: "right"
}
}
]
}
}
}'
return 0
fi
visible_items="$(echo "${alerts_json}" | jq --argjson max "${MAX_ITEMS}" '.[:$max]')"
hidden_count="$(echo "${alerts_json}" | jq --argjson max "${MAX_ITEMS}" 'if length > $max then length - $max else 0 end')"
table_rows_data="$(echo "${visible_items}" | jq -c \
--argjson maxLen "${SUMMARY_MAX_LEN}" '
map(
. as $a |
($a.number // "unknown") as $number |
($a.security_advisory.severity // "unknown") as $severity |
($a.dependency.package.name // "unknown") as $package |
($a.security_advisory.summary // "N/A") as $summary |
($a.html_url // "") as $url |
(
if ($summary | length) > $maxLen
then ($summary[0:$maxLen] + "...")
else $summary
end
) as $summaryShort |
{
level: (if $severity == "critical" then "🔴 critical" else "🟠 high" end),
alert_id: ("#" + ($number|tostring)),
package: ("`" + $package + "`"),
details: ($summaryShort + "\n[View alert](" + $url + ")")
}
)')"
jq -n \
--arg title "Dependabot Security Alerts" \
--arg subtitle "${REPOSITORY}" \
--arg summary "${summary_line}" \
--arg summary2 "${summary_line_2}" \
--arg summary3 "${summary_line_3}" \
--arg generatedAt "${generated_at}" \
--arg headerTemplate "${header_template}" \
--argjson stats "${stats_elements}" \
--argjson tableRows "${table_rows_data}" \
--argjson hidden "${hidden_count}" '
{
msg_type: "interactive",
card: {
schema: "2.0",
config: {wide_screen_mode: true},
header: {
template: $headerTemplate,
title: {tag: "plain_text", content: $title},
subtitle: {tag: "plain_text", content: $subtitle}
},
body: {
elements:
(
[
{tag: "markdown", content: $summary},
{tag: "markdown", content: $summary2},
{tag: "markdown", content: $summary3},
{tag: "hr"},
$stats[0],
{tag: "hr"},
{
tag: "table",
page_size: 10,
row_height: "auto",
header_style: {
text_align: "left",
text_size: "normal",
text_color: "grey",
bold: true,
lines: 1
},
columns: [
{name: "level", display_name: "Level", data_type: "text", width: "120px"},
{name: "alert_id", display_name: "ID", data_type: "text", width: "90px"},
{name: "package", display_name: "Package", data_type: "lark_md", width: "140px"},
{name: "details", display_name: "Summary / Link", data_type: "lark_md", width: "auto"}
],
rows: $tableRows
}
]
+ (
if $hidden > 0
then [
{tag: "hr"},
{tag: "markdown", content: ("还有 " + ($hidden|tostring) + " 条告警未展示,请点击仓库安全页查看全部。")}
]
else []
end
)
+ [
{
tag: "div",
text: {
tag: "plain_text",
content: ("通知时间:" + $generatedAt),
text_color: "grey",
text_align: "right"
}
}
]
)
}
}
}'
}
api_url="https://api.github.com/repos/${REPOSITORY}/dependabot/alerts?state=open&severity=high,critical&assignee=none&per_page=100"
alerts_json="$(curl -sS -f -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "X-GitHub-Api-Version: 2026-03-10" \
"$api_url")"
filtered_json="$(echo "${alerts_json}" | jq '[.[] | select(((.security_advisory.severity // "") == "high" or (.security_advisory.severity // "") == "critical") and ((.assignees | length) == 0))]')"
alert_count="$(echo "${filtered_json}" | jq 'length')"
echo "Filtered dependabot alerts count: ${alert_count}"
payload="$(build_payload "${filtered_json}")"
curl -sS -f -X POST "${FEISHU_WEBHOOK}" \
-H "Content-Type: application/json" \
-d "${payload}"