From d917b91a071fa6f2875a65c49370de50dc2824eb Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:30:21 +0800 Subject: [PATCH 01/10] fix(security): support prerelease suffixes and use max major in compute_min_safe_electron_major - Regex now matches "< X.Y.Z" and "< X.Y.Z-beta.N" style ranges. - Output semantic changed from max_major + 1 to max_major, so 41.0.0 being patched means minSafeElectronMajor = 41 (not 42). - Script can be sourced by test scripts; main execution is guarded. Refs GEO-155 --- scripts/update_security_rules.sh | 193 +++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100755 scripts/update_security_rules.sh diff --git a/scripts/update_security_rules.sh b/scripts/update_security_rules.sh new file mode 100755 index 0000000..ffe065b --- /dev/null +++ b/scripts/update_security_rules.sh @@ -0,0 +1,193 @@ +#!/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 +# +# This file can also be sourced by test scripts for the helper functions +# it defines (e.g. compute_min_safe_electron_major). +# + +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" + +# Compute the minimum safe Electron major version from CVE data. +# +# It returns the highest fixed Electron major version referenced in +# Resources/CVE/electron.json (or 39 if no Electron CVEs exist). +# A version is recognized when it appears in an affectedVersionRange as +# "< X.Y.Z" or "< X.Y.Z-prerelease" (e.g. "< 41.0.0-beta.8"). +# +# Args: +# $1 Optional path to the Electron CVE JSON file. Defaults to +# ${CVE_DIR}/electron.json. +compute_min_safe_electron_major() { + local electron_cve_file="${1:-${CVE_DIR}/electron.json}" + local max_major=0 + if [[ -f "${electron_cve_file}" ]]; then + # Extract version strings from affectedVersionRange values. + # Supported forms: "< X.Y.Z" and "< X.Y.Z-beta.N" (or any + # alphanumeric/dot pre-release suffix). + local ranges + ranges="$(grep -oE '< [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-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}" + else + echo "39" + fi +} + +# Everything below only runs when this script is executed directly. +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + return 0 +fi + +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 + +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 From df8d716d26b17b4a3bcba6b14d927349a1e8ffaf Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:30:25 +0800 Subject: [PATCH 02/10] test(security): add bash unit tests for compute_min_safe_electron_major Covers: - pre-release suffix parsing (< 41.0.0-beta.8 -> 41) - plain 3-segment version (< 38.8.6 -> 38) - mixed ranges taking the maximum major - merging across multiple CVE entries - missing-file fallback to 39 Refs GEO-155 --- scripts/test_compute_min_safe.sh | 93 ++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 scripts/test_compute_min_safe.sh diff --git a/scripts/test_compute_min_safe.sh b/scripts/test_compute_min_safe.sh new file mode 100755 index 0000000..6ec3ca2 --- /dev/null +++ b/scripts/test_compute_min_safe.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# +# Unit tests for compute_min_safe_electron_major in update_security_rules.sh. +# + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=update_security_rules.sh +source "${SCRIPT_DIR}/update_security_rules.sh" + +TMP_DIR="" +PASS=0 +FAIL=0 + +cleanup() { + if [[ -n "${TMP_DIR}" && -d "${TMP_DIR}" ]]; then + rm -rf "${TMP_DIR}" + fi +} +trap cleanup EXIT + +TMP_DIR="$(mktemp -d)" + +make_cve_json() { + local ranges="$1" + cat > "${TMP_DIR}/electron.json" <&2 + FAIL=$((FAIL + 1)) + fi +} + +# Test 1: pre-release suffix is parsed and major=41 is returned. +make_cve_json "< 41.0.0-beta.8" +assert_eq "pre-release suffix < 41.0.0-beta.8" "41" "$(compute_min_safe_electron_major "${TMP_DIR}/electron.json")" + +# Test 2: plain 3-segment version returns major=38. +make_cve_json "< 38.8.6" +assert_eq "plain version < 38.8.6" "38" "$(compute_min_safe_electron_major "${TMP_DIR}/electron.json")" + +# Test 3: mixed ranges and suffixes return the maximum major. +make_cve_json "< 38.8.6, < 41.0.0-beta.8, < 39.8.0" +assert_eq "mixed ranges max major" "41" "$(compute_min_safe_electron_major "${TMP_DIR}/electron.json")" + +# Test 4: multiple entries across different objects are merged. +cat > "${TMP_DIR}/electron.json" <<'EOF' +[ + { + "id": "TEST-001", + "affectedVersionRange": "< 37.5.0, < 38.8.6", + "recordedAt": "2026-07-15" + }, + { + "id": "TEST-002", + "affectedVersionRange": "< 39.8.0, < 40.7.0, < 41.0.0-beta.8", + "recordedAt": "2026-07-15" + } +] +EOF +assert_eq "multiple entries merged" "41" "$(compute_min_safe_electron_major "${TMP_DIR}/electron.json")" + +# Test 5: missing file falls back to 39. +rm -f "${TMP_DIR}/electron.json" +assert_eq "missing file fallback" "39" "$(compute_min_safe_electron_major "${TMP_DIR}/electron.json")" + +echo "---" +echo "Results: ${PASS} passed, ${FAIL} failed" + +if [[ "${FAIL}" -gt 0 ]]; then + exit 1 +fi From 4cbdade2a6b11fc78838e463fcc744e8c6ff1cf6 Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:30:31 +0800 Subject: [PATCH 03/10] chore(security): refresh security rules metadata to 2026.07.15 Running scripts/update_security_rules.sh with --date 2026-07-15 --version 2026.07.15 confirms minSafeElectronMajor stays at 39 on original main data (no Resources/CVE/electron.json present). Refs GEO-155 --- Sources/Services/SecurityAnalyzer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Services/SecurityAnalyzer.swift b/Sources/Services/SecurityAnalyzer.swift index 960b1ab..e89812f 100644 --- a/Sources/Services/SecurityAnalyzer.swift +++ b/Sources/Services/SecurityAnalyzer.swift @@ -64,8 +64,8 @@ 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 From 8bf55ce1e209f9ab7b8573dfad3ecc4a34ce6065 Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:03 +0800 Subject: [PATCH 04/10] chore(cve): stamp recordedAt in Resources/CVE/2023-08.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2023-08.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From 01b2ac77fee1dddd72bd155339069c70fcd8b7b0 Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:03 +0800 Subject: [PATCH 05/10] chore(cve): stamp recordedAt in Resources/CVE/2023-11.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2023-11.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From cca4b07c53cb2c26a630cab8460534289812ee5f Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:03 +0800 Subject: [PATCH 06/10] chore(cve): stamp recordedAt in Resources/CVE/2024-03.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2024-03.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From ae6a34a81d3a2bd34c2266823e5e5cdff03cc3ae Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:04 +0800 Subject: [PATCH 07/10] chore(cve): stamp recordedAt in Resources/CVE/2024-07.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2024-07.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From d8bbb82aeefc6458a1d4c859b00f621a589258fe Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:04 +0800 Subject: [PATCH 08/10] chore(cve): stamp recordedAt in Resources/CVE/2024-09.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2024-09.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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" } ] From 5b6669aaaf0fcb92bbe63edf339de80e489d9f2e Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:04 +0800 Subject: [PATCH 09/10] chore(cve): stamp recordedAt in Resources/CVE/2025-09.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2025-09.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } ] From c7947de4c38d10057c68ed1ce940ebbdcc583a7f Mon Sep 17 00:00:00 2001 From: Eski Yin Date: Thu, 16 Jul 2026 04:32:04 +0800 Subject: [PATCH 10/10] chore(cve): stamp recordedAt in Resources/CVE/2026-03.json to 2026-07-15 Refs GEO-155 --- Resources/CVE/2026-03.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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" } ]