Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# ShellKnight Changelog

## [v2026.09.25.003] - 2026-09-25

- **OS end of life is Microsoft's date for the build and the edition:** the Assessment Engine looked up `os_eol` by build number only, with one date per build, and several dates were years past Microsoft's. 19045 (Windows 10 22H2) read 2030-10-14 for 2025-10-14; 22621 and 22631 (Windows 11 22H2 and 23H2) read 2027-10-12 and 2028-10-10, later than even their Enterprise dates; 26100 read 2029-10-14. One date per build also cannot be right: Home/Pro and Enterprise/Education reach end of servicing on different days, and 14393, 17763, 19044 and 26100 are also LTSB/LTSC releases or Windows Server 2016/2019/2025, which run for years longer. The new `Get-OsEolDate` takes the edition family from `Win32_OperatingSystem.Caption` (Home/Pro, Enterprise/Education, LTSB/LTSC, IoT Enterprise LTSC, Server) and holds every date from Microsoft Learn's release-health and lifecycle pages. A caption it cannot place, such as a localized one, gets a date only when that date holds for every edition the machine could be; otherwise `os_eol` is `Unknown`, which is not scored (ADR 0009). New builds: 25398 (Server 23H2), 26200 (Windows 11 25H2) and 28000 (Windows 11 26H1). `os_eol` keeps its three forms, so Battlefield needs no change.
- **Windows 10 ESU does not extend end of life (ADR 0010):** a Windows 10 device reports `END OF LIFE (since 2025-10-14)` and takes the -20 whether or not it is enrolled in Extended Security Updates. Microsoft's end of support is 2025-10-14. ESU is a per-device licence that ShellKnight cannot see for consumer or cloud-granted enrolments, and commercial Year 1 ends on 2026-10-13. The ADR records the reasoning and when to revisit it.
- **Scoring change, downward for most devices it touches:** from the first run of this version the OS EOL -20 also applies to every Windows 10 22H2 and Windows 11 22H2 device, to Windows 11 23H2 Home/Pro, to the GA-channel releases of Windows 10 21H2, 1809 and 1607 and of Windows 11 21H2, and to Windows Server 23H2 (build 25398, ended 2025-10-24), which the old table did not know and so never scored. On 2026-10-13 it reaches Windows 11 24H2 Home/Pro and Windows 10 2016 LTSB; on 2026-11-10, Windows 11 23H2 Enterprise/Education. Windows 10 Enterprise LTSC 2021 and IoT Enterprise LTSC 2021 keep their later dates (2027-01-12 and 2032-01-13), where the old table would have taken 20 points from them on 2026-10-13. Nothing changed on the endpoints.
- **Regression test:** new `tests/Test-OsEol.ps1` runs `Get-OsEolDate` against Microsoft's date for every build and edition family with real captions; it also runs the unplaced-caption rule and the engine's `os_eol` lines with the -20 rule, with the clock pinned either side of an end date. It checks that every date is a Patch Tuesday and that no build the old table knew is dropped. `Test-EngineScope.ps1` adds a Pro/LTSC pair on the same build through the whole engine, and its healthy fixture moves to a build supported until 2034. `Test-EngineScope.ps1` and `Test-DeviceIdentity.ps1` load the new function.

## [v2026.09.25.002] - 2026-09-25

- **An unknown password minimum length is no longer scored or reported as 0:** `$Script:MinPasswordLen` started at 0, and only the Assessment Engine's 'Password policy' check set it, by parsing `net accounts`. When the engine aborted or was disabled, or `net accounts` gave no 'Minimum password length' value, the scoring took 20 points and the CIS Benchmark block added the High finding `Password minimum length is 0 (CIS 1.1.1)`. Battlefield raises an alert for every High finding and maps that title to the VULN `password-policy-blank`, so a collection failure was scored and alerted as a vulnerability, which ADR 0009 rules out. The value now starts at `$null`. The scoring and the CIS 1.1.1 check skip it when it is `$null`, and the log says the length is unknown. A length that was read, including a real 0, is scored and reported exactly as before.
Expand Down
8 changes: 8 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ The per-device score ShellKnight computes during a Run, 0 to 100, published on t
a letter grade. Scoped to one machine and one Run. It is NOT the number the customer sees in a
report; that is the Tenant Security Score.

### OS End of Life

The date Microsoft stops servicing a device's Windows release, for that build AND edition: Home/Pro,
Enterprise/Education, LTSB/LTSC, IoT Enterprise LTSC or Server. Reported as `os_eol`; once passed,
it costs the Device Security Score 20 points. It is Microsoft's end-of-servicing date. It is NOT
moved by an Extended Security Updates licence: Windows 10 is end of life from 2025-10-14 with or
without ESU. See [ADR 0010](docs/adr/0010-os-end-of-life-by-edition.md).

### Tenant Security Score

The single customer-level number, 0 to 100 internally, presented to the customer as a letter
Expand Down
123 changes: 99 additions & 24 deletions ShellKnight.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#Requires -RunAsAdministrator
<#
.SYNOPSIS
ShellKnight v2026.09.25.002 - Enterprise Endpoint Security & Remediation Tool
ShellKnight v2026.09.25.003 - Enterprise Endpoint Security & Remediation Tool

.DESCRIPTION
Automated endpoint security remediation, threat detection, hardening, and
Expand All @@ -18,9 +18,9 @@
C. David Burgess - PTech LLC

.VERSION
Version : v2026.09.25.002
Version : v2026.09.25.003
Released : 2026-09-25
Prior : v2026.09.25.001
Prior : v2026.09.25.002

.ENGINES
Phase 1 - Intel Engine : Threat intelligence download and cache
Expand All @@ -33,6 +33,30 @@
Phase 8 - Reporting Engine : Reporting, trending, and extended checks

.CHANGELOG
v2026.09.25.003 - OS end of life is Microsoft's date for the build AND the
edition. The engine looked it up by build number only, one date
per build, and several were years late: 19045 (Windows 10 22H2)
read 2030-10-14 for 2025-10-14, 22621 and 22631 (Windows 11 22H2
and 23H2) read 2027 and 2028, 26100 read 2029-10-14. And a build
is often several products: Home/Pro and Enterprise/Education end
on different days, and 14393, 17763, 19044 and 26100 are also
LTSB/LTSC or Windows Server, which run for years longer. New
Get-OsEolDate takes the edition from the caption (Home/Pro,
Enterprise/Education, LTSB/LTSC, IoT LTSC, Server) and holds every
date from Microsoft Learn. A caption it cannot place (localized,
say) gets a date only if it holds for every edition the machine
could be; otherwise 'Unknown', which is not scored (ADR 0009).
New builds: 25398 (Server 23H2), 26200 (Windows 11 25H2) and
28000 (26H1). os_eol keeps its three forms.
Windows 10 ESU does not extend end of life: a Windows 10 device is
END OF LIFE since 2025-10-14, enrolled or not (ADR 0010).
SCORING CHANGE. More devices take the OS EOL -20 from the first
run: Windows 10 22H2 and 11 22H2 (all editions), 11 23H2 Home/Pro,
the GA releases of 10 21H2, 1809, 1607 and 11 21H2, and Server
23H2 (25398, which the old table did not know). Windows 11
24H2 Home/Pro follows on 2026-10-13 and 23H2 Enterprise/Education
on 2026-11-10. Enterprise LTSC 2021 and IoT LTSC keep their later
dates, where the old table would have ended them on 2026-10-13.
v2026.09.25.002 - An unknown password minimum length is no longer scored or
reported as 0. $Script:MinPasswordLen started at 0, and only the
engine's 'Password policy' check set it, from 'net accounts'. So
Expand Down Expand Up @@ -473,7 +497,7 @@


# ==============================================================================
# SHELLKNIGHT v2026.09.25.002 CONFIGURATION
# SHELLKNIGHT v2026.09.25.003 CONFIGURATION
# All settings are configured here. No external config files required.
# Each engine can be independently enabled or disabled.
# ==============================================================================
Expand Down Expand Up @@ -633,7 +657,7 @@
if ($cfg.ScheduleHours) { $SK_ScheduleHours = [int]$cfg.ScheduleHours }
if ($null -ne $cfg.SelfSchedule) { $SK_SelfSchedule = [bool]$cfg.SelfSchedule }
if ($cfg.SiteName) { $SK_SiteName = $cfg.SiteName }
} catch { }

Check warning on line 660 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 660 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}

# Environment-variable overrides (bootstrap via Datto sets these; env wins)
Expand Down Expand Up @@ -662,11 +686,11 @@
# back to the hardcoded IOC list (review finding 6b; field hit 2026-07-03).
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
} catch { }

Check warning on line 689 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 689 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

# Runtime Config Object - single source of truth for all engines
$Script:Config = [PSCustomObject]@{
Version = 'v2026.09.25.002'
Version = 'v2026.09.25.003'
# Intel Engine
IntelEngine_Enabled = $SK_IntelEngine_Enabled
IntelEngine_CheckUpdates = $SK_IntelEngine_CheckForUpdates
Expand Down Expand Up @@ -949,11 +973,75 @@
# ParseExact rejects against 'yyyyMMdd', so the legacy path was broken too.)
$s = [string]$ReleaseDate
if ($s.Length -ge 8) {
try { return [datetime]::ParseExact($s.Substring(0, 8), 'yyyyMMdd', $null) } catch { }

Check warning on line 976 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 976 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}
return (Get-Date) # unknown age; scores treat this as a new machine
}

# Microsoft's end-of-servicing date for this Windows build AND edition, or
# $null when it is not known. The build alone is not enough: Home/Pro and
# Enterprise/Education reach end of servicing on different days, and 7601,
# 9200, 9600, 10240, 14393, 17763, 19044 and 26100 are each several products
# (client GA, LTSB/LTSC, IoT LTSC, Server) with dates years apart. Until
# v2026.09.25.003 the table was keyed by build only and used one date per
# build, some of them years past Microsoft's (19045 read 2030 for 2025).
# Dates are the last Patch Tuesday of servicing, from Microsoft Learn release
# health and the lifecycle pages (whose tables show the next day, 6:59:59 AM).
# GA versions: end of servicing. LTSB/LTSC and Server: end of extended
# support. Windows 10 ESU does not extend a date (ADR 0010).
# The edition comes from the English caption. A caption this cannot place
# (localized, or an edition not listed for the build) gets a date only when it
# holds for every edition the machine could be: all past, or all the same.
# Otherwise $null ('Unknown'), which is not scored (ADR 0009).
function Get-OsEolDate {
param([string]$Caption, [string]$Build, [datetime]$Now = (Get-Date))
# Order matters: an LTSC caption also says Enterprise, and 'Pro Education'
# is on the Home/Pro timeline, so Pro is tested before Education.
$family = if ($Caption -match 'Server') { 'Server' }
elseif ($Caption -match 'LTS[BC]') { if ($Caption -match 'IoT') { 'IoTLTSC' } else { 'LTSC' } }
elseif ($Caption -match '\bPro\b|\bHome\b|\bSE\b') { 'HomePro' }
elseif ($Caption -match 'Enterprise|Education') { 'EntEdu' }
else { $null }
# HomePro: Home, Pro, Pro Education, Pro for Workstations, SE.
# EntEdu: Enterprise, Education, IoT Enterprise (GA), Enterprise multi-session.
$eol = @{
'7601' = @{ HomePro = '2020-01-14'; EntEdu = '2020-01-14'; Server = '2020-01-14' } # Windows 7 SP1 / Server 2008 R2
'9200' = @{ HomePro = '2016-01-12'; EntEdu = '2016-01-12'; Server = '2023-10-10' } # Windows 8 / Server 2012
'9600' = @{ HomePro = '2023-01-10'; EntEdu = '2023-01-10'; Server = '2023-10-10' } # Windows 8.1 / Server 2012 R2
'10240' = @{ HomePro = '2017-05-09'; EntEdu = '2017-05-09'; LTSC = '2025-10-14' } # 10 1507 / 2015 LTSB
'10586' = @{ HomePro = '2017-10-10'; EntEdu = '2017-10-10' } # 10 1511
'14393' = @{ HomePro = '2018-04-10'; EntEdu = '2019-04-09'; LTSC = '2026-10-13'; IoTLTSC = '2026-10-13'; Server = '2027-01-12' } # 10 1607 / 2016 LTSB / Server 2016
'15063' = @{ HomePro = '2018-10-09'; EntEdu = '2019-10-08' } # 10 1703
'16299' = @{ HomePro = '2019-04-09'; EntEdu = '2020-10-13' } # 10 1709
'17134' = @{ HomePro = '2019-11-12'; EntEdu = '2021-05-11' } # 10 1803
'17763' = @{ HomePro = '2020-11-10'; EntEdu = '2021-05-11'; LTSC = '2029-01-09'; IoTLTSC = '2029-01-09'; Server = '2029-01-09' } # 10 1809 / LTSC 2019 / Server 2019
'18362' = @{ HomePro = '2020-12-08'; EntEdu = '2020-12-08' } # 10 1903
'18363' = @{ HomePro = '2021-05-11'; EntEdu = '2022-05-10' } # 10 1909
'19041' = @{ HomePro = '2021-12-14'; EntEdu = '2021-12-14' } # 10 2004
'19042' = @{ HomePro = '2022-05-10'; EntEdu = '2023-05-09' } # 10 20H2
'19043' = @{ HomePro = '2022-12-13'; EntEdu = '2022-12-13' } # 10 21H1
'19044' = @{ HomePro = '2023-06-13'; EntEdu = '2024-06-11'; LTSC = '2027-01-12'; IoTLTSC = '2032-01-13' } # 10 21H2 / LTSC 2021
'19045' = @{ HomePro = '2025-10-14'; EntEdu = '2025-10-14' } # 10 22H2 (ESU: ADR 0010)
'20348' = @{ Server = '2031-10-14' } # Server 2022
'22000' = @{ HomePro = '2023-10-10'; EntEdu = '2024-10-08' } # 11 21H2
'22621' = @{ HomePro = '2024-10-08'; EntEdu = '2025-10-14' } # 11 22H2
'22631' = @{ HomePro = '2025-11-11'; EntEdu = '2026-11-10' } # 11 23H2
'25398' = @{ Server = '2025-10-24' } # Server 23H2 (Annual Channel)
'26100' = @{ HomePro = '2026-10-13'; EntEdu = '2027-10-12'; LTSC = '2029-10-09'; IoTLTSC = '2034-10-10'; Server = '2034-11-14' } # 11 24H2 / LTSC 2024 / Server 2025
'26200' = @{ HomePro = '2027-10-12'; EntEdu = '2028-10-10' } # 11 25H2
'28000' = @{ HomePro = '2028-03-14'; EntEdu = '2029-03-13' } # 11 26H1
}
$row = $eol[$Build]
if (-not $row) { return $null }
if ($family -and $row.ContainsKey($family)) { return [datetime]$row[$family] }
# Not placed: every date this machine could have, on its side of the
# client/server line (drawn as the engine draws HWInfo.IsServer).
$isServer = $Caption -match 'Server'
$dates = @($row.Keys | Where-Object { ($_ -eq 'Server') -eq $isServer } | ForEach-Object { [datetime]$row[$_] } | Sort-Object)
if ($dates.Count -and ($Now -gt $dates[-1] -or $dates[0] -eq $dates[-1])) { return $dates[-1] }
return $null
}

# Get folder size in bytes
function Get-FolderSizeBytes {
param([string]$Path)
Expand Down Expand Up @@ -1008,7 +1096,7 @@
}
}
}
} catch { } # denied dir: skip it, continue with the rest of the stack

Check warning on line 1099 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1099 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}
$sizes[$profileName] = $total
}
Expand All @@ -1025,7 +1113,7 @@
$beforeBytes = ($before | Measure-Object -Property Length -Sum).Sum
$removed = 0
foreach ($f in $before) {
try { Remove-Item -LiteralPath $f.FullName -Force -ErrorAction Stop; $removed++ } catch { }

Check warning on line 1116 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1116 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}
if ($removed -gt 0) {
$freedMB = [math]::Round($beforeBytes / 1MB, 1)
Expand Down Expand Up @@ -1056,7 +1144,7 @@

# Banner
$bannerWidth = 78
$version = 'ShellKnight v2026.09.25.002'
$version = 'ShellKnight v2026.09.25.003'
$hostname = $env:COMPUTERNAME
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$psver = "PS $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
Expand Down Expand Up @@ -1131,7 +1219,7 @@
& schtasks.exe /Create /TN 'ShellKnight' /TR $action /SC HOURLY /MO $SK_ScheduleHours `
/ST $startTime /RU 'SYSTEM' /RL HIGHEST /F 2>$null | Out-Null
$Script:Health.task_ensured = ($LASTEXITCODE -eq 0)
try { $Script:Health.next_run = (Get-ScheduledTaskInfo -TaskName 'ShellKnight' -ErrorAction Stop).NextRunTime.ToString('o') } catch {}

Check warning on line 1222 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1222 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
Log-Info "Self-schedule ensured: every $SK_ScheduleHours h at :$startTime (SYSTEM)"
} catch { Log-Warn "Self-schedule failed: $($_.Exception.Message)" }
}
Expand Down Expand Up @@ -1294,7 +1382,7 @@
try { $null = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop } catch { $wmiUp = $false }
}
if (-not $deviceId -and $wmiUp) {
try { $deviceId = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Cryptography' -Name MachineGuid -ErrorAction Stop).MachineGuid } catch { }

Check warning on line 1385 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1385 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}
if ($deviceId) { $Script:DeviceId = $deviceId }
}
Expand Down Expand Up @@ -1342,24 +1430,11 @@
$blWmi = Get-CimInstance -Namespace 'Root\CIMV2\Security\MicrosoftVolumeEncryption' `
-ClassName 'Win32_EncryptableVolume' -Filter "DriveLetter='C:'" -ErrorAction Stop
$blStatus = if ($blWmi.ProtectionStatus -eq 1) { 'On' } else { 'Off'; $Script:BitLockerWarn = $true }
} catch { }

Check warning on line 1433 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1433 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
}

# OS EOL check
$eolDates = @{
'7601' = [datetime]'2020-01-14'; '9200' = [datetime]'2023-10-10'
'9600' = [datetime]'2023-10-10'; '10240'= [datetime]'2025-10-14'
'10586' = [datetime]'2017-10-10'; '14393'= [datetime]'2027-01-12'
'15063' = [datetime]'2018-10-09'; '16299'= [datetime]'2019-04-09'
'17134' = [datetime]'2019-11-12'; '17763'= [datetime]'2029-01-09'
'18362' = [datetime]'2020-05-12'; '18363'= [datetime]'2021-05-11'
'19041' = [datetime]'2025-10-14'; '19042'= [datetime]'2025-10-14'
'19043' = [datetime]'2025-10-14'; '19044'= [datetime]'2026-10-13'
'19045' = [datetime]'2030-10-14'; '20348'= [datetime]'2031-10-14'
'22000' = [datetime]'2026-10-14'; '22621'= [datetime]'2027-10-12'
'22631' = [datetime]'2028-10-10'; '26100'= [datetime]'2029-10-14'
}
$eolDate = $eolDates[$osBuild]
# OS EOL check: Microsoft's date for this build and edition
$eolDate = Get-OsEolDate -Caption $osName -Build $osBuild
$eolStr = if ($eolDate) {
if ((Get-Date) -gt $eolDate) { $Script:OsEolWarn = $true; "END OF LIFE (since $($eolDate.ToString('yyyy-MM-dd')))"}
else { "Supported until $($eolDate.ToString('yyyy-MM-dd'))" }
Expand All @@ -1381,7 +1456,7 @@
if ($avName -match 'Windows Defender|Microsoft Defender') { $defenderRegistered = $true }
else { $avProducts.Add($avName) }
}
} catch { }

Check warning on line 1459 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1459 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

# Datto AV (registered AV product; RMM handled elsewhere)
if (Get-Service -Name 'EndpointProtectionService2' -ErrorAction SilentlyContinue) {
Expand Down Expand Up @@ -1453,7 +1528,7 @@
try {
$disableRtp = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection' `
-Name 'DisableRealtimeMonitoring' -ErrorAction Stop).DisableRealtimeMonitoring
} catch { }

Check warning on line 1531 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.

Check warning on line 1531 in ShellKnight.ps1

View workflow job for this annotation

GitHub Actions / validate

[PSAvoidUsingEmptyCatchBlock] Empty catch block is used. Please use Write-Error or throw statements in catch blocks.
$defRtp = ($wd.Status -eq 'Running' -and $disableRtp -ne 1)
}
}
Expand Down Expand Up @@ -3397,7 +3472,7 @@
$sepLine = '=' * 80

Log-Info $sepLine
Log-Info " ShellKnight v2026.09.25.002 - Report"
Log-Info " ShellKnight v2026.09.25.003 - Report"
Log-Info " Hostname : $($env:COMPUTERNAME)"
Log-Info " Run Date : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Log-Info " Runtime : $runtime seconds"
Expand All @@ -3410,7 +3485,7 @@
$bannerWidth2 = 78
Write-Host ''
Write-Host " $sepLine" -ForegroundColor Cyan
Write-Host " ShellKnight v2026.09.25.002 - Report" -ForegroundColor Cyan
Write-Host " ShellKnight v2026.09.25.003 - Report" -ForegroundColor Cyan
Write-Host " Hostname : $($env:COMPUTERNAME)" -ForegroundColor White
Write-Host " Run Date : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor White
Write-Host " Runtime : $runtime seconds" -ForegroundColor White
Expand Down Expand Up @@ -3682,7 +3757,7 @@
$jsonPath = "$jsonDir\ShellKnight_${jsonStamp}_$($env:COMPUTERNAME).json"

$jsonData = [ordered]@{
version = 'v2026.09.25.002'
version = 'v2026.09.25.003'
device_id = $Script:DeviceId
hardware_type = $Script:MachineInfo['Hardware Type']
site_name = $SK_SiteName
Expand Down
Loading
Loading