diff --git a/code-tests/test-assessments/Test-Assessment.35003.Tests.ps1 b/code-tests/test-assessments/Test-Assessment.35003.Tests.ps1 new file mode 100644 index 000000000..a0bce71ab --- /dev/null +++ b/code-tests/test-assessments/Test-Assessment.35003.Tests.ps1 @@ -0,0 +1,108 @@ +Describe "Test-Assessment-35003" { + BeforeAll { + $here = $PSScriptRoot + $srcRoot = Join-Path $here "../../src/powershell" + + # Mock external module dependencies + if (-not (Get-Command Write-PSFMessage -ErrorAction SilentlyContinue)) { + function Write-PSFMessage {} + } + + # Define Get-Label if it doesn't exist (required for mocking in some environments) + if (-not (Get-Command Get-Label -ErrorAction SilentlyContinue)) { + function Get-Label {} + } + + # Load the class + $classPath = Join-Path $srcRoot "classes/ZtTest.ps1" + if (-not ("ZtTest" -as [type])) { + . $classPath + } + + # Load the SUT + $sut = Join-Path $srcRoot "tests/Test-Assessment.35003.ps1" + . $sut + + # Setup output file + $script:outputFile = Join-Path $here "../TestResults/Report-Test-Assessment.35003.md" + $outputDir = Split-Path $script:outputFile + if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir | Out-Null } + "# Test Results for 35003`n" | Set-Content $script:outputFile + } + + BeforeEach { + Mock Write-PSFMessage {} + Mock Write-ZtProgress {} + Mock Get-SafeMarkdown { param($Text) return $Text } + } + + Context "When labels exist" { + It "Should pass when at least one label exists" { + Mock Get-Label { + return @( + [PSCustomObject]@{ + DisplayName = "Confidential" + Priority = 1 + ParentId = $null + ParentLabelDisplayName = $null + }, + [PSCustomObject]@{ + DisplayName = "Public" + Priority = 0 + ParentId = $null + ParentLabelDisplayName = $null + } + ) + } + + Mock Add-ZtTestResultDetail { + param($TestId, $Title, $Status, $Result) + "## Scenario: Labels Exist`n`n$Result`n" | Add-Content $script:outputFile + } + + Test-Assessment-35003 + + Should -Invoke Add-ZtTestResultDetail -ParameterFilter { + $Status -eq $true -and $Result -match "At least one sensitivity label is configured" + } + } + } + + Context "When no labels exist" { + It "Should fail when no labels are returned" { + Mock Get-Label { return @() } + + Mock Add-ZtTestResultDetail { + param($TestId, $Title, $Status, $Result) + "## Scenario: No Labels`n`n$Result`n" | Add-Content $script:outputFile + } + + Test-Assessment-35003 + + Should -Invoke Add-ZtTestResultDetail -ParameterFilter { + $Status -eq $false -and $Result -match "No sensitivity labels are configured" + } + } + } + + Context "Error Handling" { + It "Should handle errors gracefully" { + Mock Get-Label { throw "Connection Error" } + + $script:capturedResult = $null + Mock Add-ZtTestResultDetail { + param($TestId, $Title, $Status, $Result) + $script:capturedResult = $Result + "## Scenario: Error Handling`n`n$Result`n" | Add-Content $script:outputFile + } + + Test-Assessment-35003 + + Should -Invoke Add-ZtTestResultDetail -ParameterFilter { + $Status -eq $false + } + $script:capturedResult | Should -Match "Unable to query sensitivity labels" + $script:capturedResult | Should -Match "Connection Error" + } + } +} diff --git a/src/powershell/tests/Test-Assessment.35003.md b/src/powershell/tests/Test-Assessment.35003.md new file mode 100644 index 000000000..c77ccbed3 --- /dev/null +++ b/src/powershell/tests/Test-Assessment.35003.md @@ -0,0 +1,20 @@ +Sensitivity labels are the foundation of Microsoft Information Protection, enabling organizations to classify and protect sensitive data across Microsoft 365 services, on-premises locations, and third-party applications. Without sensitivity labels configured, organizations have no standardized way to identify, classify, or protect sensitive information, leaving confidential data vulnerable to unauthorized access, sharing, or exfiltration. Conversely, too many labels presented to the end user overwhelm and reduce effectiveness. + +**Remediation action** + +To create sensitivity labels: +1. Navigate to [Microsoft Purview compliance portal > Information Protection > Labels](https://compliance.microsoft.com/informationprotection/labels) +2. Select "Create a label" +3. Configure label settings including: + - Name and description + - Scope (Files & emails, Meetings, Schematized data assets, etc.) + - Protection settings (encryption, content marking, auto-labeling) +4. Save the label +5. Publish the label via a label policy to make it available to users + +- [Learn about sensitivity labels](https://learn.microsoft.com/microsoft-365/compliance/sensitivity-labels) +- [Create and configure sensitivity labels](https://learn.microsoft.com/microsoft-365/compliance/create-sensitivity-labels) +- [Get started with sensitivity labels](https://learn.microsoft.com/microsoft-365/compliance/get-started-with-sensitivity-labels) + + +%TestResult% diff --git a/src/powershell/tests/Test-Assessment.35003.ps1 b/src/powershell/tests/Test-Assessment.35003.ps1 new file mode 100644 index 000000000..2cec00a11 --- /dev/null +++ b/src/powershell/tests/Test-Assessment.35003.ps1 @@ -0,0 +1,106 @@ +<# +.SYNOPSIS + Total Sensitivity Labels Configured + +.DESCRIPTION + This test checks if there is at least one sensitivity label configured in the tenant. + Sensitivity labels are the foundation of Microsoft Information Protection. + +.NOTES + Test ID: 35003 + Pillar: Data + Risk Level: High +#> + +function Test-Assessment-35003 { + [ZtTest( + Category = 'sensitivity-labels', + ImplementationCost = 'Medium', + MinimumLicense = ('MIP_P1'), + Pillar = 'Data', + RiskLevel = 'High', + SfiPillar = '', + TenantType = ('Workforce'), + TestId = 35003, + Title = 'Total Sensitivity Labels Configured', + UserImpact = 'Low' + )] + [CmdletBinding()] + param() + + #region Data Collection + Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose + + $activity = 'Checking Sensitivity Labels' + Write-ZtProgress -Activity $activity -Status 'Getting Sensitivity Labels' + + $labels = @() + $errorMsg = $null + + try { + # Query: Get all sensitivity labels + $labels = Get-Label -ErrorAction Stop + } + catch { + $errorMsg = $_ + Write-PSFMessage "Error querying Sensitivity Labels: $_" -Level Error + } + #endregion Data Collection + + #region Assessment Logic + if ($errorMsg) { + $passed = $false + } + else { + $passed = $labels.Count -gt 0 + } + #endregion Assessment Logic + + #region Report Generation + if ($errorMsg) { + $testResultMarkdown = "### Investigate`n`n" + $testResultMarkdown += "Unable to query sensitivity labels due to error: $errorMsg" + } + else { + if ($passed) { + $testResultMarkdown = "✅ At least one sensitivity label is configured in the tenant.`n`n" + } + else { + $testResultMarkdown = "❌ No sensitivity labels are configured.`n`n" + } + + $testResultMarkdown += "### Sensitivity Label Configuration Summary`n`n" + $testResultMarkdown += "**Label Statistics:**`n" + $testResultMarkdown += "* Total Label Count: $($labels.Count)`n" + + $topLevelCount = ($labels | Where-Object { [string]::IsNullOrEmpty($_.ParentId) }).Count + $subLabelCount = ($labels | Where-Object { -not [string]::IsNullOrEmpty($_.ParentId) }).Count + + $testResultMarkdown += "* Top-Level Labels Count: $topLevelCount`n" + $testResultMarkdown += "* Sub-Labels Count: $subLabelCount`n`n" + + if ($labels.Count -gt 0) { + $testResultMarkdown += "**Sample Labels** (up to 5):`n" + $testResultMarkdown += "| Label Name | Priority | Parent Label |`n" + $testResultMarkdown += "|:---|:---|:---|`n" + + foreach ($label in ($labels | Select-Object -First 5)) { + $parentName = if (-not [string]::IsNullOrEmpty($label.ParentLabelDisplayName)) { $label.ParentLabelDisplayName } else { "None" } + $labelName = Get-SafeMarkdown -Text $label.DisplayName + $parentName = Get-SafeMarkdown -Text $parentName + $testResultMarkdown += "| $labelName | $($label.Priority) | $parentName |`n" + } + } + + $testResultMarkdown += "`n[Manage Sensitivity Labels in Microsoft Purview](https://purview.microsoft.com/informationprotection/informationprotectionlabels/sensitivitylabels)`n" + } + #endregion Report Generation + + $testResultDetail = @{ + TestId = '35003' + Title = 'Total Sensitivity Labels Configured' + Status = $passed + Result = $testResultMarkdown + } + Add-ZtTestResultDetail @testResultDetail +}