Skip to content

Commit abcbb60

Browse files
committed
Merge branch 'dev' of https://github.com/KelvinTegelaar/CIPP-API into dev
2 parents 2ab0e0e + f37d68a commit abcbb60

13 files changed

Lines changed: 330 additions & 101 deletions

Config/CIPPDBCacheTypes.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@
244244
"friendlyName": "Exchange Tenant Allow/Block List",
245245
"description": "Exchange Online tenant allow/block list"
246246
},
247+
{
248+
"type": "ExoInboundConnector",
249+
"friendlyName": "Exchange Inbound Connectors",
250+
"description": "Exchange Online inbound connectors (includes enhanced filtering settings)"
251+
},
252+
{
253+
"type": "ExoProtectionAlert",
254+
"friendlyName": "Exchange Protection Alerts",
255+
"description": "Microsoft 365 protection alert policies (Security & Compliance endpoint)"
256+
},
247257
{
248258
"type": "Mailboxes",
249259
"friendlyName": "Mailboxes",

Modules/CIPPCore/Public/Invoke-CIPPDBCacheCollection.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ function Invoke-CIPPDBCacheCollection {
8787
'ExoAdminAuditLogConfig'
8888
'ExoPresetSecurityPolicy'
8989
'ExoTenantAllowBlockList'
90+
'ExoInboundConnector'
91+
'ExoProtectionAlert'
9092
'OwaMailboxPolicy'
9193
'ReportSubmissionPolicy'
9294
'ExoTransportConfig'
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function Set-CIPPDBCacheExoInboundConnector {
2+
<#
3+
.SYNOPSIS
4+
Caches Exchange Online inbound connectors
5+
6+
.PARAMETER TenantFilter
7+
The tenant to cache inbound connector data for
8+
9+
.PARAMETER QueueId
10+
The queue ID to update with total tasks (optional)
11+
#>
12+
[CmdletBinding()]
13+
param(
14+
[Parameter(Mandatory = $true)]
15+
[string]$TenantFilter,
16+
[string]$QueueId
17+
)
18+
19+
try {
20+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching Exchange inbound connectors' -sev Debug
21+
22+
$InboundConnectors = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-InboundConnector'
23+
if ($InboundConnectors) {
24+
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ExoInboundConnector' -Data $InboundConnectors -AddCount
25+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Cached $($InboundConnectors.Count) inbound connectors" -sev Debug
26+
}
27+
$InboundConnectors = $null
28+
29+
} catch {
30+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Failed to cache inbound connector data: $($_.Exception.Message)" -sev Error
31+
}
32+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function Set-CIPPDBCacheExoProtectionAlert {
2+
<#
3+
.SYNOPSIS
4+
Caches Exchange Online / Purview protection alert policies
5+
6+
.DESCRIPTION
7+
Calls Get-ProtectionAlert via the Security & Compliance PowerShell endpoint
8+
(requires the -Compliance switch on New-ExoRequest).
9+
10+
.PARAMETER TenantFilter
11+
The tenant to cache protection alert data for
12+
13+
.PARAMETER QueueId
14+
The queue ID to update with total tasks (optional)
15+
#>
16+
[CmdletBinding()]
17+
param(
18+
[Parameter(Mandatory = $true)]
19+
[string]$TenantFilter,
20+
[string]$QueueId
21+
)
22+
23+
try {
24+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message 'Caching Exchange protection alerts' -sev Debug
25+
26+
$ProtectionAlerts = New-ExoRequest -tenantid $TenantFilter -cmdlet 'Get-ProtectionAlert' -Compliance
27+
if ($ProtectionAlerts) {
28+
Add-CIPPDbItem -TenantFilter $TenantFilter -Type 'ExoProtectionAlert' -Data $ProtectionAlerts -AddCount
29+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Cached $($ProtectionAlerts.Count) protection alerts" -sev Debug
30+
}
31+
$ProtectionAlerts = $null
32+
33+
} catch {
34+
Write-LogMessage -API 'CIPPDBCache' -tenant $TenantFilter -message "Failed to cache protection alert data: $($_.Exception.Message)" -sev Error
35+
}
36+
}

Modules/CIPPStandards/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ function Invoke-CIPPStandardSpamFilterPolicy {
77
.SYNOPSIS
88
(Label) Default Spam Filter Policy
99
.DESCRIPTION
10-
(Helptext) This standard creates a Spam filter policy similar to the default strict policy.
11-
(DocsDescription) This standard creates a Spam filter policy similar to the default strict policy, the following settings are configured to on by default: IncreaseScoreWithNumericIps, IncreaseScoreWithRedirectToOtherPort, MarkAsSpamEmptyMessages, MarkAsSpamJavaScriptInHtml, MarkAsSpamSpfRecordHardFail, MarkAsSpamFromAddressAuthFail, MarkAsSpamNdrBackscatter, MarkAsSpamBulkMail, InlineSafetyTipsEnabled, PhishZapEnabled, SpamZapEnabled
10+
(Helptext) This standard creates a Spam filter policy aligned with the Microsoft Strict preset.
11+
(DocsDescription) This standard creates a Spam filter policy aligned with the Microsoft Strict preset. All Advanced Spam Filter (ASF) settings are left Off per Microsoft guidance (ASF is deprecated and prevents false-positive reporting). The following settings are configured On by default: MarkAsSpamBulkMail, InlineSafetyTipsEnabled, PhishZapEnabled, SpamZapEnabled.
1212
.NOTES
1313
CAT
1414
Defender Standards
@@ -127,20 +127,20 @@ function Invoke-CIPPStandardSpamFilterPolicy {
127127
($CurrentState.BulkThreshold -eq [int]$Settings.BulkThreshold) -and
128128
($CurrentState.QuarantineRetentionPeriod -eq 30) -and
129129
($CurrentState.IncreaseScoreWithImageLinks -eq $IncreaseScoreWithImageLinks) -and
130-
($CurrentState.IncreaseScoreWithNumericIps -eq 'On') -and
131-
($CurrentState.IncreaseScoreWithRedirectToOtherPort -eq 'On') -and
130+
($CurrentState.IncreaseScoreWithNumericIps -eq 'Off') -and
131+
($CurrentState.IncreaseScoreWithRedirectToOtherPort -eq 'Off') -and
132132
($CurrentState.IncreaseScoreWithBizOrInfoUrls -eq $IncreaseScoreWithBizOrInfoUrls) -and
133-
($CurrentState.MarkAsSpamEmptyMessages -eq 'On') -and
134-
($CurrentState.MarkAsSpamJavaScriptInHtml -eq 'On') -and
133+
($CurrentState.MarkAsSpamEmptyMessages -eq 'Off') -and
134+
($CurrentState.MarkAsSpamJavaScriptInHtml -eq 'Off') -and
135135
($CurrentState.MarkAsSpamFramesInHtml -eq $MarkAsSpamFramesInHtml) -and
136136
($CurrentState.MarkAsSpamObjectTagsInHtml -eq $MarkAsSpamObjectTagsInHtml) -and
137137
($CurrentState.MarkAsSpamEmbedTagsInHtml -eq $MarkAsSpamEmbedTagsInHtml) -and
138138
($CurrentState.MarkAsSpamFormTagsInHtml -eq $MarkAsSpamFormTagsInHtml) -and
139139
($CurrentState.MarkAsSpamWebBugsInHtml -eq $MarkAsSpamWebBugsInHtml) -and
140140
($CurrentState.MarkAsSpamSensitiveWordList -eq $MarkAsSpamSensitiveWordList) -and
141-
($CurrentState.MarkAsSpamSpfRecordHardFail -eq 'On') -and
142-
($CurrentState.MarkAsSpamFromAddressAuthFail -eq 'On') -and
143-
($CurrentState.MarkAsSpamNdrBackscatter -eq 'On') -and
141+
($CurrentState.MarkAsSpamSpfRecordHardFail -eq 'Off') -and
142+
($CurrentState.MarkAsSpamFromAddressAuthFail -eq 'Off') -and
143+
($CurrentState.MarkAsSpamNdrBackscatter -eq 'Off') -and
144144
($CurrentState.MarkAsSpamBulkMail -eq 'On') -and
145145
($CurrentState.InlineSafetyTipsEnabled -eq $true) -and
146146
($CurrentState.PhishZapEnabled -eq $true) -and
@@ -183,20 +183,20 @@ function Invoke-CIPPStandardSpamFilterPolicy {
183183
BulkThreshold = [int]$Settings.BulkThreshold
184184
QuarantineRetentionPeriod = 30
185185
IncreaseScoreWithImageLinks = $IncreaseScoreWithImageLinks
186-
IncreaseScoreWithNumericIps = 'On'
187-
IncreaseScoreWithRedirectToOtherPort = 'On'
186+
IncreaseScoreWithNumericIps = 'Off'
187+
IncreaseScoreWithRedirectToOtherPort = 'Off'
188188
IncreaseScoreWithBizOrInfoUrls = $IncreaseScoreWithBizOrInfoUrls
189-
MarkAsSpamEmptyMessages = 'On'
190-
MarkAsSpamJavaScriptInHtml = 'On'
189+
MarkAsSpamEmptyMessages = 'Off'
190+
MarkAsSpamJavaScriptInHtml = 'Off'
191191
MarkAsSpamFramesInHtml = $MarkAsSpamFramesInHtml
192192
MarkAsSpamObjectTagsInHtml = $MarkAsSpamObjectTagsInHtml
193193
MarkAsSpamEmbedTagsInHtml = $MarkAsSpamEmbedTagsInHtml
194194
MarkAsSpamFormTagsInHtml = $MarkAsSpamFormTagsInHtml
195195
MarkAsSpamWebBugsInHtml = $MarkAsSpamWebBugsInHtml
196196
MarkAsSpamSensitiveWordList = $MarkAsSpamSensitiveWordList
197-
MarkAsSpamSpfRecordHardFail = 'On'
198-
MarkAsSpamFromAddressAuthFail = 'On'
199-
MarkAsSpamNdrBackscatter = 'On'
197+
MarkAsSpamSpfRecordHardFail = 'Off'
198+
MarkAsSpamFromAddressAuthFail = 'Off'
199+
MarkAsSpamNdrBackscatter = 'Off'
200200
MarkAsSpamBulkMail = 'On'
201201
InlineSafetyTipsEnabled = $true
202202
PhishZapEnabled = $true

Modules/CIPPTests/Public/Tests/ORCA/Identity/Invoke-CippTestORCA102.ps1

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,28 @@ function Invoke-CippTestORCA102 {
5454
$null = $Result.Append("**Non-Compliant Policies:** $($FailedPolicies.Count)`n`n")
5555
$null = $Result.Append("| Policy Name | Enabled ASF Options |`n")
5656
$null = $Result.Append("|------------|---------------------|`n")
57+
$ASFSettingMap = [ordered]@{
58+
IncreaseScoreWithImageLinks = 'ImageLinks'
59+
IncreaseScoreWithNumericIps = 'NumericIPs'
60+
IncreaseScoreWithRedirectToOtherPort = 'RedirectToOtherPort'
61+
IncreaseScoreWithBizOrInfoUrls = 'BizOrInfoUrls'
62+
MarkAsSpamEmptyMessages = 'EmptyMessages'
63+
MarkAsSpamJavaScriptInHtml = 'JavaScript'
64+
MarkAsSpamFramesInHtml = 'Frames'
65+
MarkAsSpamObjectTagsInHtml = 'ObjectTags'
66+
MarkAsSpamEmbedTagsInHtml = 'EmbedTags'
67+
MarkAsSpamFormTagsInHtml = 'FormTags'
68+
MarkAsSpamWebBugsInHtml = 'WebBugs'
69+
MarkAsSpamSensitiveWordList = 'SensitiveWordList'
70+
MarkAsSpamSpfRecordHardFail = 'SpfRecordHardFail'
71+
MarkAsSpamFromAddressAuthFail = 'FromAddressAuthFail'
72+
MarkAsSpamNdrBackscatter = 'NdrBackscatter'
73+
}
5774
foreach ($Policy in $FailedPolicies) {
5875
$EnabledOptions = [System.Collections.Generic.List[string]]::new()
59-
if ($Policy.IncreaseScoreWithImageLinks -eq 'On') { $EnabledOptions.Add('ImageLinks') | Out-Null }
60-
if ($Policy.IncreaseScoreWithNumericIps -eq 'On') { $EnabledOptions.Add('NumericIPs') | Out-Null }
61-
if ($Policy.MarkAsSpamEmptyMessages -eq 'On') { $EnabledOptions.Add('EmptyMessages') | Out-Null }
62-
if ($Policy.MarkAsSpamJavaScriptInHtml -eq 'On') { $EnabledOptions.Add('JavaScript') | Out-Null }
76+
foreach ($Property in $ASFSettingMap.Keys) {
77+
if ($Policy.$Property -eq 'On') { $EnabledOptions.Add($ASFSettingMap[$Property]) | Out-Null }
78+
}
6379
$null = $Result.Append("| $($Policy.Identity) | $($EnabledOptions -join ', ') |`n")
6480
}
6581
}

Modules/CIPPTests/Public/Tests/ORCA/Identity/Invoke-CippTestORCA103.ps1

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ function Invoke-CippTestORCA103 {
2020
$IsCompliant = $true
2121
$Issues = [System.Collections.Generic.List[string]]::new()
2222

23-
if ($Policy.RecipientLimitExternalPerHour -ne 500) {
23+
if ($Policy.RecipientLimitExternalPerHour -le 0 -or $Policy.RecipientLimitExternalPerHour -gt 500) {
2424
$IsCompliant = $false
25-
$Issues.Add("RecipientLimitExternalPerHour: $($Policy.RecipientLimitExternalPerHour) (should be 500)") | Out-Null
25+
$Issues.Add("RecipientLimitExternalPerHour: $($Policy.RecipientLimitExternalPerHour) (should be between 1 and 500)") | Out-Null
2626
}
27-
if ($Policy.RecipientLimitInternalPerHour -ne 1000) {
27+
if ($Policy.RecipientLimitInternalPerHour -le 0 -or $Policy.RecipientLimitInternalPerHour -gt 1000) {
2828
$IsCompliant = $false
29-
$Issues.Add("RecipientLimitInternalPerHour: $($Policy.RecipientLimitInternalPerHour) (should be 1000)") | Out-Null
29+
$Issues.Add("RecipientLimitInternalPerHour: $($Policy.RecipientLimitInternalPerHour) (should be between 1 and 1000)") | Out-Null
3030
}
31-
if ($Policy.ActionWhenThresholdReached -ne 'BlockUserForToday') {
31+
if ($Policy.ActionWhenThresholdReached -ne 'BlockUser') {
3232
$IsCompliant = $false
33-
$Issues.Add("ActionWhenThresholdReached: $($Policy.ActionWhenThresholdReached) (should be BlockUserForToday)") | Out-Null
33+
$Issues.Add("ActionWhenThresholdReached: $($Policy.ActionWhenThresholdReached) (should be BlockUser)") | Out-Null
3434
}
3535

3636
if ($IsCompliant) {

Modules/CIPPTests/Public/Tests/ORCA/Identity/Invoke-CippTestORCA113.ps1

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,45 @@ function Invoke-CippTestORCA113 {
1313
return
1414
}
1515

16+
# Exclude the Built-In Protection preset — Microsoft owns it and intentionally allows click-through on the baseline.
17+
$CustomPolicies = $SafeLinksPolicies | Where-Object {
18+
$_.IsBuiltInProtection -ne $true
19+
}
20+
1621
$FailedPolicies = [System.Collections.Generic.List[object]]::new()
1722
$PassedPolicies = [System.Collections.Generic.List[object]]::new()
1823

19-
foreach ($Policy in $SafeLinksPolicies) {
20-
if ($Policy.DoNotAllowClickThrough -eq $true) {
21-
$PassedPolicies.Add($Policy)
24+
foreach ($Policy in $CustomPolicies) {
25+
if ($Policy.AllowClickThrough -eq $false) {
26+
$PassedPolicies.Add($Policy) | Out-Null
2227
} else {
23-
$FailedPolicies.Add($Policy)
28+
$FailedPolicies.Add($Policy) | Out-Null
2429
}
2530
}
2631

27-
if ($FailedPolicies.Count -eq 0) {
32+
if ($PassedPolicies.Count -gt 0 -and $FailedPolicies.Count -eq 0) {
2833
$Status = 'Passed'
29-
$Result = [System.Text.StringBuilder]::new("All Safe Links policies have click-through disabled (DoNotAllowClickThrough = true).`n`n")
34+
$Result = [System.Text.StringBuilder]::new("All custom Safe Links policies have click-through disabled (AllowClickThrough = false).`n`n")
3035
$null = $Result.Append("**Compliant Policies:** $($PassedPolicies.Count)`n`n")
31-
if ($PassedPolicies.Count -gt 0) {
32-
$null = $Result.Append("| Policy Name | DoNotAllowClickThrough |`n")
33-
$null = $Result.Append("|------------|----------------------|`n")
34-
foreach ($Policy in $PassedPolicies) {
35-
$null = $Result.Append("| $($Policy.Identity) | $($Policy.DoNotAllowClickThrough) |`n")
36-
}
36+
$null = $Result.Append("| Policy Name | AllowClickThrough |`n")
37+
$null = $Result.Append("|------------|-------------------|`n")
38+
foreach ($Policy in $PassedPolicies) {
39+
$null = $Result.Append("| $($Policy.Identity) | $($Policy.AllowClickThrough) |`n")
3740
}
41+
} elseif ($PassedPolicies.Count -eq 0 -and $FailedPolicies.Count -eq 0) {
42+
$Status = 'Failed'
43+
$Result = [System.Text.StringBuilder]::new("No custom Safe Links policies are configured. The Built-In Protection policy allows click-through by design.`n`n**Remediation:** Create a custom Safe Links policy with `AllowClickThrough = `$false`.")
3844
} else {
3945
$Status = 'Failed'
40-
$Result = [System.Text.StringBuilder]::new("Some Safe Links policies allow click-through, which reduces protection.`n`n")
46+
$Result = [System.Text.StringBuilder]::new("$($FailedPolicies.Count) custom Safe Links policies allow click-through, which reduces protection.`n`n")
4147
$null = $Result.Append("**Failed Policies:** $($FailedPolicies.Count) | **Passed Policies:** $($PassedPolicies.Count)`n`n")
4248
$null = $Result.Append("### Non-Compliant Policies`n`n")
43-
$null = $Result.Append("| Policy Name | DoNotAllowClickThrough | Recommended |`n")
44-
$null = $Result.Append("|------------|----------------------|-------------|`n")
49+
$null = $Result.Append("| Policy Name | AllowClickThrough | Recommended |`n")
50+
$null = $Result.Append("|------------|-------------------|-------------|`n")
4551
foreach ($Policy in $FailedPolicies) {
46-
$null = $Result.Append("| $($Policy.Identity) | $($Policy.DoNotAllowClickThrough) | true |`n")
52+
$null = $Result.Append("| $($Policy.Identity) | $($Policy.AllowClickThrough) | false |`n")
4753
}
48-
$null = $Result.Append("`n**Remediation:** Disable click-through (set DoNotAllowClickThrough to true) to prevent users from bypassing Safe Links protection.")
54+
$null = $Result.Append("`n**Remediation:** Set AllowClickThrough to false to prevent users from bypassing Safe Links protection.")
4955
}
5056

5157
Add-CippTestResult -TenantFilter $Tenant -TestId 'ORCA113' -TestType 'Identity' -Status $Status -ResultMarkdown $Result -Risk 'High' -Name 'AllowClickThrough is disabled in Safe Links policies' -UserImpact 'Low' -ImplementationEffort 'Low' -Category 'Safe Links'

Modules/CIPPTests/Public/Tests/ORCA/Identity/Invoke-CippTestORCA179.ps1

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,29 @@ function Invoke-CippTestORCA179 {
1313
return
1414
}
1515

16+
# Exclude the Built-In Protection preset — Microsoft scopes it to external senders by design.
17+
$CustomPolicies = $Policies | Where-Object {
18+
$_.IsBuiltInProtection -ne $true
19+
}
20+
1621
$FailedPolicies = [System.Collections.Generic.List[object]]::new()
1722
$PassedPolicies = [System.Collections.Generic.List[object]]::new()
1823

19-
foreach ($Policy in $Policies) {
24+
foreach ($Policy in $CustomPolicies) {
2025
if ($Policy.EnableForInternalSenders -eq $true) {
2126
$PassedPolicies.Add($Policy) | Out-Null
2227
} else {
2328
$FailedPolicies.Add($Policy) | Out-Null
2429
}
2530
}
2631

27-
if ($FailedPolicies.Count -eq 0) {
32+
if ($PassedPolicies.Count -gt 0 -and $FailedPolicies.Count -eq 0) {
2833
$Status = 'Passed'
29-
$Result = [System.Text.StringBuilder]::new("All Safe Links policies are enabled for internal senders.`n`n")
34+
$Result = [System.Text.StringBuilder]::new("All custom Safe Links policies are enabled for internal senders.`n`n")
3035
$null = $Result.Append("**Compliant Policies:** $($PassedPolicies.Count)")
36+
} elseif ($PassedPolicies.Count -eq 0 -and $FailedPolicies.Count -eq 0) {
37+
$Status = 'Failed'
38+
$Result = [System.Text.StringBuilder]::new("No custom Safe Links policies are configured. The Built-In Protection policy does not cover internal senders.`n`n**Remediation:** Create a custom Safe Links policy with `EnableForInternalSenders = `$true`.")
3139
} else {
3240
$Status = 'Failed'
3341
$Result = [System.Text.StringBuilder]::new("$($FailedPolicies.Count) Safe Links policies are not enabled for internal senders.`n`n")

0 commit comments

Comments
 (0)