วันก่อนเขียนสคริปต์ตรวจสอบ Debian oval อัตโนมัติเขียนเสร็จ อ่ะ ไหนๆ สมัครโปรของ เจ๊มินิ (gemini.google.com) แล้ว ให้เจ๊ช่วยขัดเกลาสคริปต์ bash script บ้านๆ เสร็จปุบ ดูหรูไฮ… ทันตาเห็น จากสคริปต์ พบว่าเขียน bash ยังไงให้ถูก ? โวะเพิ่งรู้ว่ามีนั่นนี่โน่น…
- สร้างสคริปต์ gem-oval-check.sh มีข้อความว่า
gem-oval-check.sh
#!/usr/bin/env bash
# OVAL Updater & Parser - Final Fixed Version
# Description: Downloads Debian OVAL definitions, checks for CVEs, generates links, and sends an email report.
# -------------------------
# 1. Configuration & Variables
# -------------------------
readonly DEBIAN_CODENAME=$(lsb_release -c | awk '{print $2}')
readonly DEBIAN_RELEASE=$(cat /etc/debian_version)
readonly SERVER_HOSTNAME=$(hostname -f)
readonly SERVER_IP=$(hostname -I | awk '{print $1}')
readonly URL="https://www.debian.org/security/oval/oval-definitions-${DEBIAN_CODENAME}.xml.bz2"
readonly DEST_BZ2="oval-definitions-${DEBIAN_CODENAME}.xml.bz2"
readonly DEST_XML="oval-definitions-${DEBIAN_CODENAME}.xml"
readonly TMP_BZ2="/tmp/${DEST_BZ2}"
readonly RESULT_XML="oval_result.xml"
readonly ID_FILE="/tmp/oval_ids_${DEBIAN_CODENAME}"
readonly MSG_FILE="/tmp/oval_messages_${DEBIAN_CODENAME}"
readonly CVE_REPORT="/tmp/oval_cve_titles_parsed_${DEBIAN_CODENAME}"
readonly REPORT_TO="***************YOUR E-MAIL ADDRESS******************"
SUBJECT="OVAL Report ${SERVER_HOSTNAME} $(date +%F)"
# -------------------------
# 2. Functions
# -------------------------
# Function for clean up temporary files
cleanup() {
rm -f "${ID_FILE}" "${MSG_FILE}" "${CVE_REPORT}"
}
trap cleanup EXIT
# Function to generate URL based on the security ID (CVE or DSA)
generate_url() {
local id_string=$1
if [[ "$id_string" =~ ^CVE-[0-9]{4}-[0-9]+$ ]]; then
echo "https://cve.mitre.org/cgi-bin/cvename.cgi?name=${id_string}"
elif [[ "$id_string" =~ ^DSA-[0-9]+-[0-9]+$ ]]; then
echo "https://www.debian.org/security/${id_string}"
else
echo "(No Link Available)"
fi
}
# Function to generate and send email
send_report() {
local exit_status=$1
local message_type=$2 # 'SUCCESS', 'INFO', 'ERROR', 'CVE_FOUND'
# กำหนด SUBJECT ด้วยข้อความธรรมดาแทน Emoji/Non-ASCII
local status_prefix=""
case "${message_type}" in
"SUCCESS")
status_prefix="[OVAL OK] Not Found"
;;
"INFO")
status_prefix="[OVAL INFO] Not Found (Old File Check)"
;;
"ERROR")
status_prefix="[OVAL ERROR]"
;;
"CVE_FOUND")
status_prefix="[OVAL ALERT] CVE Found!"
;;
esac
local final_subject="${status_prefix} - OVAL Report ${SERVER_HOSTNAME} $(date +%F)"
# FIX: เพิ่ม IP Address ในส่วนหัวของรายงาน
{
echo "Host Name: ${SERVER_HOSTNAME}"
echo "IP Address: ${SERVER_IP}"
} > "${MSG_FILE}"
case "${message_type}" in
"SUCCESS")
echo "**********************" >> "${MSG_FILE}"
echo "* Congratulations! *" >> "${MSG_FILE}"
echo "**********************" >> "${MSG_FILE}"
echo "Distribution: ${DEBIAN_CODENAME} Release: ${DEBIAN_RELEASE}" >> "${MSG_FILE}"
echo "No CVE found" >> "${MSG_FILE}"
echo "No definitions with result=true found" >> "${MSG_FILE}"
;;
"INFO")
echo "**********************" >> "${MSG_FILE}"
echo "* OVAL Check Ran *" >> "${MSG_FILE}"
echo "**********************" >> "${MSG_FILE}"
echo "File not modified (HTTP 304). Check performed on local file." >> "${MSG_FILE}"
echo "Distribution: ${DEBIAN_CODENAME} Release: ${DEBIAN_RELEASE}" >> "${MSG_FILE}"
echo "No CVE found" >> "${MSG_FILE}"
;;
"ERROR")
echo "**********************" >> "${MSG_FILE}"
echo "* ERROR! *" >> "${MSG_FILE}"
echo "**********************" >> "${MSG_FILE}"
echo "$3" >> "${MSG_FILE}"
;;
"CVE_FOUND")
echo "**********************" >> "${MSG_FILE}"
echo "* CVE Found!!! *" >> "${MSG_FILE}"
echo "**********************" >> "${MSG_FILE}"
echo "Distribution: ${DEBIAN_CODENAME} Release: ${DEBIAN_RELEASE}" >> "${MSG_FILE}"
echo "--------------------------------------------------------" >> "${MSG_FILE}"
echo "Package | Security ID | URL" >> "${MSG_FILE}"
echo "--------------------------------------------------------" >> "${MSG_FILE}"
cat "${CVE_REPORT}" >> "${MSG_FILE}"
;;
esac
if ! mail -s "${final_subject}" "${REPORT_TO}" < "${MSG_FILE}"; then
echo "🚨 ERROR: Failed to send email report to ${REPORT_TO}" >&2
fi
echo "Report sent to ${REPORT_TO}. Exiting with status ${exit_status}."
exit "${exit_status}"
}
# -------------------------
# 3. Main Logic
# -------------------------
echo "🚀 Starting OVAL check for Debian ${DEBIAN_CODENAME} (${DEBIAN_RELEASE}) on ${SERVER_HOSTNAME} (${SERVER_IP})..."
STATUS=$(curl -s -f -w "%{http_code}" -z "${TMP_BZ2}" -o "${TMP_BZ2}" "${URL}" || echo "999")
case "${STATUS}" in
200)
echo "✅ New/updated file downloaded (HTTP 200). Processing..."
rm -f "${DEST_XML}" "${RESULT_XML}"
cp "${TMP_BZ2}" "${DEST_BZ2}"
;&
304)
if [ "$STATUS" -eq 304 ]; then
echo "ℹ️ OVAL file not modified (HTTP 304). Running check on existing file..."
fi
if [ ! -f "${DEST_XML}" ] && [ ! -f "${DEST_BZ2}" ]; then
if [ -f "${TMP_BZ2}" ]; then
echo "⚠️ Local file missing. Using cached file for check."
cp "${TMP_BZ2}" "${DEST_BZ2}"
else
send_report 1 "ERROR" "Cannot process OVAL check. Local files missing (HTTP ${STATUS})."
fi
fi
if [ ! -f "${DEST_XML}" ]; then
if ! bunzip2 -f "${DEST_BZ2}"; then
send_report 1 "ERROR" "Failed to decompress OVAL file: bunzip2 failed."
fi
fi
echo "🔬 Running OVAL evaluation with oscap..."
oscap oval eval --results "${RESULT_XML}" "${DEST_XML}" || true
grep "<definition " "${RESULT_XML}" 2>/dev/null | \
sed -nE 's/.*id="([^"]+)".*result="([^"]+)".*/\1,\2/p' | \
awk -F, 'tolower($2) ~ /true/ {print $1}' | sort -u > "${ID_FILE}"
if [ -s "${ID_FILE}" ]; then
echo "🚨 Found $(wc -l < "${ID_FILE}") definitions with result=true."
: > "${CVE_REPORT}"
while IFS= read -r ID || [ -n "${ID}" ]; do
[ -z "${ID}" ] && continue
CVE_TITLE=$(grep -a -A6 -F -- "$ID" "${RESULT_XML}" 2>/dev/null | \
sed -n 's/.*<title>\(.*\)<\/title>.*/\1/p' | head -n1 || true)
if [ -n "$CVE_TITLE" ]; then
SECURITY_ID=$(echo "${CVE_TITLE}" | awk '{print $1}')
PACKAGE_NAME=$(echo "${CVE_TITLE}" | awk '{print $2}')
LINK=$(generate_url "${SECURITY_ID}")
PARSED_CVE="${PACKAGE_NAME} | ${SECURITY_ID} | ${LINK}"
else
PARSED_CVE="(no-title-found-for-${ID})"
fi
echo "${PARSED_CVE}" >> "${CVE_REPORT}"
done < "${ID_FILE}"
send_report 0 "CVE_FOUND"
else
echo "✅ No vulnerable definitions found (result=true)."
if [ "$STATUS" -eq 304 ]; then
send_report 0 "INFO"
else
send_report 0 "SUCCESS"
fi
fi
;;
999)
send_report 1 "ERROR" "Network/Curl failed to reach ${URL}"
;;
*)
send_report 1 "ERROR" "Something went wrong: Unexpected HTTP status code ${STATUS} from ${URL}"
;;
esac
- เปลี่ยนสิทธิ์
Bash
chmod +x gem-oval-check.sh
- อย่าลืมก่อนใช้งานต้องติดตั้ง openscap-scanner ด้วยคำสั่ง
Bash
apt install -y openscap-scanner
- จากนั้นตั้ง crontab รันวันละครั้ง
Bash
crontab -e
- กรอกข้อมูล
Bash
30 2 * * * /path_to_script/gem-oval-check.sh > /dev/null 2>&1
- ก็จะมีอีเมลส่งมาทุกวันนนน
- จบ
- ขอให้สนุก
Leave a Reply