Skip to content

Commit fe6b32b

Browse files
authored
Data 35013: Encryption-Enabled Labels (#861)
* added test * added test * added test * updated code * updated code * updated code * updated .md * resolved copilot comments
1 parent d7b521b commit fe6b32b

2 files changed

Lines changed: 244 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Sensitivity labels provide classification capabilities, but without encryption, labels merely mark content as sensitive without preventing unauthorized access. Encryption-enabled labels apply Azure Rights Management protection to documents and emails, enforcing access controls that persist with the content regardless of where it is stored or shared. Users with "Confidential" labels on documents can still forward those files to unauthorized recipients unless encryption prevents file access based on identity. Organizations investing in sensitivity label frameworks without enabling encryption gain visibility into data classification but lack technical enforcement of protection policies. Encrypted labels ensure that only authorized users and applications can decrypt content, preventing data exfiltration even if files are leaked, stolen, or improperly shared. At least one encryption-enabled label should exist for high-value data requiring protection beyond classification metadata.
2+
3+
**Remediation action**
4+
5+
To create or enable encryption on sensitivity labels:
6+
7+
1. Navigate to Microsoft Purview portal → Information Protection → Labels → Sensitivity labels
8+
2. Create a new label or edit an existing label
9+
3. Under label scope, ensure "Items" is selected (to apply encryption to files and emails)
10+
4. In protection settings, select "Apply or remove encryption"
11+
5. Configure encryption settings:
12+
- **Assign permissions now**: Define specific users/groups with explicit permissions
13+
- **Let users assign permissions**: Allow Do Not Forward or user-defined permissions per document
14+
6. Select the encryption method:
15+
- **Standard RMS** (Template-based): Uses organization's default RMS templates
16+
- **Double Key Encryption (DKE)**: For highly sensitive data requiring customer-managed key (note: blocks co-authoring)
17+
7. Save and publish the label to make it available to users
18+
19+
For detailed guidance:
20+
- [Restrict access to content by using encryption in sensitivity labels](https://learn.microsoft.com/en-us/microsoft-365/compliance/encryption-sensitivity-labels)
21+
<!--- Results --->
22+
%TestResult%
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<#
2+
.SYNOPSIS
3+
Validates that at least one encryption-enabled sensitivity label is configured.
4+
5+
.DESCRIPTION
6+
This test checks if encryption-enabled sensitivity labels exist by:
7+
1. Retrieving all sensitivity labels with LabelActions
8+
2. Parsing LabelActions JSON to identify encrypt actions
9+
3. Analyzing encryption settings (type, permissions, co-authoring)
10+
11+
.NOTES
12+
Test ID: 35013
13+
Category: Sensitivity Labels Configuration
14+
Required Module: ExchangeOnlineManagement v3.5.1+
15+
Required Connection: Connect-IPPSSession
16+
#>
17+
18+
function Test-Assessment-35013 {
19+
[ZtTest(
20+
Category = 'Sensitivity Labels Configuration',
21+
ImplementationCost = 'Medium',
22+
MinimumLicense = 'Microsoft 365 E3',
23+
Pillar = 'Data',
24+
RiskLevel = 'High',
25+
SfiPillar = 'Protect tenants and production systems',
26+
TenantType = ('Workforce', 'External'),
27+
TestId = 35013,
28+
Title = 'Encryption-Enabled Labels',
29+
UserImpact = 'High'
30+
)]
31+
[CmdletBinding()]
32+
param()
33+
34+
#region Data Collection
35+
Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose
36+
$activity = 'Checking encryption-enabled sensitivity labels'
37+
Write-ZtProgress -Activity $activity -Status 'Querying sensitivity labels'
38+
39+
$getCmdletFailed = $false
40+
$parsingFailed = $false
41+
$allLabels = $null
42+
$encryptedLabels = @()
43+
44+
# Query: Get all sensitivity labels
45+
try {
46+
$allLabels = Get-Label -ErrorAction Stop
47+
48+
# Parse LabelActions to extract encryption details
49+
foreach ($label in $allLabels) {
50+
try {
51+
$labelActions = $label.LabelActions | ConvertFrom-Json
52+
$encryptAction = $labelActions | Where-Object { $_.Type -eq 'encrypt' }
53+
54+
if ($null -ne $encryptAction) {
55+
# Check if encryption is disabled
56+
$disabledSetting = $encryptAction.Settings | Where-Object { $_.Key -eq 'disabled' }
57+
if ($disabledSetting -and $disabledSetting.Value -eq 'true') {
58+
continue # Skip this label as encryption is disabled
59+
}
60+
61+
# Check if DKE using Capabilities property (more reliable than LabelActions)
62+
$isDKE = $label.Capabilities -contains 'DoubleKeyEncryption'
63+
64+
# Extract encryption details from Settings array (Key-Value pairs)
65+
$protectionTypeSetting = $encryptAction.Settings | Where-Object { $_.Key -eq 'protectiontype' }
66+
67+
# Determine encryption type
68+
if ($isDKE) {
69+
$encryptionType = 'dke'
70+
}
71+
elseif ($protectionTypeSetting) {
72+
$encryptionType = $protectionTypeSetting.Value
73+
}
74+
else {
75+
$encryptionType = 'template'
76+
}
77+
78+
$rightsDefSetting = $encryptAction.Settings | Where-Object { $_.Key -eq 'rightsdefinitions' }
79+
$rightsDef = if ($rightsDefSetting) { $rightsDefSetting.Value } else { 'Not specified' }
80+
81+
$contentExpirySetting = $encryptAction.Settings | Where-Object { $_.Key -eq 'contentexpiredondateindaysornever' }
82+
$contentExpiry = if ($contentExpirySetting) { $contentExpirySetting.Value } else { 'Never' }
83+
84+
# Determine if co-authoring is blocked
85+
$coAuthoringBlocked = ($encryptionType -eq 'dke') -or ($contentExpiry -ne 'Never')
86+
87+
$encryptedLabels += [PSCustomObject]@{
88+
Name = $label.DisplayName
89+
EncryptionType = $encryptionType
90+
RightsDefinitions = $rightsDef
91+
CoAuthoringBlocked = if ($coAuthoringBlocked) { 'Yes' } else { 'No' }
92+
}
93+
}
94+
}
95+
catch {
96+
Write-PSFMessage "Failed to parse LabelActions for label '$($label.DisplayName)': $_" -Tag Test -Level Warning
97+
$parsingFailed = $true
98+
}
99+
}
100+
}
101+
catch {
102+
$getCmdletFailed = $true
103+
Write-PSFMessage "Failed to retrieve sensitivity labels: $_" -Tag Test -Level Warning
104+
}
105+
#endregion Data Collection
106+
107+
#region Assessment Logic
108+
$testResultMarkdown = ''
109+
$passed = $false
110+
$customStatus = $null
111+
112+
# Check if Get-Label cmdlet failed
113+
if ($getCmdletFailed) {
114+
$testResultMarkdown = "⚠️ Unable to determine encryption-enabled label configuration due to query failure, connection issues, or insufficient permissions.`n`n%TestResult%"
115+
$passed = $false
116+
$customStatus = 'Investigate'
117+
}
118+
# Check if labels were retrieved but parsing failed
119+
elseif ($parsingFailed) {
120+
$testResultMarkdown = "⚠️ Labels exist but encryption configuration cannot be determined for some labels.`n`n%TestResult%"
121+
$passed = $false
122+
$customStatus = 'Investigate'
123+
}
124+
# Check encrypted label count
125+
elseif ($encryptedLabels.Count -eq 0) {
126+
$testResultMarkdown = "❌ No encryption-enabled labels exist; all labels provide classification only.`n`n%TestResult%"
127+
$passed = $false
128+
}
129+
else {
130+
$testResultMarkdown = "✅ At least one encryption-enabled sensitivity label is configured.`n`n%TestResult%"
131+
$passed = $true
132+
}
133+
#endregion Assessment Logic
134+
135+
#region Report Generation
136+
$mdInfo = ''
137+
138+
if ($encryptedLabels.Count -gt 0) {
139+
$formatTemplate = @'
140+
141+
## [{0}]({1})
142+
143+
| Label name | Encryption type | Default permissions identities | Co-Authoring blocked |
144+
| :--------- | :-------------- | :----------------------------- | :------------------: |
145+
{2}
146+
147+
'@
148+
149+
$reportTitle = 'Encryption Label Details'
150+
$portalLink = 'https://purview.microsoft.com/informationprotection/informationprotectionlabels/sensitivitylabels'
151+
152+
# Build table rows
153+
$labelDetails = ''
154+
foreach ($encLabel in $encryptedLabels) {
155+
$name = if ($encLabel.Name) { Get-SafeMarkdown -Text $encLabel.Name } else { 'N/A' }
156+
$encType = switch ($encLabel.EncryptionType) {
157+
'template' { 'Standard RMS' }
158+
'dke' { 'Double Key Encryption (DKE)' }
159+
'userdefined' { 'User-Defined' }
160+
default { $encLabel.EncryptionType }
161+
}
162+
163+
# Format rights definitions - show first 5 identities (users, groups, or domains)
164+
$rights = 'Not specified'
165+
if ($encLabel.RightsDefinitions -and $encLabel.RightsDefinitions -ne 'Not specified') {
166+
try {
167+
# Parse the JSON string containing rights definitions
168+
$rightsArray = $encLabel.RightsDefinitions | ConvertFrom-Json
169+
if ($rightsArray) {
170+
$identities = @($rightsArray | Where-Object { $_.Identity } | ForEach-Object { Get-SafeMarkdown -Text $_.Identity })
171+
if ($identities.Count -gt 5) {
172+
$rights = ($identities[0..4] -join ', ') + ', ...'
173+
}
174+
else {
175+
$rights = $identities -join ', '
176+
}
177+
}
178+
}
179+
catch {
180+
# If parsing fails, show fallback message
181+
$rights = 'Unable to parse permissions'
182+
}
183+
}
184+
185+
$coAuthBlocked = $encLabel.CoAuthoringBlocked
186+
187+
$labelDetails += "| $name | $encType | $rights | $coAuthBlocked |`n"
188+
}
189+
190+
$labelDetails += "`n**Summary:**`n"
191+
$labelDetails += "* Total Encryption-Enabled Labels: $($encryptedLabels.Count)`n"
192+
193+
# Count by encryption type
194+
$standardRMS = @($encryptedLabels | Where-Object { $_.EncryptionType -eq 'template' }).Count
195+
$userDefined = @($encryptedLabels | Where-Object { $_.EncryptionType -eq 'userdefined' }).Count
196+
$dkeLabels = @($encryptedLabels | Where-Object { $_.EncryptionType -eq 'dke' }).Count
197+
198+
$labelDetails += "* Standard RMS: $standardRMS`n"
199+
$labelDetails += "* User-Defined: $userDefined`n"
200+
$labelDetails += "* Double Key Encryption (DKE): $dkeLabels"
201+
202+
$mdInfo = $formatTemplate -f $reportTitle, $portalLink, $labelDetails
203+
}
204+
205+
# Replace the placeholder with detailed information
206+
$testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
207+
#endregion Report Generation
208+
209+
$params = @{
210+
TestId = '35013'
211+
Title = 'Encryption-Enabled Labels'
212+
Status = $passed
213+
Result = $testResultMarkdown
214+
}
215+
216+
if ($null -ne $customStatus) {
217+
$params.CustomStatus = $customStatus
218+
}
219+
220+
# Add test result details
221+
Add-ZtTestResultDetail @params
222+
}

0 commit comments

Comments
 (0)