diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/E8-CAT.ps1 b/E8-CAT.ps1
new file mode 100644
index 0000000..264de12
--- /dev/null
+++ b/E8-CAT.ps1
@@ -0,0 +1,198 @@
+[CmdletBinding()]
+param(
+ [ValidateSet('ML1','ML2','ML3','All')] [string]$Profile = 'ML1',
+ [string]$RulesPath = ".\rules",
+ [string]$ProfilesPath = ".\profiles",
+ [string]$OutDir = ".\out"
+)
+
+function New-Result($rule,$status,$evidence){
+ [pscustomobject]@{
+ RuleId = $rule.id
+ Title = $rule.title
+ Strategy = $rule.strategy
+ Level = $Profile
+ Status = $status # PASS / FAIL / SKIPPED / N/A / ERROR
+ Evidence = $evidence
+ Timestamp = (Get-Date).ToString("s")
+ }
+}
+
+function Test-Registry {
+ param($r)
+ $root = if ($r.scope -eq 'HKCU') { 'HKCU:' } else { 'HKLM:' }
+ $path = if ($r.path.StartsWith('\')) { Join-Path $root $r.path.Substring(1) } else { Join-Path $root $r.path }
+ try {
+ if (-not (Test-Path $path)) { return @{ok=$false; ev="Path missing: $path"} }
+ $prop = Get-ItemProperty -Path $path -ErrorAction Stop
+ if (-not $r.name -or $r.name -eq '') { return @{ ok=$true; ev="Key exists: $path" } }
+ if ($null -eq $prop.$($r.name)) { return @{ok=$false; ev="Value missing: $path\$($r.name)"} }
+ $val = $prop.$($r.name)
+ $op = if ($r.op) { $r.op } else { 'eq' }
+ $ok = $false
+ switch ($op) {
+ 'eq' { $ok = ([string]$val -eq [string]$r.expected) }
+ 'ne' { $ok = ([string]$val -ne [string]$r.expected) }
+ 'in' { $ok = ($r.expected -contains ([string]$val)) }
+ 'ge' { $ok = ([double]$val -ge [double]$r.expected) }
+ 'le' { $ok = ([double]$val -le [double]$r.expected) }
+ default { $ok = ([string]$val -eq [string]$r.expected) }
+ }
+ @{ ok=$ok; ev="[$path] $($r.name)=$val (expect $op $($r.expected))" }
+ } catch { @{ ok=$false; ev=("Error: {0}" -f $_.Exception.Message) } }
+}
+
+function Test-File {
+ param($r)
+ $exists = Test-Path $r.path
+ $ok = if ($r.op -eq 'absent') { -not $exists } else { $exists }
+ @{ ok=$ok; ev="$($r.path) Exists=$exists (expect $($r.op))" }
+}
+
+function Test-Command {
+ param($r)
+ try {
+ $expected = if ($null -ne $r.expectedExitCode) { [int]$r.expectedExitCode } else { 0 }
+ $p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -NonInteractive -Command `$ErrorActionPreference='Stop'; $($r.command)" -NoNewWindow -PassThru -Wait
+ $ok = ($p.ExitCode -eq $expected)
+ @{ ok=$ok; ev=("ExitCode={0} (expect {1})" -f $p.ExitCode, $expected) }
+ } catch { @{ ok=$false; ev=("Error: {0}" -f $_.Exception.Message) } }
+}
+
+function Test-ScriptBlock {
+ param($r)
+ try {
+ $sb = [ScriptBlock]::Create($r.script)
+ $res = & $sb
+ if ($null -eq $res) { return @{ ok=$false; ev="Script returned `$null (treat as SKIPPED)"; skipped=$true } }
+ $ok = [bool]$res
+ @{ ok=$ok; ev=("Script returned: {0}. Expected $true; return $null to SKIP." -f $res) }
+ } catch { @{ ok=$false; ev=("Error: {0}" -f $_.Exception.Message) } }
+}
+
+function Test-ADOptional {
+ param($r)
+ if (-not (Get-Module -ListAvailable ActiveDirectory)) {
+ return @{ ok=$false; ev="Skipped (ActiveDirectory module not found)"; skipped=$true }
+ }
+ try {
+ $sb = [ScriptBlock]::Create($r.script)
+ $res = & $sb
+ if ($null -eq $res) { return @{ ok=$false; ev="Script returned `$null (treat as SKIPPED)"; skipped=$true } }
+ $ok = [bool]$res
+ @{ ok=$ok; ev=("Script returned: {0}. Expected $true; return $null to SKIP." -f $res) }
+ } catch { @{ ok=$false; ev=("Error: {0}" -f $_.Exception.Message) } }
+}
+
+# Load rules
+$ruleFiles = Get-ChildItem -Path $RulesPath -Filter *.json -File
+$allRules = @()
+foreach ($f in $ruleFiles) {
+ $data = Get-Content $f.FullName -Raw | ConvertFrom-Json
+ if ($data -is [System.Array]) { $allRules += $data } else { $allRules += ,$data }
+}
+
+# Load profile object unless All
+$ProfileObj = $null
+if ($Profile -ne 'All') {
+ $profileFile = Join-Path $ProfilesPath ("{0}.json" -f $Profile.ToLower())
+ if (-not (Test-Path $profileFile)) { throw "Profile not found: $profileFile" }
+ $ProfileObj = Get-Content $profileFile -Raw | ConvertFrom-Json
+}
+
+# Evaluate (single level or all levels)
+$results = New-Object System.Collections.Generic.List[object]
+
+# Determine rule set
+$rules = $allRules
+if ($Profile -ne 'All' -and $ProfileObj -and ($ProfileObj.PSObject.Properties.Name -contains 'includeRuleIds') -and $ProfileObj.includeRuleIds) {
+ $rules = @()
+ foreach ($r in $allRules) { if ($ProfileObj.includeRuleIds -contains $r.id) { $rules += ,$r } }
+}
+
+$levelsToRun = if ($Profile -eq 'All') { @('ML1','ML2','ML3') } else { @($Profile) }
+$levelOrder = @{ ML1=1; ML2=2; ML3=3 }
+
+foreach ($runLevel in $levelsToRun) {
+ foreach ($rule in $rules) {
+ switch ($rule.type) {
+ 'registry' { $r = Test-Registry $rule }
+ 'file' { $r = Test-File $rule }
+ 'command' { $r = Test-Command $rule }
+ 'scriptblock' { $r = Test-ScriptBlock $rule }
+ 'ad-optional' { $r = Test-ADOptional $rule }
+ default { $r = @{ ok=$false; ev=("Unknown type {0}" -f $rule.type) } }
+ }
+
+ $applies = $true
+ if ($rule.minLevel) { $applies = ($levelOrder[$runLevel] -ge $levelOrder[$rule.minLevel]) }
+
+ if ($r.skipped) { $status = 'SKIPPED' }
+ else {
+ if (-not $applies) { $status = 'N/A' }
+ else { if ($r.ok) { $status = 'PASS' } else { $status = 'FAIL' } }
+ }
+
+ $results.Add([pscustomobject]@{
+ RuleId = $rule.id
+ Title = $rule.title
+ Strategy = $rule.strategy
+ Level = $runLevel
+ Status = $status
+ Evidence = $r.ev
+ Timestamp= (Get-Date).ToString("s")
+ })
+ }
+}
+
+# Score per level (avoid $pass name to prevent any oddities)
+$levelScores = @{}
+foreach ($lvl in $levelsToRun) {
+ $scored = $results | Where-Object { $_.Level -eq $lvl -and $_.Status -in 'PASS','FAIL' }
+ $passCount = ($scored | Where-Object { $_.Status -eq 'PASS' }).Count
+ $failCount = ($scored | Where-Object { $_.Status -eq 'FAIL' }).Count
+ $totalCount = [math]::Max(1, $scored.Count)
+ $pct = [math]::Round(100 * $passCount / $totalCount, 1)
+ $levelScores[$lvl] = @{ Pass=$passCount; Fail=$failCount; Total=$totalCount; Pct=$pct }
+ Write-Host ("E8-CAT {0} score: {1}% (PASS={2} / FAIL={3} / Total={4})" -f $lvl,$pct,$passCount,$failCount,$totalCount)
+}
+
+New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
+$ts = (Get-Date).ToString("yyyyMMdd-HHmmss")
+$jsonPath = Join-Path $OutDir ("E8CAT-{0}-{1}.json" -f $Profile,$ts)
+$csvPath = Join-Path $OutDir ("E8CAT-{0}-{1}.csv" -f $Profile,$ts)
+$htmlPath = Join-Path $OutDir ("E8CAT-{0}-{1}.html" -f $Profile,$ts)
+
+$results | ConvertTo-Json -Depth 6 | Set-Content -Path $jsonPath -Encoding UTF8
+$results | Export-Csv -NoTypeInformation -Path $csvPath -Encoding UTF8
+
+$summary = ''
+foreach ($k in ('ML1','ML2','ML3')) {
+ if ($levelScores.ContainsKey($k)) {
+ $s = $levelScores[$k]
+ $summary += ("
{0}: {1}% (PASS {2} / FAIL {3} / Total {4})" -f $k,$s.Pct,$s.Pass,$s.Fail,$s.Total)
+ }
+}
+$summary = ""
+
+$rows = $results | ForEach-Object {
+ "$($_.RuleId) | $($_.Strategy) | $($_.Title) | $($_.Level) | $($_.Status) | $($_.Evidence -replace '<','<') |
"
+} | Out-String
+
+@"
+E8-CAT $Profile
+
+
+E8-CAT - Profile $Profile
+Scores
+$summary
+RuleId | Strategy | Title | Level | Status | Evidence |
+
+$rows
+
+
+"@ | Set-Content -Path $htmlPath -Encoding UTF8
+
+Write-Host "Saved: $jsonPath"
+Write-Host "Saved: $csvPath"
+Write-Host "Saved: $htmlPath"
diff --git a/README.md b/README.md
index d17e6e2..14d2397 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,110 @@
-# E8-CAT
+# E8-CAT – Essential Eight Compliance Assessment Tool
-Essential Eight compliance assessment tool
\ No newline at end of file
+`E8-CAT` is a lightweight PowerShell-based compliance scanner, similar in spirit to CIS-CAT, designed to check Windows workstations and servers against the [ACSC Essential Eight](https://www.cyber.gov.au/acsc/view-all-content/essential-eight) hardening strategies.
+
+This build includes rules for **Maturity Levels 1–3** and can report on all levels in a single run.
+
+---
+
+## Features
+- **Profiles:** Run checks for a specific level (`ML1`, `ML2`, `ML3`) or all at once (`All`).
+- **All-level mode:** With `-Profile All`, the scanner evaluates ML1–3 in one pass and reports per-level results and scores.
+- **Per-rule applicability:** Rules know their minimum level. If they don’t apply to a level, they’re marked **N/A**.
+- **Evidence-based:** Each rule outputs evidence showing registry values, feature state, or script results.
+- **Skip logic:** If a product isn’t installed (e.g., Chrome, Edge, Firefox, IE on Win11), the rule reports **SKIPPED**.
+- **Cross-scope checks:** Registry policies are checked under both **HKLM** and **HKCU**.
+- **Output formats:** JSON, CSV, and HTML reports saved under `.\out\`.
+- **PowerShell 5.1 compatible:** Works on standard Windows builds (no modern operators like `??`).
+
+---
+
+## Usage
+```powershell
+Set-ExecutionPolicy Bypass -Scope Process -Force
+
+# Navigate into the E8-CAT folder
+Set-Location .\E8-CAT
+
+# Run all levels in one pass
+.\E8-CAT.ps1 -Profile All
+
+# Run a specific maturity level
+.\E8-CAT.ps1 -Profile ML1
+.\E8-CAT.ps1 -Profile ML2
+.\E8-CAT.ps1 -Profile ML3
+```
+
+---
+
+## Outputs
+Results are written to `.\out` with timestamped filenames:
+- **CSV** – Easy import into Excel or SIEM tools
+- **JSON** – Machine-readable for pipelines and dashboards
+- **HTML** – Human-friendly report with tables and score summaries
+
+Example output files:
+```
+.\out\E8CAT-ML1-20250902-153936.csv
+.\out\E8CAT-ML1-20250902-153936.json
+.\out\E8CAT-ML1-20250902-153936.html
+```
+
+---
+
+## Rule Coverage
+Rules are organised by strategy:
+
+- **RM – Restrict Macros:**
+ - Office macro settings (Word/Excel/PowerPoint/Outlook, Office 15.0 & 16.0)
+ - Block macros from the Internet
+ - Macro runtime AV scanning
+ - Trusted Publisher enforcement (ML3)
+
+- **AH – Application Hardening:**
+ - Internet Explorer 11 feature disabled (skips on Win11)
+ - Java browser plugin absent
+ - Microsoft Edge SmartScreen + download restrictions
+ - Chrome SafeBrowsing, download restrictions, extension blocklist
+ - Firefox enterprise policy presence
+ - Windows SmartScreen (multiple policy keys)
+
+- **AC – Application Control:**
+ - AppLocker policy present and enforced (not AuditOnly)
+ - Windows Defender Application Control (WDAC) policy present
+ - Software Restriction Policies present
+
+- **RA – Restrict Admin Privileges:**
+ - Built-in Administrator account disabled
+ - UAC (EnableLUA) enabled
+ - Local Administrator Password Solution (LAPS) policy present (Windows or legacy)
+
+---
+
+## Rule Semantics
+Rules are defined in `.\rules\*.json`. Each rule specifies:
+- `id`, `title`, `strategy`, `type`, `script` (or registry/command parameters)
+- `minLevel` (ML1, ML2, ML3)
+
+**Return values in rules:**
+- `$true` → **PASS**
+- `$false` → **FAIL**
+- `$null` → **SKIPPED**
+
+---
+
+## Profiles
+Profiles are stored under `.\profiles\ml1.json`, `ml2.json`, `ml3.json`. They contain the rule IDs included at each level.
+When running `-Profile All`, these profiles are ignored and all rules are checked, with results shown for each level.
+
+---
+
+## Example Run
+```powershell
+PS C:\E8-CAT> .\E8-CAT.ps1 -Profile All
+E8-CAT ML1 score: 78.9% (PASS=15 / FAIL=4 / Total=19)
+E8-CAT ML2 score: 65.0% (PASS=13 / FAIL=7 / Total=20)
+E8-CAT ML3 score: 42.9% (PASS=9 / FAIL=12 / Total=21)
+Saved: .\out\E8CAT-All-20250902-161413.json
+Saved: .\out\E8CAT-All-20250902-161413.csv
+Saved: .\out\E8CAT-All-20250902-161413.html
+```
diff --git a/profiles/ml1.json b/profiles/ml1.json
new file mode 100644
index 0000000..9dbc670
--- /dev/null
+++ b/profiles/ml1.json
@@ -0,0 +1,41 @@
+{
+ "name": "ML1",
+ "includeRuleIds": [
+ "RM-01-VBAWarnings-Word-160",
+ "RM-02-BlockInternet-Word-160",
+ "RM-01-VBAWarnings-Excel-160",
+ "RM-02-BlockInternet-Excel-160",
+ "RM-01-VBAWarnings-PowerPoint-160",
+ "RM-02-BlockInternet-PowerPoint-160",
+ "RM-01-VBAWarnings-Outlook-160",
+ "RM-02-BlockInternet-Outlook-160",
+ "RM-01-VBAWarnings-Word-150",
+ "RM-02-BlockInternet-Word-150",
+ "RM-01-VBAWarnings-Excel-150",
+ "RM-02-BlockInternet-Excel-150",
+ "RM-01-VBAWarnings-PowerPoint-150",
+ "RM-02-BlockInternet-PowerPoint-150",
+ "RM-01-VBAWarnings-Outlook-150",
+ "RM-02-BlockInternet-Outlook-150",
+ "RM-03-MacroRuntimeScan-160",
+ "RM-03-MacroRuntimeScan-150",
+ "RM-TRUSTED-PUBLISHERS-160",
+ "RM-TRUSTED-PUBLISHERS-150",
+ "AH-IE11-Feature-Disabled",
+ "AH-Java-Plugin-Absent",
+ "AH-Edge-SmartScreen",
+ "AH-Edge-DownloadRestrictions",
+ "AH-Chrome-SafeBrowsing",
+ "AH-Chrome-DownloadRestrictions",
+ "AH-Chrome-Ext-Blocklist",
+ "AH-Firefox-PolicyKey",
+ "AH-Windows-SmartScreen",
+ "AC-01-AppLocker",
+ "AC-AppLocker-Enforced",
+ "AC-02-WDAC",
+ "AC-03-SRP",
+ "RA-Local-Administrator-Disabled",
+ "RA-UAC-Enabled",
+ "RA-LAPS-PolicyPresent"
+ ]
+}
\ No newline at end of file
diff --git a/profiles/ml2.json b/profiles/ml2.json
new file mode 100644
index 0000000..22ec9b7
--- /dev/null
+++ b/profiles/ml2.json
@@ -0,0 +1,41 @@
+{
+ "name": "ML2",
+ "includeRuleIds": [
+ "RM-01-VBAWarnings-Word-160",
+ "RM-02-BlockInternet-Word-160",
+ "RM-01-VBAWarnings-Excel-160",
+ "RM-02-BlockInternet-Excel-160",
+ "RM-01-VBAWarnings-PowerPoint-160",
+ "RM-02-BlockInternet-PowerPoint-160",
+ "RM-01-VBAWarnings-Outlook-160",
+ "RM-02-BlockInternet-Outlook-160",
+ "RM-01-VBAWarnings-Word-150",
+ "RM-02-BlockInternet-Word-150",
+ "RM-01-VBAWarnings-Excel-150",
+ "RM-02-BlockInternet-Excel-150",
+ "RM-01-VBAWarnings-PowerPoint-150",
+ "RM-02-BlockInternet-PowerPoint-150",
+ "RM-01-VBAWarnings-Outlook-150",
+ "RM-02-BlockInternet-Outlook-150",
+ "RM-03-MacroRuntimeScan-160",
+ "RM-03-MacroRuntimeScan-150",
+ "RM-TRUSTED-PUBLISHERS-160",
+ "RM-TRUSTED-PUBLISHERS-150",
+ "AH-IE11-Feature-Disabled",
+ "AH-Java-Plugin-Absent",
+ "AH-Edge-SmartScreen",
+ "AH-Edge-DownloadRestrictions",
+ "AH-Chrome-SafeBrowsing",
+ "AH-Chrome-DownloadRestrictions",
+ "AH-Chrome-Ext-Blocklist",
+ "AH-Firefox-PolicyKey",
+ "AH-Windows-SmartScreen",
+ "AC-01-AppLocker",
+ "AC-AppLocker-Enforced",
+ "AC-02-WDAC",
+ "AC-03-SRP",
+ "RA-Local-Administrator-Disabled",
+ "RA-UAC-Enabled",
+ "RA-LAPS-PolicyPresent"
+ ]
+}
\ No newline at end of file
diff --git a/profiles/ml3.json b/profiles/ml3.json
new file mode 100644
index 0000000..dc03c28
--- /dev/null
+++ b/profiles/ml3.json
@@ -0,0 +1,41 @@
+{
+ "name": "ML3",
+ "includeRuleIds": [
+ "RM-01-VBAWarnings-Word-160",
+ "RM-02-BlockInternet-Word-160",
+ "RM-01-VBAWarnings-Excel-160",
+ "RM-02-BlockInternet-Excel-160",
+ "RM-01-VBAWarnings-PowerPoint-160",
+ "RM-02-BlockInternet-PowerPoint-160",
+ "RM-01-VBAWarnings-Outlook-160",
+ "RM-02-BlockInternet-Outlook-160",
+ "RM-01-VBAWarnings-Word-150",
+ "RM-02-BlockInternet-Word-150",
+ "RM-01-VBAWarnings-Excel-150",
+ "RM-02-BlockInternet-Excel-150",
+ "RM-01-VBAWarnings-PowerPoint-150",
+ "RM-02-BlockInternet-PowerPoint-150",
+ "RM-01-VBAWarnings-Outlook-150",
+ "RM-02-BlockInternet-Outlook-150",
+ "RM-03-MacroRuntimeScan-160",
+ "RM-03-MacroRuntimeScan-150",
+ "RM-TRUSTED-PUBLISHERS-160",
+ "RM-TRUSTED-PUBLISHERS-150",
+ "AH-IE11-Feature-Disabled",
+ "AH-Java-Plugin-Absent",
+ "AH-Edge-SmartScreen",
+ "AH-Edge-DownloadRestrictions",
+ "AH-Chrome-SafeBrowsing",
+ "AH-Chrome-DownloadRestrictions",
+ "AH-Chrome-Ext-Blocklist",
+ "AH-Firefox-PolicyKey",
+ "AH-Windows-SmartScreen",
+ "AC-01-AppLocker",
+ "AC-AppLocker-Enforced",
+ "AC-02-WDAC",
+ "AC-03-SRP",
+ "RA-Local-Administrator-Disabled",
+ "RA-UAC-Enabled",
+ "RA-LAPS-PolicyPresent"
+ ]
+}
\ No newline at end of file
diff --git a/rules/ac.json b/rules/ac.json
new file mode 100644
index 0000000..e1481f9
--- /dev/null
+++ b/rules/ac.json
@@ -0,0 +1,34 @@
+[
+ {
+ "id": "AC-01-AppLocker",
+ "title": "AppLocker policy present",
+ "strategy": "AC",
+ "type": "scriptblock",
+ "script": "try { (Get-AppLockerPolicy -Effective).RuleCollections.Count -gt 0 } catch { $false }",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AC-AppLocker-Enforced",
+ "title": "AppLocker enforcement not AuditOnly for at least one collection",
+ "strategy": "AC",
+ "type": "scriptblock",
+ "script": "(Get-AppLockerPolicy -Effective -ErrorAction SilentlyContinue).RuleCollections | Where-Object { $_.EnforcementMode -and $_.EnforcementMode -ne 'AuditOnly' } | Measure-Object | ForEach-Object { $_.Count -gt 0 }",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AC-02-WDAC",
+ "title": "WDAC policy key present",
+ "strategy": "AC",
+ "type": "scriptblock",
+ "script": "Test-Path 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\CI\\Policy'",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AC-03-SRP",
+ "title": "Software Restriction Policies present",
+ "strategy": "AC",
+ "type": "scriptblock",
+ "script": "Test-Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers'",
+ "minLevel": "ML1"
+ }
+]
\ No newline at end of file
diff --git a/rules/ah.json b/rules/ah.json
new file mode 100644
index 0000000..7fa6693
--- /dev/null
+++ b/rules/ah.json
@@ -0,0 +1,74 @@
+[
+ {
+ "id": "AH-IE11-Feature-Disabled",
+ "title": "Internet Explorer 11 feature is disabled/removed (skip on Win11)",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$f=Get-WindowsOptionalFeature -Online -FeatureName Internet-Explorer-Optional-amd64 -ErrorAction SilentlyContinue; if($null -eq $f){ return $null }; $f.State -in @('Disabled','Removed')",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Java-Plugin-Absent",
+ "title": "Legacy Java browser plugin not present",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "if( (Test-Path 'HKLM:\\SOFTWARE\\JavaSoft\\Java Plug-in') -or (Test-Path 'HKLM:\\SOFTWARE\\Oracle\\JavaDeploy\\WebDeployJava') ){ return $false } else { return $true }",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Edge-SmartScreen",
+ "title": "Microsoft Edge SmartScreen enabled via policy",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$edge=@('C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe','C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe') | Where-Object {Test-Path $_}; if(-not $edge){ return $null }; $keys=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Edge','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Edge','HKLM:\\SOFTWARE\\Microsoft\\PolicyManager\\current\\device\\Browser'); foreach($k in $keys){ if(Test-Path $k){ try{ $val=(Get-ItemProperty -Path $k -ErrorAction Stop).SmartScreenEnabled; if($null -ne $val -and [int]$val -ge 1){ return $true } } catch{} } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Edge-DownloadRestrictions",
+ "title": "Edge download restrictions present",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$edge=@('C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe','C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe') | Where-Object {Test-Path $_}; if(-not $edge){ return $null }; foreach($k in @('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Edge','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Edge')){ if(Test-Path $k){ $v=(Get-ItemProperty -Path $k -ErrorAction SilentlyContinue).DownloadRestrictions; if($null -ne $v -and [int]$v -ge 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Chrome-SafeBrowsing",
+ "title": "Chrome SafeBrowsing policy configured",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$chrome=@('C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe','C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe') | Where-Object {Test-Path $_}; if(-not $chrome){ return $null }; foreach($k in @('HKLM:\\SOFTWARE\\Policies\\Google\\Chrome','HKCU:\\SOFTWARE\\Policies\\Google\\Chrome')){ if(Test-Path $k){ $v=(Get-ItemProperty -Path $k -ErrorAction SilentlyContinue).SafeBrowsingProtectionLevel; if($null -ne $v -and [int]$v -ge 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Chrome-DownloadRestrictions",
+ "title": "Chrome download restrictions present",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$chrome=@('C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe','C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe') | Where-Object {Test-Path $_}; if(-not $chrome){ return $null }; foreach($k in @('HKLM:\\SOFTWARE\\Policies\\Google\\Chrome','HKCU:\\SOFTWARE\\Policies\\Google\\Chrome')){ if(Test-Path $k){ $v=(Get-ItemProperty -Path $k -ErrorAction SilentlyContinue).DownloadRestrictions; if($null -ne $v -and [int]$v -ge 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Chrome-Ext-Blocklist",
+ "title": "Chrome extension install blocklist configured",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "if(-not (Test-Path 'HKLM:\\SOFTWARE\\Policies\\Google\\Chrome') -and -not (Test-Path 'HKCU:\\SOFTWARE\\Policies\\Google\\Chrome')){ return $null } $k='HKLM:\\SOFTWARE\\Policies\\Google\\Chrome\\ExtensionInstallBlocklist'; $k2='HKCU:\\SOFTWARE\\Policies\\Google\\Chrome\\ExtensionInstallBlocklist'; if(Test-Path $k -or Test-Path $k2){ return $true } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Firefox-PolicyKey",
+ "title": "Firefox enterprise policies present",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$ff=@('C:\\Program Files\\Mozilla Firefox\\firefox.exe','C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe') | Where-Object {Test-Path $_}; if(-not $ff){ return $null }; if( (Test-Path 'HKLM:\\SOFTWARE\\Policies\\Mozilla\\Firefox') -or (Test-Path 'HKCU:\\SOFTWARE\\Policies\\Mozilla\\Firefox') ){ return $true } else { return $false }",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "AH-Windows-SmartScreen",
+ "title": "Windows SmartScreen enabled by policy",
+ "strategy": "AH",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\System','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\System','HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Explorer','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Explorer'); foreach($p in $paths){ if(Test-Path $p){ $prop=Get-ItemProperty -Path $p -ErrorAction SilentlyContinue; if($null -ne $prop.EnableSmartScreen -and [int]$prop.EnableSmartScreen -eq 1){ return $true }; if($null -ne $prop.SmartScreenEnabled -and [int]$prop.SmartScreenEnabled -ge 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ }
+]
\ No newline at end of file
diff --git a/rules/ra.json b/rules/ra.json
new file mode 100644
index 0000000..f4edc72
--- /dev/null
+++ b/rules/ra.json
@@ -0,0 +1,26 @@
+[
+ {
+ "id": "RA-Local-Administrator-Disabled",
+ "title": "Built-in Administrator account is disabled",
+ "strategy": "RA",
+ "type": "scriptblock",
+ "script": "$u=Get-LocalUser -Name 'Administrator' -ErrorAction SilentlyContinue; if($null -eq $u){ return $true }; return (-not $u.Enabled)",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RA-UAC-Enabled",
+ "title": "User Account Control (EnableLUA) enabled",
+ "strategy": "RA",
+ "type": "scriptblock",
+ "script": "try { $v=(Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' -ErrorAction Stop).EnableLUA; if($null -ne $v -and [int]$v -eq 1){ return $true } else { return $false } } catch { return $false }",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RA-LAPS-PolicyPresent",
+ "title": "Windows LAPS (or legacy LAPS) policy present",
+ "strategy": "RA",
+ "type": "scriptblock",
+ "script": "if( (Test-Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\LAPS') -or (Test-Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft Services\\AdmPwd') ){ return $true } else { return $false }",
+ "minLevel": "ML1"
+ }
+]
\ No newline at end of file
diff --git a/rules/rm.json b/rules/rm.json
new file mode 100644
index 0000000..42e39d6
--- /dev/null
+++ b/rules/rm.json
@@ -0,0 +1,162 @@
+[
+ {
+ "id": "RM-01-VBAWarnings-Word-160",
+ "title": "Word macros disabled by policy (VBAWarnings=4) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Word\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Word\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Word-160",
+ "title": "Word: block macros from Internet (blockcontentexecutionfrominternet=1) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Word\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Word\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-Excel-160",
+ "title": "Excel macros disabled by policy (VBAWarnings=4) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Excel\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Excel\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Excel-160",
+ "title": "Excel: block macros from Internet (blockcontentexecutionfrominternet=1) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Excel\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Excel\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-PowerPoint-160",
+ "title": "PowerPoint macros disabled by policy (VBAWarnings=4) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\PowerPoint\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\PowerPoint\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-PowerPoint-160",
+ "title": "PowerPoint: block macros from Internet (blockcontentexecutionfrominternet=1) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\PowerPoint\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\PowerPoint\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-Outlook-160",
+ "title": "Outlook macros disabled by policy (VBAWarnings=4) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Outlook\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Outlook\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Outlook-160",
+ "title": "Outlook: block macros from Internet (blockcontentexecutionfrominternet=1) Office 16.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Outlook\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Outlook\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-Word-150",
+ "title": "Word macros disabled by policy (VBAWarnings=4) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Word\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Word\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Word-150",
+ "title": "Word: block macros from Internet (blockcontentexecutionfrominternet=1) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Word\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Word\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-Excel-150",
+ "title": "Excel macros disabled by policy (VBAWarnings=4) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Excel\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Excel\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Excel-150",
+ "title": "Excel: block macros from Internet (blockcontentexecutionfrominternet=1) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Excel\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Excel\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-PowerPoint-150",
+ "title": "PowerPoint macros disabled by policy (VBAWarnings=4) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\PowerPoint\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\PowerPoint\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-PowerPoint-150",
+ "title": "PowerPoint: block macros from Internet (blockcontentexecutionfrominternet=1) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\PowerPoint\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\PowerPoint\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-01-VBAWarnings-Outlook-150",
+ "title": "Outlook macros disabled by policy (VBAWarnings=4) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Outlook\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Outlook\\Security'); foreach($p in $paths){} foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).VBAWarnings; if($null -ne $v -and [int]$v -eq 4){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-02-BlockInternet-Outlook-150",
+ "title": "Outlook: block macros from Internet (blockcontentexecutionfrominternet=1) Office 15.0",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Outlook\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Outlook\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).blockcontentexecutionfrominternet; if($null -ne $v -and [int]$v -eq 1){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-03-MacroRuntimeScan-160",
+ "title": "Macro runtime AV scanning configured (Office 16.0 common security)",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Common\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Common\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).MacroRuntimeScanScope; if($null -ne $v -and @('1','2') -contains ([string]$v)){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-03-MacroRuntimeScan-150",
+ "title": "Macro runtime AV scanning configured (Office 15.0 common security)",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$paths=@('HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Common\\Security','HKCU:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Common\\Security'); foreach($p in $paths){ if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).MacroRuntimeScanScope; if($null -ne $v -and @('1','2') -contains ([string]$v)){ return $true } } } $false",
+ "minLevel": "ML1"
+ },
+ {
+ "id": "RM-TRUSTED-PUBLISHERS-160",
+ "title": "Trusted Publishers enforcement present (Office 16.0)",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$p='HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\16.0\\Common\\Security'; if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).TrustedPublisher; if($null -ne $v -and [int]$v -eq 1){ return $true } } $false",
+ "minLevel": "ML3"
+ },
+ {
+ "id": "RM-TRUSTED-PUBLISHERS-150",
+ "title": "Trusted Publishers enforcement present (Office 15.0)",
+ "strategy": "RM",
+ "type": "scriptblock",
+ "script": "$p='HKLM:\\SOFTWARE\\Policies\\Microsoft\\Office\\15.0\\Common\\Security'; if(Test-Path $p){ $v=(Get-ItemProperty -Path $p -ErrorAction SilentlyContinue).TrustedPublisher; if($null -ne $v -and [int]$v -eq 1){ return $true } } $false",
+ "minLevel": "ML3"
+ }
+]
\ No newline at end of file