From bee8763c671b9083684d5fe54b2418a7dd60c480 Mon Sep 17 00:00:00 2001 From: benedict Date: Wed, 6 May 2026 03:23:32 +0700 Subject: [PATCH] add playbooks --- playbooks/archLinux.yaml | 344 ++++++++++++++++++++++++++++++ playbooks/mac.yaml | 306 ++++++++++++++++++++++++++ playbooks/windows.yaml | 449 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1099 insertions(+) create mode 100644 playbooks/archLinux.yaml create mode 100644 playbooks/mac.yaml create mode 100644 playbooks/windows.yaml diff --git a/playbooks/archLinux.yaml b/playbooks/archLinux.yaml new file mode 100644 index 0000000..fbb34d7 --- /dev/null +++ b/playbooks/archLinux.yaml @@ -0,0 +1,344 @@ +# yaml-language-server: $schema=https://github.com/benedictjohannes/crobe/releases/latest/download/playbook.schema.json +title: "Lixus Desktop Compliance Report (Arch Linux)" + +sections: + - title: "1. OS Maintenance & Security" + description: + - "Checks for OS updates, official verification, and authentication security." + assertions: + - code: OS_RECENTLY_UPDATED + title: "OS Recently Updated" + description: "Verify the system has been updated within the last 30 days." + cmds: + - exec: + # On Debian/Ubuntu, check last modification of pkgcache. + # On others, checking /var/log/dnf.log or /var/log/yum.log might work. + # We use a JS function to try multiple methods. + func: | + ({ os }) => { + return ` + if [ -f /var/cache/apt/pkgcache.bin ]; then + stat -c %Y /var/cache/apt/pkgcache.bin + elif [ -f /var/log/dnf.log ]; then + stat -c %Y /var/log/dnf.log + elif [ -f /var/log/pacman.log ]; then + stat -c %Y /var/log/pacman.log + else + echo "Unknown" + fi + ` + } + gather: + - key: "OS_LAST_UPDATE_TIME" + func: | + (stdout) => { + const val = parseInt(stdout.trim()); + return isNaN(val) ? "Unknown" : new Date(val * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout, stderr) => { + const lastUpdate = parseInt(stdout.trim()); + if (isNaN(lastUpdate)) return -1; + const thirtyDaysInSeconds = 30 * 24 * 60 * 60; + const nowInSeconds = Math.floor(Date.now() / 1000); + return (nowInSeconds - lastUpdate) < thirtyDaysInSeconds ? 1 : -1; + } + passDescription: "System was updated in the last 30 days." + failDescription: "System has not been updated in the last 30 days." + + - code: OS_INTEGRITY + title: "OS Integrity Verification" + description: "Checks if the OS is recognized from official sources." + cmds: + - exec: + script: "cat /etc/os-release" + stdOutRule: + # Check ID or ID_LIKE for arch linux + regex: "(ID|ID_LIKE)=.*(arch)" + passDescription: "OS is identified as a recognized distribution." + failDescription: "OS distribution is not recognized or is non-standard." + - code: DEVICE_INFO + title: "Device Identification" + description: "Gathering hardware identities (Brand, Model, and Serial)." + cmds: + - exec: + script: | + BRAND=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "Unknown") + MODEL=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "Unknown") + # Serial often requires sudo + SERIAL=$(sudo cat /sys/class/dmi/id/product_serial 2>/dev/null || echo "Unknown") + echo "$BRAND|||$MODEL|||$SERIAL" + gather: + - key: "DEVICE_BRAND" + func: "(stdout) => stdout.split('|||')[0]" + - key: "DEVICE_MODEL" + func: "(stdout) => stdout.split('|||')[1]" + - key: "DEVICE_SERIAL" + func: "(stdout) => stdout.split('|||')[2].trim()" + stdOutRule: + func: (stdout) => stdout.split('|||').length === 3 + passDescription: Device information successfully gathered + failDescription: Fail to gather device information + + - title: "2. Identity & Access Control" + description: + - "Audits login configurations, screen lock, and administrative privileges." + assertions: + - code: NO_AUTO_LOGIN + title: "No Auto Login" + description: "Ensures automatic login is disabled." + cmds: + - exec: + # Checks GDM, LightDM, and SDDM for auto-login configuration + script: | + grep -i "AutomaticLoginEnable=true" /etc/gdm3/custom.conf /etc/gdm/custom.conf 2>/dev/null | grep -v "^#" + grep -i "autologin-user=" /etc/lightdm/lightdm.conf 2>/dev/null | grep -v "^#" + awk '/\[Autologin\]/{flag=1; next} /^\[/{flag=0} flag && /^User=/{print $0}' /etc/sddm.conf /etc/sddm.conf.d/*.conf 2>/dev/null | grep -v "^#" + exitCodeRules: + - min: 1 # No match found (good) + max: 1 + result: 1 + - min: 0 # Match found (bad) + max: 0 + result: -1 + passDescription: "Auto login is disabled." + failDescription: "Auto login appears to be enabled." + + - code: SCREEN_LOCK_TIMEOUT + title: "Screen Lock Timeout" + description: "Verify screen lock timeout is under 30 minutes (1800 seconds)." + cmds: + - exec: + # Check GNOME and KDE for screen lock timeout + script: | + if command -v gsettings >/dev/null 2>&1; then + VAL=$(gsettings get org.gnome.desktop.session idle-delay 2>/dev/null | awk '{print $NF}') + if [ -n "$VAL" ] && [ "$VAL" != "0" ] && [ "$VAL" != "uint32" ]; then + echo "$VAL" + exit 0 + fi + fi + CUR_USER=$(who | awk '{print $1}' | head -n 1) + if [ -n "$CUR_USER" ]; then + KDE_VAL=$(sudo -u "$CUR_USER" kreadconfig6 --group Daemon --key Timeout 2>/dev/null) + [ -z "$KDE_VAL" ] && KDE_VAL=$(sudo -u "$CUR_USER" kreadconfig5 --group Daemon --key Timeout 2>/dev/null) + if [ -n "$KDE_VAL" ] && [ "$KDE_VAL" != "0" ]; then + echo $((KDE_VAL * 60)) + exit 0 + fi + fi + KDE_CFG_VAL=$(find /home -maxdepth 3 -name kscreenlockerrc -exec grep "^Timeout=" {} + 2>/dev/null | head -n 1 | cut -d'=' -f2) + if [ -n "$KDE_CFG_VAL" ] && [ "$KDE_CFG_VAL" != "0" ]; then + echo $((KDE_CFG_VAL * 60)) + exit 0 + fi + echo "0" + gather: + - key: "SCREEN_LOCK_TIMEOUT_SECONDS" + func: | + (stdout) => { + const val = stdout.replace("uint32 ", "").trim(); + return isNaN(parseInt(val)) ? "Unknown" : val; + } + stdOutRule: + func: | + (stdout) => { + const val = stdout.replace("uint32 ", "").trim(); + const timeout = parseInt(val); + // 1800s = 30m. Note: 0 usually means 'never' if not careful, but we treat it as passing if strictly < 1800. + // However, '0' in GNOME often means Disabled. + if (timeout === 0) return -1; + return timeout <= 1800 ? 1 : -1; + } + passDescription: "Screen lock timeout is configured within safe limits." + failDescription: "Screen lock timeout is too long or disabled." + + - code: USER_PRIVILEGE + title: "User is not running as local administrator" + description: "Ensures the current user is not local administrator/root." + cmds: + - exec: + script: "id -u" + gather: + - key: "CURRENT_USER_ID" + func: "(stdout) => stdout.trim()" + stdOutRule: + # Current user should not be root (0) + regex: "^[1-9][0-9]*$" + passDescription: "User is operating with standard privileges." + failDescription: "User is operating with administrative privileges." + + - title: "3. Disk Security" + description: + - "Checks for disk encryption status." + assertions: + - code: ENCRYPTION_STATUS + title: "Disk / Storage is Encrypted" + description: "Verify that at least one block device is encrypted." + cmds: + - exec: + script: "lsblk -p -l -n -o NAME,FSTYPE | grep -i 'crypto_LUKS' | awk '{print $1}'" + gather: + - key: "ENCRYPTED_DEVICES" + func: | + (stdout) => { + return stdout.trim().split('\n').map(s => s.trim()).filter(Boolean).join(', '); + } + passDescription: "Encrypted volumes detected." + failDescription: "No encrypted volumes found." + + - title: "4. Threat and Malware Protection" + description: + - "Checks for Antivirus installation, update status, and real-time protection." + assertions: + - code: ANTIVIRUS_ACTIVE + title: "Antivirus Active" + description: "Verify that antivirus is running." + preCmds: + - script: sudo systemctl start clamav-daemon + excludeFromReport: true + cmds: + - exec: + script: "systemctl is-active clamav-daemon" + passDescription: "Antivirus is active" + failDescription: "Antivirus is not active or not installed." + + - code: ANTIVIRUS_UPDATED + title: "Antivirus Definitions Up-to-Date" + description: "Check if antivirus definitions were updated in the last 30 days." + preCmds: + - script: sudo freshclam + excludeFromReport: true + cmds: + - exec: + # Check freshclam log or /var/lib/clamav database age + script: "stat -c %Y /var/lib/clamav/daily.cld 2>/dev/null || stat -c %Y /var/lib/clamav/daily.cvd 2>/dev/null || stat -c %Y /var/lib/clamav/main.cvd 2>/dev/null" + gather: + - key: "ANTIVIRUS_LAST_UPDATE" + includeStdErr: false + func: | + (stdout) => { + return new Date(Number(stdout.trim()) * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout) => { + const stamp = parseInt(stdout.trim()); + if (isNaN(stamp)) return -1; + const thirtyDays = 30 * 24 * 60 * 60; + const now = Math.floor(Date.now() / 1000); + return (now - stamp) < thirtyDays ? 1 : -1; + } + passDescription: "Definitions are up to date." + failDescription: "Definitions are older than 30 days or missing." + + - title: "5. Network Security" + description: + - "Checks firewall and network protection status." + assertions: + - code: FIREWALL_ACTIVE + title: "Firewall Status" + description: "Verify that a firewall is active." + cmds: + - exec: + # systemctl is-active doesn't require root and can check multiple services + script: | + if systemctl is-active --quiet ufw; then + echo "active (ufw)" + elif systemctl is-active --quiet firewalld; then + echo "active (firewalld)" + else + echo "inactive" + exit 1 + fi + gather: + - key: "FIREWALL_STATUS" + func: "(stdout) => stdout.trim() || 'Inactive/Unknown'" + passDescription: "Firewall is active." + failDescription: "No active firewall detected." + + - title: "6. Boot Security" + description: + - "Verification of firmware security settings." + assertions: + - code: SECURE_BOOT_ENABLED + title: "Secure Boot State" + description: "Check if UEFI Secure Boot is enabled." + cmds: + - exec: + script: "sbctl status" + stdOutRule: + regex: "Secure Boot:.*Enabled" + passDescription: "Secure Boot is enabled." + failDescription: "Secure Boot is disabled." + + - title: "7. Application Integrity" + description: + - "Audits browser update status." + assertions: + - code: BROWSER_UP_TO_DATE + title: "Browser Updated" + description: "Verify if the primary web browsers has been updated within the last 30 days." + cmds: + - exec: + # Check modification time of browser binary as a proxy for update + script: | + for b in /usr/bin/firefox /usr/bin/google-chrome /usr/bin/chromium /usr/bin/brave-browser; do + if [ -f "$b" ]; then + echo "$b:$(stat -c %Y "$b")" + fi + done + gather: + - key: "FIREFOX_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('firefox')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split(':')[1]) * 1000).toISOString(); + } + - key: "CHROME_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('chrome') || l.includes('chromium')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split(':')[1]) * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout) => { + const lines = stdout.trim().split('\n').filter(Boolean); + let latest = 0; + lines.forEach(line => { + const stamp = parseInt(line.split(':')[1]); + if (stamp > latest) latest = stamp; + }); + if (latest === 0) return -1; + const thirtyDays = 30 * 24 * 60 * 60; + const now = Date.now() / 1000; + return (now - latest) < thirtyDays ? 1 : -1; + } + passDescription: "At least one browser appears recently updated." + failDescription: "No recently updated browsers found." + + - title: "8. Monitoring & Logging" + description: + - "Ensures system event logging is active." + assertions: + - code: LOGGING_ACTIVE + title: "System has active logging service" + description: "Verify that system logging service is active." + cmds: + - exec: + script: "systemctl is-active systemd-journald" + passDescription: "System logging is active." + failDescription: "System logging service is not running." + +reportDestination: folder +#reportDestinationFolder: MyCustomFolder01 +reportDestinationHttps: + url: https://gway.lixus.id/campaignhub/api/log/complianceProbe04 + signatureSecret: DontWorryBeHappy + format: json + additionalHeaders: + X-Custom-Header: "Custom Value" \ No newline at end of file diff --git a/playbooks/mac.yaml b/playbooks/mac.yaml new file mode 100644 index 0000000..84140e2 --- /dev/null +++ b/playbooks/mac.yaml @@ -0,0 +1,306 @@ +# yaml-language-server: $schema=https://github.com/benedictjohannes/crobe/releases/latest/download/playbook.schema.json +title: "Lixus Desktop Compliance Report (MacOS)" + +reportFrontmatter: + author: "SRE Team" + +sections: + - title: "1. OS Maintenance & Security" + description: + - "Checks for OS updates, official verification, and authentication security." + assertions: + - code: OS_RECENTLY_UPDATED + title: "OS Recently Updated" + description: "Verify the system has been updated within the last 30 days." + cmds: + - exec: + script: | + # Get the last update time from SystemVersion.plist modification date + last_update=$(stat -f %m /System/Library/CoreServices/SystemVersion.plist 2>/dev/null) + if [ -n "$last_update" ]; then + echo "$last_update" + else + echo "Unknown" + fi + gather: + - key: "OS_LAST_UPDATE_TIME" + func: | + (stdout) => { + const val = parseInt(stdout.trim()); + return isNaN(val) ? "Unknown" : new Date(val * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout, stderr) => { + const lastUpdate = parseInt(stdout.trim()); + if (isNaN(lastUpdate)) return -1; + const thirtyDaysInSeconds = 30 * 24 * 60 * 60; + const nowInSeconds = Math.floor(Date.now() / 1000); + return (nowInSeconds - lastUpdate) < thirtyDaysInSeconds ? 1 : -1; + } + passDescription: "System was updated in the last 30 days." + failDescription: "System has not been updated in the last 30 days." + + - code: OS_INTEGRITY + title: "OS Integrity Verification" + description: "Checks if the OS is recognized from official sources." + cmds: + - exec: + script: "sw_vers -productName" + stdOutRule: + regex: "macOS" + passDescription: "OS is identified as a recognized distribution." + failDescription: "OS distribution is not recognized or is non-standard." + - code: DEVICE_INFO + title: "Device Identification" + description: "Gathering hardware identities (Brand, Model, and Serial)." + cmds: + - exec: + func: | + ({ os }) => ` + MODEL=$(sysctl -n hw.model) + SERIAL=$(system_profiler SPHardwareDataType | awk '/Serial Number/ {print $NF}') + echo "${ os === 'mac' ? 'Apple' : 'Unknown' }|||$MODEL|||$SERIAL" + ` + gather: + - key: "DEVICE_BRAND" + func: "(stdout) => stdout.split('|||')[0]" + - key: "DEVICE_MODEL" + func: "(stdout) => stdout.split('|||')[1]" + - key: "DEVICE_SERIAL" + func: "(stdout) => stdout.split('|||')[2].trim()" + stdOutRule: + func: (stdout) => stdout.split('|||').length === 3 + passDescription: Device information successfully gathered + failDescription: Fail to gather device information + + + - title: "2. Identity & Access Control" + description: + - "Audits login configurations, screen lock, and administrative privileges." + assertions: + - code: NO_AUTO_LOGIN + title: "No Auto Login" + description: "Ensures automatic login is disabled." + cmds: + - exec: + script: "if [ -f /etc/kcpassword ]; then echo 'Enabled'; else echo 'None'; fi" + stdOutRule: + regex: "None" + passDescription: "Auto login is disabled." + failDescription: "Auto login appears to be enabled in system preferences." + + - code: SCREEN_LOCK_TIMEOUT + title: "Screen Lock Timeout" + description: "Verify screen lock timeout is under 30 minutes (1800 seconds)." + cmds: + - exec: + script: | + idle=$(defaults -currentHost read com.apple.screensaver idleTime 2>/dev/null || echo 0) + display_sleep=$(pmset -g custom | awk '/displaysleep/ {print $2}' | sort -n | tail -n 1) + display_sleep_sec=$((display_sleep * 60)) + if [ "$idle" -eq 0 ] && [ "$display_sleep_sec" -eq 0 ]; then + echo 0 + elif [ "$idle" -eq 0 ]; then + echo "$display_sleep_sec" + elif [ "$display_sleep_sec" -eq 0 ]; then + echo "$idle" + elif [ "$idle" -lt "$display_sleep_sec" ]; then + echo "$idle" + else + echo "$display_sleep_sec" + fi + gather: + - key: "SCREEN_LOCK_TIMEOUT_SECONDS" + func: | + (stdout) => { + const val = stdout.trim(); + return isNaN(parseInt(val)) ? "Unknown" : val; + } + stdOutRule: + func: | + (stdout) => { + const timeout = parseInt(stdout.trim()); + if (timeout === 0) return -1; + return timeout <= 1800 ? 1 : -1; + } + passDescription: "Screen lock timeout is configured within safe limits." + failDescription: "Screen lock timeout is too long or disabled." + + - code: USER_PRIVILEGE + title: "User is not running as local administrator" + description: "Ensures the current user is not local administrator/root." + cmds: + - exec: + script: "id -u" + gather: + - key: "CURRENT_USER_ID" + func: "(stdout) => stdout.trim()" + stdOutRule: + # Current user should not be root (0) + regex: "^[1-9][0-9]*$" + passDescription: "User is operating with standard privileges." + failDescription: "User is operating with administrative privileges." + + - title: "3. Disk Security" + description: + - "Checks for disk encryption status." + assertions: + - code: ENCRYPTION_STATUS + title: "Disk / Storage is Encrypted" + description: "Verify that at least one block device is encrypted." + cmds: + - exec: + script: "fdesetup status" + gather: + - key: "ENCRYPTED_DEVICES" + func: "(stdout) => stdout.includes('FileVault is On') ? 'Main Disk (FileVault)' : 'None'" + stdOutRule: + regex: "FileVault is On" + passDescription: "Encrypted volumes detected." + failDescription: "No encrypted volumes found." + + - title: "4. Threat and Malware Protection" + description: + - "Checks for Antivirus installation, update status, and real-time protection." + assertions: + - code: ANTIVIRUS_ACTIVE + title: "Antivirus Active" + description: "Verify that antivirus is running." + cmds: + - exec: + script: | + GK=$(spctl --status | grep -o "enabled") + SIP=$(csrutil status | grep -o "enabled") + echo "Gatekeeper: $GK, SIP: $SIP" + stdOutRule: + regex: "Gatekeeper: enabled, SIP: enabled" + passDescription: "System protection services are active" + failDescription: "One or more system protection services are disabled." + + - code: ANTIVIRUS_UPDATED + title: "Antivirus Definitions Up-to-Date" + description: "Check if antivirus definitions were updated in the last 30 days." + cmds: + - exec: + script: "stat -f %m /Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/Resources/XProtect.yara 2>/dev/null || echo 'Unknown'" + gather: + - key: "ANTIVIRUS_LAST_UPDATE" + func: | + (stdout) => { + const val = parseInt(stdout.trim()); + return isNaN(val) ? "Unknown" : new Date(val * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout) => { + const stamp = parseInt(stdout.trim()); + if (isNaN(stamp)) return -1; + const thirtyDays = 30 * 24 * 60 * 60; + const now = Math.floor(Date.now() / 1000); + return (now - stamp) < thirtyDays ? 1 : -1; + } + passDescription: "Definitions are up to date." + failDescription: "Definitions are older than 30 days or missing." + + - title: "5. Network Security" + description: + - "Checks firewall and network protection status." + assertions: + - code: FIREWALL_ACTIVE + title: "Firewall Status" + description: "Verify that a firewall is active." + cmds: + - exec: + script: "/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate" + gather: + - key: "FIREWALL_STATUS" + func: "(stdout) => stdout.trim()" + stdOutRule: + regex: "Firewall is enabled" + passDescription: "Firewall is active." + failDescription: "No active firewall detected." + + - title: "6. Boot Security" + description: + - "Verification of firmware security settings." + assertions: + - code: SECURE_BOOT_ENABLED + title: "Secure Boot State" + description: "Check if UEFI Secure Boot is enabled." + cmds: + - exec: + script: "system_profiler SPiBridgeDataType SPSoftwareDataType 2>/dev/null | grep 'Secure Boot' | awk -F':' '{print $2}' | xargs" + stdOutRule: + regex: "(Full Security|Medium Security)" + passDescription: "Secure Boot is enabled." + failDescription: "Secure Boot is disabled or set to No Security." + + - title: "7. Application Integrity" + description: + - "Audits browser update status." + assertions: + - code: BROWSER_UP_TO_DATE + title: "Browser Updated" + description: "Verify if the primary web browsers has been updated within the last 30 days." + cmds: + - exec: + script: | + for b in /Applications/Safari.app /Applications/Google\ Chrome.app /Applications/Firefox.app; do + if [ -d "$b" ]; then + echo "$b:$(stat -f %m "$b")" + fi + done + gather: + - key: "SAFARI_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('Safari')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split(':')[1]) * 1000).toISOString(); + } + - key: "FIREFOX_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('Firefox')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split(':')[1]) * 1000).toISOString(); + } + - key: "CHROME_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('Chrome')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split(':')[1]) * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout) => { + const lines = stdout.trim().split('\n').filter(Boolean); + let latest = 0; + lines.forEach(line => { + const stamp = parseInt(line.split(':')[1]); + if (stamp > latest) latest = stamp; + }); + if (latest === 0) return -1; + const thirtyDays = 30 * 24 * 60 * 60; + const now = Date.now() / 1000; + return (now - latest) < thirtyDays ? 1 : -1; + } + passDescription: "At least one browser appears recently updated." + failDescription: "No recently updated browsers found." + + - title: "8. Monitoring & Logging" + description: + - "Ensures system event logging is active." + assertions: + - code: LOGGING_ACTIVE + title: "System has active logging service" + description: "Verify that system logging service is active." + cmds: + - exec: + script: "log show --last 1m >/dev/null 2>&1 && echo 'active' || echo 'inactive'" + stdOutRule: + regex: "active" + passDescription: "System logging is active." + failDescription: "System logging service is not running." diff --git a/playbooks/windows.yaml b/playbooks/windows.yaml new file mode 100644 index 0000000..448c13f --- /dev/null +++ b/playbooks/windows.yaml @@ -0,0 +1,449 @@ +# yaml-language-server: $schema=https://github.com/benedictjohannes/crobe/releases/latest/download/playbook.schema.json +title: "Windows Compliance Playbook" +sections: + - title: "1. OS Maintenance & Security" + description: + - "Checks for OS updates, official verification, and authentication security." + assertions: + - code: OS_RECENTLY_UPDATED + title: "OS Recently Updated" + description: "Verify the system has been updated within the last 30 days." + cmds: + - exec: + shell: "powershell" + script: | + $history = (New-Object -ComObject Microsoft.Update.Session).CreateUpdateSearcher().QueryHistory(0,1) + if ($history) { + [Math]::Floor((New-TimeSpan -Start '1970-01-01' -End $history[0].Date.ToUniversalTime()).TotalSeconds) + } else { + echo "Unknown" + } + gather: + - key: "OS_LAST_UPDATE_TIME" + func: | + (stdout) => { + const val = parseInt(stdout.trim()); + return isNaN(val) ? "Unknown" : new Date(val * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout, stderr) => { + const lastUpdate = parseInt(stdout.trim()); + if (isNaN(lastUpdate)) return -1; + const thirtyDaysInSeconds = 30 * 24 * 60 * 60; + const nowInSeconds = Math.floor(Date.now() / 1000); + return (nowInSeconds - lastUpdate) < thirtyDaysInSeconds ? 1 : -1; + } + passDescription: "System was updated in the last 30 days." + failDescription: "System has not been updated in the last 30 days." + + - code: OS_INTEGRITY + title: "OS Integrity Verification" + description: "Checks if the OS is recognized from official sources." + cmds: + - exec: + shell: "powershell" + script: "(Get-WmiObject Win32_OperatingSystem).Caption" + stdOutRule: + regex: "Windows" + passDescription: "OS is identified as a recognized distribution." + failDescription: "OS distribution is not recognized or is non-standard." + - code: DEVICE_INFO + title: "Device Identification" + description: "Gathering hardware identities (Brand, Model, and Serial)." + cmds: + - exec: + script: | + $sys = Get-CimInstance Win32_ComputerSystem + $bios = Get-CimInstance Win32_BIOS + Write-Output "$($sys.Manufacturer)|||$($sys.Model)|||$($bios.SerialNumber)" + gather: + - key: "DEVICE_BRAND" + func: "(stdout) => stdout.split('|||')[0]" + - key: "DEVICE_MODEL" + func: "(stdout) => stdout.split('|||')[1]" + - key: "DEVICE_SERIAL" + func: "(stdout) => stdout.split('|||')[2].trim()" + stdOutRule: + func: (stdout) => stdout.split('|||').length === 3 + passDescription: Device information successfully gathered + failDescription: Fail to gather device information + + - title: "2. Identity & Access Control" + description: + - "Audits login configurations, screen lock, and administrative privileges." + assertions: + - code: NO_AUTO_LOGIN + title: "No Auto Login" + description: "Ensures automatic login is disabled." + cmds: + - exec: + shell: "powershell" + script: | + $val = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -ErrorAction SilentlyContinue + if ($val) { $val.AutoAdminLogon } else { "0" } + stdOutRule: + regex: "^0$" + passDescription: "Auto login is disabled." + failDescription: "Auto login appears to be enabled." + - code: SCREEN_LOCK_TIMEOUT + title: "Screen Lock Timeout" + description: "Verify screen lock timeout is under 30 minutes (1800 seconds)." + + cmds: + - exec: + shell: "powershell" + script: | + $policyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" + $policy = Get-ItemProperty -Path $policyPath -Name InactivityTimeoutSecs -ErrorAction SilentlyContinue + + if ($policy -and $policy.InactivityTimeoutSecs) { + Write-Output "POLICY:$($policy.InactivityTimeoutSecs)" + exit + } + + $ssPath = "HKCU:\Control Panel\Desktop" + $timeout = Get-ItemProperty -Path $ssPath -Name ScreenSaveTimeOut -ErrorAction SilentlyContinue + $secure = Get-ItemProperty -Path $ssPath -Name ScreenSaverIsSecure -ErrorAction SilentlyContinue + + if ($timeout -and $secure.ScreenSaverIsSecure -eq "1") { + Write-Output "SCREENSAVER:$($timeout.ScreenSaveTimeOut)" + exit + } + + # Modern "Require sign-in" lock detection (DelayLockInterval) + $delayLock = Get-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name DelayLockInterval -ErrorAction SilentlyContinue + if ($null -ne $delayLock) { + $videoIdle = powercfg /q SCHEME_CURRENT SUB_VIDEO VIDEOIDLE + $acOff = 0 + foreach ($line in $videoIdle) { + if ($line -like "*Current AC Power Setting Index:*") { + $val = $line.Split(':')[-1].Trim() + $acOff = [Convert]::ToInt32($val, 16) + } + } + $totalLock = $acOff + [int]$delayLock.DelayLockInterval + Write-Output "MODERN:$totalLock" + exit + } + Write-Output "NOT_CONFIGURED" + + gather: + - key: "SCREEN_LOCK_TIMEOUT_SECONDS" + func: | + (stdout) => { + if (!stdout.includes(":")) return "Unknown"; + const val = parseInt(stdout.split(":")[1]); + return isNaN(val) ? "Unknown" : val; + } + stdOutRule: + func: | + (stdout) => { + const out = stdout.trim(); + + if (out === "NOT_CONFIGURED") return -1; + + const parts = out.split(":"); + if (parts.length !== 2) return -1; + + const val = parseInt(parts[1]); + if (isNaN(val)) return -1; + + return val <= 1800 ? 1 : -1; + } + passDescription: "Screen lock timeout is configured within safe limits." + failDescription: "Screen lock timeout is too long or disabled." + + - code: USER_PRIVILEGE + title: "User is not running as local administrator" + description: "Ensures the current user is not local administrator/root." + cmds: + - exec: + shell: "powershell" + script: | + try { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $sid = $identity.User.Value + $username = $identity.Name + + # Check for the RID 500 (Built-in Administrator) + if ($sid -match "-500$") { + Write-Output "BUILTIN_ADMIN|$username|$sid" + } else { + Write-Output "STANDARD|$username|$sid" + } + + } catch { + Write-Output "UNKNOWN" + } + gather: + - key: "CURRENT_USER_ID" + func: | + (stdout) => { + const val = stdout.trim(); + if (!val.includes("|")) return val; + return val.split("|")[0]; + } + stdOutRule: + func: | + (stdout) => { + const val = stdout.trim(); + if (val === "UNKNOWN") return -1; + if (val.startsWith("BUILTIN_ADMIN")) return -1; + return 1; + } + passDescription: "User is operating with standard privileges." + failDescription: "User is operating with administrative privileges." + + - title: "3. Disk Security" + description: + - "Checks for disk encryption status." + assertions: + - code: ENCRYPTION_STATUS + title: "Disk / Storage is Encrypted" + description: "Verify that at least one block device is encrypted." + + cmds: + - exec: + shell: "powershell" + script: | + $ErrorActionPreference = "Stop" + + try { + $volumes = Get-BitLockerVolume + + if ($volumes) { + $encrypted = $volumes | Where-Object { $_.ProtectionStatus -eq 'On' } + + if ($encrypted) { + Write-Output "ENCRYPTED" + } else { + Write-Output "NOT_ENCRYPTED" + } + exit + } + } catch {} + + try { + $reg = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\BitLocker" -ErrorAction Stop + + if ($reg) { + Write-Output "ENCRYPTED" + exit + } + } catch {} + + Write-Output "INSUFFICIENT_PRIVILEGE" + gather: + - key: "ENCRYPTED_DEVICES" + func: "(stdout) => stdout.trim()" + stdOutRule: + func: | + (stdout) => { + const val = stdout.trim(); + + if (val === "ENCRYPTED") return 1; + if (val === "NOT_ENCRYPTED") return -1; + + if (val === "INSUFFICIENT_PRIVILEGE") return 0; + + return -1; + } + passDescription: "Encrypted volumes detected." + failDescription: "No encrypted volumes found." + + - title: "4. Threat and Malware Protection" + description: + - "Checks for Antivirus installation, update status, and real-time protection." + assertions: + - code: ANTIVIRUS_ACTIVE + title: "Antivirus Active" + description: "Verify that antivirus is running." + cmds: + - exec: + shell: "powershell" + script: "(Get-MpComputerStatus).RealTimeProtectionEnabled" + stdOutRule: + regex: "True" + passDescription: "Antivirus is active" + failDescription: "Antivirus is not active or not installed." + + - code: ANTIVIRUS_UPDATED + title: "Antivirus Definitions Up-to-Date" + description: "Check if antivirus definitions were updated in the last 30 days." + cmds: + - exec: + shell: "powershell" + script: | + $date = (Get-MpComputerStatus).AntivirusSignatureLastUpdated + if ($date) { + [Math]::Floor((New-TimeSpan -Start '1970-01-01' -End $date.ToUniversalTime()).TotalSeconds) + } else { + echo "Unknown" + } + gather: + - key: "ANTIVIRUS_LAST_UPDATE" + func: | + (stdout) => { + const val = parseInt(stdout.trim()); + return isNaN(val) ? "Unknown" : new Date(val * 1000).toISOString(); + } + stdOutRule: + func: | + (stdout) => { + const stamp = parseInt(stdout.trim()); + if (isNaN(stamp)) return -1; + const thirtyDays = 30 * 24 * 60 * 60; + const now = Math.floor(Date.now() / 1000); + return (now - stamp) < thirtyDays ? 1 : -1; + } + passDescription: "Definitions are up to date." + failDescription: "Definitions are older than 30 days or missing." + + - title: "5. Network Security" + description: + - "Checks firewall and network protection status." + assertions: + - code: FIREWALL_ACTIVE + title: "Firewall Status" + description: "Verify that a firewall is active." + cmds: + - exec: + shell: "powershell" + script: | + $profiles = Get-NetFirewallProfile -Profile Domain,Public,Private + $active = $profiles | Where-Object {$_.Enabled -eq 'True'} + if ($active) { "Active (" + ($active | ForEach-Object {$_.Name} | Out-String).Trim() + ")" } else { "Inactive" } + gather: + - key: "FIREWALL_STATUS" + func: "(stdout) => stdout.trim()" + stdOutRule: + regex: "Active" + passDescription: "Firewall is active." + failDescription: "No active firewall detected." + + - title: "6. Boot Security" + description: + - "Verification of firmware security settings." + assertions: + - code: SECURE_BOOT_ENABLED + title: "Secure Boot State" + description: "Check if UEFI Secure Boot is enabled." + cmds: + - exec: + shell: "powershell" + script: | + try { + $result = Confirm-SecureBootUEFI + if ($result -eq $true) { + "ENABLED" + } else { + "DISABLED" + } + } catch { + if ($_.Exception.Message -like "*Access was denied*") { + "INSUFFICIENT_PRIVILEGE" + } else { + "NOT_SUPPORTED" + } + } + gather: [] + stdOutRule: + func: | + (stdout) => { + const val = stdout.trim(); + + if (val === "ENABLED") return 1; + if (val === "DISABLED") return -1; + + if (val === "INSUFFICIENT_PRIVILEGE") return 0; + + if (val === "NOT_SUPPORTED") return 1; + + return -1; + } + passDescription: "Secure Boot is enabled." + failDescription: "Secure Boot is disabled." + + - title: "7. Application Integrity" + description: + - "Audits browser update status." + assertions: + - code: BROWSER_UP_TO_DATE + title: "Browser Updated" + description: "Verify if the primary web browsers has been updated within the last 30 days." + + cmds: + - exec: + shell: "powershell" + script: | + $paths = @( + "C:\Program Files\Google\Chrome\Application\chrome.exe", + "$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe", + "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", + "C:\Program Files\Mozilla Firefox\firefox.exe" + ) + + foreach ($p in $paths) { + if (Test-Path $p) { + $file = Get-Item $p + $time = [int](New-TimeSpan -Start '1970-01-01' -End $file.LastWriteTimeUtc).TotalSeconds + Write-Output "$p|$time" + } + } + gather: + - key: "FIREFOX_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('firefox')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split('|')[1]) * 1000).toISOString(); + } + - key: "CHROME_LAST_UPDATE" + func: | + (stdout) => { + const line = stdout.trim().split('\n').find(l => l.includes('chrome') || l.includes('chromium')); + if (!line) return "Not Installed"; + return new Date(parseInt(line.split('|')[1]) * 1000).toISOString(); + } + + stdOutRule: + func: | + (stdout) => { + const lines = stdout.trim().split('\n').filter(Boolean); + if (lines.length === 0) return -1; + + const now = Date.now() / 1000; + const thirtyDays = 30 * 24 * 60 * 60; + + let pass = false; + + lines.forEach(line => { + const parts = line.split('|'); + const ts = parseInt(parts[1]); + + if (!isNaN(ts) && (now - ts) < thirtyDays) { + pass = true; + } + }); + + return pass ? 1 : -1; + } + passDescription: "At least one browser appears recently updated." + failDescription: "No recently updated browsers found." + + - title: "8. Monitoring & Logging" + description: + - "Ensures system event logging is active." + assertions: + - code: LOGGING_ACTIVE + title: "System has active logging service" + description: "Verify that system logging service is active." + cmds: + - exec: + shell: "powershell" + script: "(Get-Service EventLog).Status" + stdOutRule: + regex: "Running" + passDescription: "System logging is active." + failDescription: "System logging service is not running."