# 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"