Skip to content

Commit c849a85

Browse files
committed
WinRM/SSH detection, config sync, report hardening, test coverage (v0.4.9)
- Add WinRM (5985/5986) and SSH (22) listener detection in Check-Network - Sync TrustedCompanies in Get-DefaultConfig with config.example.json - Use System.Net.WebUtility.HtmlEncode for report detail escaping - Document score formula weights in ReportGenerator - Add Network and DefenseEvasion Assert-FindingCount in test harness Made-with: Cursor
1 parent 93d0f4b commit c849a85

7 files changed

Lines changed: 61 additions & 7 deletions

File tree

AmIHacked.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ if ($script:NonInteractive) {
8080
$script:RedactMap = @{}
8181
$script:SuppressedCount = 0
8282

83-
$script:Version = "0.4.8"
83+
$script:Version = "0.4.9"
8484

8585
# ── Helpers (loaded first) ───────────────────────────────────────────────────
8686

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [0.4.9] - 2026-03-15
8+
9+
### Added
10+
- **WinRM listener detection** -- flags ports 5985/5986 if not in TrustedPorts (WARNING, T1021.006)
11+
- **SSH listener detection** -- flags port 22 if not in TrustedPorts (INFO, T1021.004)
12+
- **Test assertions** -- `Assert-FindingCount` for Network and DefenseEvasion modules
13+
14+
### Fixed
15+
- **TrustedCompanies config drift** -- `Get-DefaultConfig` expanded from 9 to 38 entries, synced with `config.example.json`
16+
- **Report HTML injection hardening** -- finding details now use `[System.Net.WebUtility]::HtmlEncode()` instead of manual `<`/`>` replacement
17+
- **Score formula documented** -- added comment explaining penalty weights and cap behavior
18+
719
## [0.4.8] - 2026-03-15
820

921
### Added

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
[![PowerShell 5.1+](https://img.shields.io/badge/PowerShell-5.1%2B-0d1117?style=for-the-badge&logo=powershell&logoColor=5391FE)](https://docs.microsoft.com/powershell/)
1313
[![Windows 10/11](https://img.shields.io/badge/Windows-10%20%2F%2011-0d1117?style=for-the-badge&logo=windows&logoColor=white)](https://www.microsoft.com/windows)
1414
[![License: MIT](https://img.shields.io/badge/License-MIT-0d1117?style=for-the-badge&logoColor=white)](LICENSE)
15-
[![Version](https://img.shields.io/badge/Version-0.4.8-FF6B6B?style=for-the-badge)](#changelog)
15+
[![Version](https://img.shields.io/badge/Version-0.4.9-FF6B6B?style=for-the-badge)](#changelog)
1616

1717
[![Zero Dependencies](https://img.shields.io/badge/Dependencies-Zero-0d1117?style=flat-square&labelColor=0d1117)](#)
1818
[![MITRE ATT&CK](https://img.shields.io/badge/MITRE%20ATT%26CK-40%2B%20Techniques-ff3333?style=flat-square&labelColor=0d1117)](#mitre-attck-coverage)
@@ -140,7 +140,7 @@ Baselines enable **change detection** — the most powerful signal for catching
140140

141141
```
142142
---AMIHACKED-SUMMARY-JSON---
143-
{"verdict":"CAUTION","critical":0,"warning":3,"info":12,"suppressed":0,"total":15,"duration":28.4,"reportPath":"...","version":"0.4.8"}
143+
{"verdict":"CAUTION","critical":0,"warning":3,"info":12,"suppressed":0,"total":15,"duration":28.4,"reportPath":"...","version":"0.4.9"}
144144
```
145145

146146
- Exit code reflects findings: **0** = clean, **1** = warnings only, **2** = critical findings detected

lib/Helpers.ps1

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,18 @@ function Get-DefaultConfig {
281281
TrustedCompanies = @(
282282
"Microsoft Corporation", "Google LLC", "Slack Technologies",
283283
"Spotify AB", "Discord Inc.", "Mozilla Corporation",
284-
"Apple Inc.", "Adobe Inc.", "Valve Corporation"
284+
"Apple Inc.", "Adobe Inc.", "Valve Corporation",
285+
"GitHub, Inc.", "GitHub", "Node.js Foundation", "OpenJS Foundation",
286+
"Rockstar Games", "Cfx.re", "Python Software Foundation",
287+
"NVIDIA Corporation", "Intel Corporation", "Samsung Electronics",
288+
"Amazon.com Services LLC", "Amazon Web Services",
289+
"Proton AG", "Notion Labs, Inc.", "LM Studio", "Anthropic",
290+
"JetBrains s.r.o.", "Docker Inc", "Canonical Ltd.",
291+
"Postman Inc.", "Activision Publishing", "Blizzard Entertainment",
292+
"Electronic Arts", "Epic Games", "Ubisoft",
293+
"Dropbox, Inc.", "Zoom Video Communications, Inc.",
294+
"1Password", "AgileBits Inc.", "Bitwarden Inc.",
295+
"The Chromium Authors", "The Electron Authors"
285296
)
286297

287298
TrustedAppDirs = @()

lib/ReportGenerator.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function Generate-HtmlReport {
1818
$infoCount = ($Findings | Where-Object { $_.Severity -eq "INFO" }).Count
1919
$totalCount = $Findings.Count
2020

21+
# Score weights: CRITICAL=-25, WARNING=-5, INFO=-0.5; result clamped to [0,100] (4+ criticals always score 0)
2122
$penalty = ($critCount * 25) + ($warnCount * 5) + ($infoCount * 0.5)
2223
$score = [math]::Max(0, [math]::Min(100, [math]::Round(100 - $penalty)))
2324
$scoreColor = if ($score -ge 80) { "#22c55e" } elseif ($score -ge 50) { "#f59e0b" } else { "#ef4444" }
@@ -110,7 +111,7 @@ function Generate-HtmlReport {
110111
$detailsHtml = @"
111112
<div class="finding-details">
112113
<button class="details-toggle" onclick="toggleDetails(this)">Show Technical Details</button>
113-
<pre class="details-content" style="display:none;">$($finding.Details | ConvertTo-Json -Depth 3 | ForEach-Object { $_ -replace '<','&lt;' -replace '>','&gt;' })</pre>
114+
<pre class="details-content" style="display:none;">$([System.Net.WebUtility]::HtmlEncode(($finding.Details | ConvertTo-Json -Depth 3)))</pre>
114115
</div>
115116
"@
116117
}

modules/Check-Network.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,34 @@ function Invoke-NetworkChecks {
205205
-MITRE @("T1571")
206206
}
207207

208+
# WinRM and SSH listeners — always check regardless of commonListenPorts exclusions
209+
$specialListeners = @(
210+
@{ Port = 5985; Severity = "WARNING"; Title = "WinRM Listener Active: port 5985"; MITRE = "T1021.006"
211+
Description = "WinRM (HTTP) is listening on port 5985. WinRM enables remote PowerShell command execution and is commonly abused by attackers for lateral movement."
212+
Remediation = "If WinRM is not required, disable it: 'Disable-PSRemoting -Force'. Restrict access with firewall rules if it must remain enabled." }
213+
@{ Port = 5986; Severity = "WARNING"; Title = "WinRM Listener Active: port 5986"; MITRE = "T1021.006"
214+
Description = "WinRM (HTTPS) is listening on port 5986. WinRM enables remote PowerShell command execution and is commonly abused by attackers for lateral movement."
215+
Remediation = "If WinRM is not required, disable it: 'Disable-PSRemoting -Force'. Restrict access with firewall rules if it must remain enabled." }
216+
@{ Port = 22; Severity = "INFO"; Title = "SSH Listener Active"; MITRE = "T1021.004"
217+
Description = "An OpenSSH server is listening on port 22. SSH enables remote command execution; verify this service is intentional and that key-based authentication is enforced."
218+
Remediation = "If SSH is not required, stop and disable the OpenSSH Server service. If required, ensure 'PasswordAuthentication no' is set in sshd_config and restrict access via firewall." }
219+
)
220+
foreach ($check in $specialListeners) {
221+
if ($trustedPorts -contains $check.Port) { continue }
222+
$match = $listeners | Where-Object { $_.LocalPort -eq $check.Port -and ($_.LocalAddress -eq "0.0.0.0" -or $_.LocalAddress -eq "::") }
223+
if ($match) {
224+
$procId = $match[0].OwningProcess
225+
$proc = $procLookup[$procId]
226+
$procName = if ($proc) { $proc.Name } else { "Unknown (PID: $procId)" }
227+
Add-Finding -Severity $check.Severity -Category "Network" `
228+
-Title $check.Title `
229+
-Description "$($check.Description) Process: '$procName'." `
230+
-Remediation $check.Remediation `
231+
-Details @{ Port = $check.Port; Process = $procName; PID = $procId } `
232+
-MITRE @($check.MITRE)
233+
}
234+
}
235+
208236
# ── 4. DNS Configuration ─────────────────────────────────────────────
209237

210238
Write-Status "Checking DNS configuration..."

tests/Invoke-MockScan.ps1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ if (-not $jsonFiles) {
133133

134134
# Category coverage: each active module should produce at least one finding
135135
Write-TestHeader "Module Coverage"
136-
Assert-FindingCount -Findings $findings -Category "FileSystem" -MinCount 1 -TestName "FileSystem module produced findings"
137-
Assert-FindingCount -Findings $findings -Category "General" -MinCount 1 -TestName "General module produced findings"
136+
Assert-FindingCount -Findings $findings -Category "FileSystem" -MinCount 1 -TestName "FileSystem module produced findings"
137+
Assert-FindingCount -Findings $findings -Category "General" -MinCount 1 -TestName "General module produced findings"
138+
Assert-FindingCount -Findings $findings -Category "Network" -MinCount 1 -TestName "Network module produced findings"
139+
Assert-FindingCount -Findings $findings -Category "DefenseEvasion" -MinCount 1 -TestName "DefenseEvasion module produced findings"
138140

139141
# Severity field is always one of the three valid values
140142
Write-TestHeader "Severity Field Validity"

0 commit comments

Comments
 (0)