diff --git a/Resources/CVE/2023-08.json b/Resources/CVE/2023-08.json index 8f2538c..9ae29e7 100644 --- a/Resources/CVE/2023-08.json +++ b/Resources/CVE/2023-08.json @@ -6,6 +6,6 @@ "severity": "medium", "summary": "ssl.SSLSocket may bypass TLS handshake verification on aborted connections, allowing pre-handshake data to be read by callers", "affectedVersionRange": "< 3.8.18, < 3.9.18, < 3.10.13, < 3.11.5", - "recordedAt": "2023-08-25" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2023-11.json b/Resources/CVE/2023-11.json index 3a5a202..c03f6d0 100644 --- a/Resources/CVE/2023-11.json +++ b/Resources/CVE/2023-11.json @@ -6,6 +6,6 @@ "severity": "high", "summary": "Integer overflow in Qt Network HTTP/2 implementation allows remote servers to cause heap corruption or denial of service via a crafted HTTP/2 response", "affectedVersionRange": "< 5.15.17, < 6.5.4, < 6.6.2", - "recordedAt": "2023-11-10" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2024-03.json b/Resources/CVE/2024-03.json index 7f25597..30c82f4 100644 --- a/Resources/CVE/2024-03.json +++ b/Resources/CVE/2024-03.json @@ -6,6 +6,6 @@ "severity": "medium", "summary": "zipfile module vulnerable to zip-bomb via quoted-overlap attack; extracting crafted archives may consume excessive disk space or memory", "affectedVersionRange": "< 3.9.19, < 3.10.14, < 3.11.8, < 3.12.2", - "recordedAt": "2024-03-19" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2024-07.json b/Resources/CVE/2024-07.json index 6d85daa..cba0089 100644 --- a/Resources/CVE/2024-07.json +++ b/Resources/CVE/2024-07.json @@ -6,6 +6,6 @@ "severity": "high", "summary": "Qt Network HTTP/2 server push handling allows man-in-the-middle attackers to inject responses or read cleartext data due to missing validation of push promise headers", "affectedVersionRange": "< 6.7.2", - "recordedAt": "2024-07-04" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2024-09.json b/Resources/CVE/2024-09.json index 0fc96d4..6fbf5a0 100644 --- a/Resources/CVE/2024-09.json +++ b/Resources/CVE/2024-09.json @@ -6,7 +6,7 @@ "severity": "high", "summary": "ASAR Integrity bypass via content modification (Windows only, requires embeddedAsarIntegrityValidation + onlyLoadAppFromAsar fuses)", "affectedVersionRange": "< 30.0.5", - "recordedAt": "2024-09-26" + "recordedAt": "2026-07-15" }, { "id": "CVE-2024-46993", @@ -15,7 +15,7 @@ "severity": "medium", "summary": "Heap buffer overflow in NativeImage::CreateFromPath / CreateFromBuffer", "affectedVersionRange": "< 28.3.2, < 29.3.3, < 30.0.3", - "recordedAt": "2024-09-26" + "recordedAt": "2026-07-15" }, { "id": "CVE-2024-6232", @@ -24,7 +24,7 @@ "severity": "high", "summary": "tarfile module Regular Expression Denial of Service (ReDoS) via crafted PAX header strings, causing excessive CPU usage", "affectedVersionRange": "< 3.9.20, < 3.10.15, < 3.11.10, < 3.12.7", - "recordedAt": "2024-09-03" + "recordedAt": "2026-07-15" }, { "id": "CVE-2024-8088", @@ -33,6 +33,6 @@ "severity": "medium", "summary": "zipimport module infinite loop when processing ZIP files containing entries with excessive null bytes in the name field", "affectedVersionRange": "< 3.9.20, < 3.10.15, < 3.11.10, < 3.12.5", - "recordedAt": "2024-09-03" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2025-09.json b/Resources/CVE/2025-09.json index 83bc9c6..c82cfb9 100644 --- a/Resources/CVE/2025-09.json +++ b/Resources/CVE/2025-09.json @@ -5,6 +5,6 @@ "severity": "medium", "summary": "ASAR Integrity bypass via resource modification (requires embeddedAsarIntegrityValidation + onlyLoadAppFromAsar fuses)", "affectedVersionRange": "< 35.7.5, < 36.8.1, < 37.3.1", - "recordedAt": "2025-09-04" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/2026-03.json b/Resources/CVE/2026-03.json index 53ad750..c5c559f 100644 --- a/Resources/CVE/2026-03.json +++ b/Resources/CVE/2026-03.json @@ -5,7 +5,7 @@ "severity": "high", "summary": "Context Isolation bypass via window.open", "affectedVersionRange": "< 27.1.0, < 26.6.0, < 25.9.7", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2023-39956", @@ -13,7 +13,7 @@ "severity": "high", "summary": "Renderer process sandbox escape", "affectedVersionRange": "< 26.2.1, < 25.8.1, < 24.8.3", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2023-29198", @@ -21,7 +21,7 @@ "severity": "critical", "summary": "Out-of-bounds write in V8 (Chromium)", "affectedVersionRange": "< 25.0.0", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2022-29247", @@ -29,7 +29,7 @@ "severity": "high", "summary": "Protocol handler allows loading arbitrary code", "affectedVersionRange": "< 19.0.0, < 18.3.1, < 17.4.1", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2022-21718", @@ -37,7 +37,7 @@ "severity": "medium", "summary": "Arbitrary file read via custom protocol handler", "affectedVersionRange": "< 17.0.0, < 16.0.6, < 15.3.5", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2021-39184", @@ -45,7 +45,7 @@ "severity": "high", "summary": "Context Isolation bypass via window.open", "affectedVersionRange": "< 15.0.0, < 14.1.0, < 13.3.0", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" }, { "id": "CVE-2020-15215", @@ -53,6 +53,6 @@ "severity": "critical", "summary": "Remote code execution via nativeWindowOpen", "affectedVersionRange": "< 11.0.0, < 10.1.2, < 9.3.3", - "recordedAt": "2026-03-13" + "recordedAt": "2026-07-15" } ] diff --git a/Resources/CVE/cef.json b/Resources/CVE/cef.json new file mode 100644 index 0000000..a80700f --- /dev/null +++ b/Resources/CVE/cef.json @@ -0,0 +1,11 @@ +[ + { + "id": "CVE-2026-11645", + "cveId": "CVE-2026-11645", + "framework": "cef", + "severity": "high", + "summary": "Out-of-bounds read/write in Chromium V8 JavaScript engine allowing sandboxed code execution via crafted HTML page", + "affectedVersionRange": "< 149.0.7827", + "recordedAt": "2026-07-15" + } +] diff --git a/Resources/CVE/electron.json b/Resources/CVE/electron.json new file mode 100644 index 0000000..16ecdf3 --- /dev/null +++ b/Resources/CVE/electron.json @@ -0,0 +1,47 @@ +[ + { + "id": "CVE-2026-34780", + "cveId": "CVE-2026-34780", + "framework": "electron", + "severity": "high", + "summary": "Context Isolation bypass via contextBridge VideoFrame transfer; attacker in main world can access isolated world Node.js APIs", + "affectedVersionRange": "< 39.8.0, < 40.7.0, < 41.0.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-34769", + "cveId": "CVE-2026-34769", + "framework": "electron", + "severity": "high", + "summary": "Renderer command-line switch injection via undocumented commandLineSwitches webPreference", + "affectedVersionRange": "< 38.8.6, < 39.8.0, < 40.7.0, < 41.0.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-34774", + "cveId": "CVE-2026-34774", + "framework": "electron", + "severity": "high", + "summary": "Use-after-free in offscreen child window paint callback when parent WebContents is destroyed before child", + "affectedVersionRange": "< 39.8.1, < 40.7.0, < 41.0.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-34771", + "cveId": "CVE-2026-34771", + "framework": "electron", + "severity": "high", + "summary": "Use-after-free in fullscreen, pointer-lock, or keyboard-lock permission request callbacks", + "affectedVersionRange": "< 38.8.6, < 39.8.0, < 40.7.0, < 41.0.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-34770", + "cveId": "CVE-2026-34770", + "framework": "electron", + "severity": "high", + "summary": "Use-after-free in powerMonitor module after native object is garbage-collected", + "affectedVersionRange": "< 38.8.6, < 39.8.1, < 40.8.0, < 41.0.0", + "recordedAt": "2026-07-15" + } +] diff --git a/Resources/CVE/python.json b/Resources/CVE/python.json new file mode 100644 index 0000000..68cd89e --- /dev/null +++ b/Resources/CVE/python.json @@ -0,0 +1,56 @@ +[ + { + "id": "CVE-2026-6100", + "cveId": "CVE-2026-6100", + "framework": "python", + "severity": "critical", + "summary": "Use-after-free in lzma.LZMADecompressor, bz2.BZ2Decompressor, and gzip.GzipFile when reused after MemoryError", + "affectedVersionRange": "< 3.9.25, < 3.10.20, < 3.11.15, < 3.12.13, < 3.13.14, < 3.14.6, < 3.15.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-7210", + "cveId": "CVE-2026-7210", + "framework": "python", + "severity": "high", + "summary": "Insufficient entropy for Expat hash-flooding protection in xml.parsers.expat and xml.etree.ElementTree", + "affectedVersionRange": "< 3.9.25, < 3.10.20, < 3.11.15, < 3.12.13, < 3.13.14, < 3.14.6, < 3.15.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-9669", + "cveId": "CVE-2026-9669", + "framework": "python", + "severity": "high", + "summary": "Out-of-bounds stack write in bz2.BZ2Decompressor when reused after a decompression error", + "affectedVersionRange": "< 3.9.25, < 3.10.20, < 3.11.15, < 3.12.13, < 3.13.14, < 3.14.6, < 3.15.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-3087", + "cveId": "CVE-2026-3087", + "framework": "python", + "severity": "high", + "summary": "shutil.unpack_archive() extracts ZIP archives with absolute Windows paths outside target directory", + "affectedVersionRange": "< 3.9.25, < 3.10.20, < 3.11.15, < 3.12.13, < 3.13.14, < 3.14.5, < 3.15.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-4786", + "cveId": "CVE-2026-4786", + "framework": "python", + "severity": "high", + "summary": "Incomplete mitigation of CVE-2026-4519 allows command injection via %action in webbrowser.open()", + "affectedVersionRange": "< 3.9.25, < 3.10.20, < 3.11.15, < 3.12.13, < 3.13.14, < 3.14.6, < 3.15.0", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2026-15308", + "cveId": "CVE-2026-15308", + "framework": "python", + "severity": "high", + "summary": "CPU denial-of-service in incremental html.parser.HTMLParser via repeated unterminated markup declarations", + "affectedVersionRange": "< 3.15.0", + "recordedAt": "2026-07-15" + } +] diff --git a/Resources/CVE/qt.json b/Resources/CVE/qt.json new file mode 100644 index 0000000..78132b5 --- /dev/null +++ b/Resources/CVE/qt.json @@ -0,0 +1,20 @@ +[ + { + "id": "CVE-2026-6210", + "cveId": "CVE-2026-6210", + "framework": "qt", + "severity": "high", + "summary": "Type confusion and heap-buffer-overflow in Qt SVG marker/mask handling via crafted SVG image", + "affectedVersionRange": "< 6.8.8, < 6.11.1", + "recordedAt": "2026-07-15" + }, + { + "id": "CVE-2025-14576", + "cveId": "CVE-2025-14576", + "framework": "qt", + "severity": "high", + "summary": "QML/JavaScript code injection in Qt Quick VectorImage component via malicious SVG node IDs", + "affectedVersionRange": "< 6.8.7, < 6.10.2", + "recordedAt": "2026-07-15" + } +] diff --git a/Sources/Services/SecurityAnalyzer.swift b/Sources/Services/SecurityAnalyzer.swift index 960b1ab..4e825bf 100644 --- a/Sources/Services/SecurityAnalyzer.swift +++ b/Sources/Services/SecurityAnalyzer.swift @@ -64,13 +64,13 @@ struct SecurityDataStatus: Equatable { struct SecurityAnalyzer { // SECURITY_RULES_METADATA_START - private static let securityRulesVersion = "2026.03.27" - private static let securityRulesLastReviewedAt = "2026-03-27" + private static let securityRulesVersion = "2026.07.15" + private static let securityRulesLastReviewedAt = "2026-07-15" private static let securityRulesReminderThresholdDays = 30 // SECURITY_RULES_METADATA_END // 已知安全的最低 Electron 主版本(Electron 39+ 目前无已知未修复 CVE) - private static let minSafeElectronMajor = 39 + private static let minSafeElectronMajor = 41 // 从 Resources/CVE/*.json 加载所有框架的 CVE 数据 private static let allVulnerabilities: [SecurityIssue] = loadAllCVEs() diff --git a/scripts/update_security_rules.sh b/scripts/update_security_rules.sh new file mode 100755 index 0000000..09dfca6 --- /dev/null +++ b/scripts/update_security_rules.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash +# +# Update FrameworkScanner security rules metadata. +# Usage: +# scripts/update_security_rules.sh --date 2026-07-15 --version 2026.07.15 --stamp-cves +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +ANALYZER_FILE="${PROJECT_ROOT}/Sources/Services/SecurityAnalyzer.swift" +CVE_DIR="${PROJECT_ROOT}/Resources/CVE" + +DATE="" +VERSION="" +THRESHOLD="" +STAMP_CVE=false + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ -z "${DATE}" || -z "${VERSION}" ]]; then + echo "Error: --date and --version are required." >&2 + usage >&2 + exit 1 +fi + +if [[ ! "${DATE}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then + echo "Error: --date must be in YYYY-MM-DD format." >&2 + exit 1 +fi + +if [[ ! "${VERSION}" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$ ]]; then + echo "Error: --version must be in YYYY.MM.DD format." >&2 + exit 1 +fi + +if [[ ! -f "${ANALYZER_FILE}" ]]; then + echo "Error: SecurityAnalyzer.swift not found at ${ANALYZER_FILE}" >&2 + exit 1 +fi + +# Compute the minimum safe Electron major version from CVE data. +# It is set to one major above the highest fixed Electron version referenced +# in Resources/CVE/electron.json (or 39 if no Electron CVEs exist). +compute_min_safe_electron_major() { + local electron_cve_file="${CVE_DIR}/electron.json" + local max_major=0 + if [[ -f "${electron_cve_file}" ]]; then + # Extract version strings from affectedVersionRange values. + local ranges + ranges="$(grep -oE '< [0-9]+\.[0-9]+\.[0-9]+' "${electron_cve_file}" | sed 's/^< //' || true)" + while IFS= read -r ver; do + [[ -z "${ver}" ]] && continue + local major + major="$(echo "${ver}" | cut -d. -f1)" + if [[ "${major}" =~ ^[0-9]+$ && "${major}" -gt "${max_major}" ]]; then + max_major="${major}" + fi + done <<< "${ranges}" + fi + if [[ "${max_major}" -gt 0 ]]; then + echo "$((max_major + 1))" + else + echo "39" + fi +} + +MIN_SAFE_ELECTRON_MAJOR="$(compute_min_safe_electron_major)" + +# Update metadata constants in SecurityAnalyzer.swift. +sed -i.bak \ + -e "s/private static let securityRulesVersion = \"[^\"]*\"/private static let securityRulesVersion = \"${VERSION}\"/" \ + -e "s/private static let securityRulesLastReviewedAt = \"[^\"]*\"/private static let securityRulesLastReviewedAt = \"${DATE}\"/" \ + -e "s/private static let minSafeElectronMajor = [0-9]*/private static let minSafeElectronMajor = ${MIN_SAFE_ELECTRON_MAJOR}/" \ + "${ANALYZER_FILE}" + +if [[ -n "${THRESHOLD}" ]]; then + if [[ ! "${THRESHOLD}" =~ ^[0-9]+$ ]]; then + echo "Error: --threshold must be a positive integer." >&2 + exit 1 + fi + sed -i.bak \ + -e "s/private static let securityRulesReminderThresholdDays = [0-9]*/private static let securityRulesReminderThresholdDays = ${THRESHOLD}/" \ + "${ANALYZER_FILE}" +fi + +rm -f "${ANALYZER_FILE}.bak" + +# Optionally stamp recordedAt for all CVE entries. +if [[ "${STAMP_CVE}" == true ]]; then + if [[ ! -d "${CVE_DIR}" ]]; then + echo "Error: CVE directory not found at ${CVE_DIR}" >&2 + exit 1 + fi + + python3 - "${CVE_DIR}" "${DATE}" <<'PY' +import json +import os +import sys + +cve_dir = sys.argv[1] +stamp_date = sys.argv[2] + +for filename in sorted(os.listdir(cve_dir)): + if not filename.endswith('.json'): + continue + filepath = os.path.join(cve_dir, filename) + try: + with open(filepath, 'r', encoding='utf-8') as f: + data = json.load(f) + except (json.JSONDecodeError, OSError): + print(f'Warning: skipping {filename}', file=sys.stderr) + continue + + if not isinstance(data, list): + continue + + changed = False + for entry in data: + if isinstance(entry, dict): + entry['recordedAt'] = stamp_date + changed = True + + if changed: + with open(filepath, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2, ensure_ascii=False) + f.write('\n') +PY +fi + +echo "Security rules metadata updated: version=${VERSION}, date=${DATE}, minSafeElectronMajor=${MIN_SAFE_ELECTRON_MAJOR}" +if [[ "${STAMP_CVE}" == true ]]; then + echo "CVE recordedAt stamped to ${DATE}." +fi