Skip to content

Commit e0d0230

Browse files
Dev to release (KelvinTegelaar#1990)
2 parents 951d2db + 65a0899 commit e0d0230

380 files changed

Lines changed: 32032 additions & 18442 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ yarn.lock
2121
/*.ps1
2222
!/profile.ps1
2323
.DS_Store
24+
proxyman.pem

CIPPDBCacheTypes.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@
279279
"friendlyName": "OneDrive Usage",
280280
"description": "OneDrive usage statistics"
281281
},
282+
{
283+
"type": "OfficeActivations",
284+
"friendlyName": "Office Activations",
285+
"description": "Microsoft 365 app activation counts per user per platform (Windows, Mac, Mobile)"
286+
},
287+
{
288+
"type": "CopilotReadinessActivity",
289+
"friendlyName": "Copilot Readiness Activity",
290+
"description": "Per-user Copilot readiness signals (update channel, Teams, Outlook, Office docs) over 30 days"
291+
},
282292
{
283293
"type": "ConditionalAccessPolicies",
284294
"friendlyName": "Conditional Access Policies",
@@ -328,5 +338,30 @@
328338
"type": "DetectedApps",
329339
"friendlyName": "Detected Apps",
330340
"description": "All detected applications with devices where each app is installed"
341+
},
342+
{
343+
"type": "SensitivityLabels",
344+
"friendlyName": "Sensitivity Labels",
345+
"description": "Microsoft Purview sensitivity labels configured in the tenant"
346+
},
347+
{
348+
"type": "DlpCompliancePolicies",
349+
"friendlyName": "DLP Compliance Policies",
350+
"description": "Data Loss Prevention compliance policies from the Purview compliance portal"
351+
},
352+
{
353+
"type": "CopilotUsageUserDetail",
354+
"friendlyName": "Copilot Usage User Detail",
355+
"description": "Per-user Microsoft 365 Copilot usage details across apps (30-day period)"
356+
},
357+
{
358+
"type": "CopilotUserCountSummary",
359+
"friendlyName": "Copilot User Count Summary",
360+
"description": "Aggregate active Copilot user counts by app (30-day period)"
361+
},
362+
{
363+
"type": "CopilotUserCountTrend",
364+
"friendlyName": "Copilot User Count Trend",
365+
"description": "Daily Copilot active user count trend (7-day period)"
331366
}
332367
]

Config/standards.json

Lines changed: 1168 additions & 109 deletions
Large diffs are not rendered by default.

ConversionTable.csv

Lines changed: 4146 additions & 4115 deletions
Large diffs are not rendered by default.

Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
55
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
66

77
COPY . /home/site/wwwroot
8+
9+
# Optionally install Proxyman CA certificate if proxyman.pem exists in the build context
10+
RUN if [ -f /home/site/wwwroot/proxyman.pem ]; then \
11+
apt-get update && apt-get install -y --no-install-recommends ca-certificates && \
12+
cp /home/site/wwwroot/proxyman.pem /usr/local/share/ca-certificates/proxyman.crt && \
13+
update-ca-certificates && \
14+
rm -rf /var/lib/apt/lists/*; \
15+
fi

Modules/CIPPCore/CIPPCore.psd1

-4.38 KB
Binary file not shown.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
function ConvertTo-StringList {
2+
<#
3+
.SYNOPSIS
4+
Turns encoded list data into something you can foreach over.
5+
6+
.DESCRIPTION
7+
String input: if it is JSON (object or array), it is converted first; otherwise comma/semicolon/newline
8+
splitting applies. Other shapes: wrapper objects, or an existing array/list. After conversion, the return
9+
value is always foreach-able (empty collection is @()). Arrays and IList instances are returned
10+
as-is — they are already foreach-able without conversion.
11+
This exists because front end multi-value input is annoying to deal with.
12+
13+
.PARAMETER InputObject
14+
Encoded list (string/JSON), wrapper object, or an existing array/list.
15+
16+
.PARAMETER PropertyNames
17+
On hashtables/PSCustomObjects, property names to read in order.
18+
19+
.OUTPUTS
20+
Always an enumerable suitable for: foreach ($item in (ConvertTo-StringList ...)) { }
21+
#>
22+
[CmdletBinding()]
23+
param(
24+
[Parameter(Position = 0)]
25+
[Alias('Input', 'Value')]
26+
[AllowNull()]
27+
$InputObject,
28+
29+
[string[]]$PropertyNames = @('Items', 'Value')
30+
)
31+
32+
# Output must be foreach-able; $null input yields empty collection.
33+
if ($null -eq $InputObject) {
34+
return @()
35+
}
36+
37+
if ($InputObject -is [string]) {
38+
$s = $InputObject.Trim()
39+
if (-not $s) {
40+
return @()
41+
}
42+
if ($s.StartsWith('[') -or $s.StartsWith('{')) {
43+
try {
44+
$parsed = $s | ConvertFrom-Json -ErrorAction Stop
45+
return ConvertTo-StringList -InputObject $parsed -PropertyNames $PropertyNames
46+
} catch {
47+
}
48+
}
49+
return @(
50+
$s -split '[,;\r\n]+' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
51+
)
52+
}
53+
54+
if ($InputObject -is [Array]) {
55+
return $InputObject
56+
}
57+
if ($InputObject -is [System.Collections.IList] -and $InputObject -isnot [string]) {
58+
return $InputObject
59+
}
60+
61+
if ($InputObject -is [hashtable]) {
62+
if ($InputObject.Count -eq 0) {
63+
return @()
64+
}
65+
foreach ($name in $PropertyNames) {
66+
if ($InputObject.ContainsKey($name)) {
67+
return ConvertTo-StringList -InputObject $InputObject[$name] -PropertyNames $PropertyNames
68+
}
69+
}
70+
foreach ($p in $InputObject.GetEnumerator() | Sort-Object { $_.Key }) {
71+
$v = $p.Value
72+
if ($null -eq $v) { continue }
73+
if ($v -is [string] -or ($v -is [System.Collections.IEnumerable] -and $v -isnot [hashtable] -and $v -isnot [pscustomobject])) {
74+
return ConvertTo-StringList -InputObject $v -PropertyNames @()
75+
}
76+
}
77+
$single = "$InputObject".Trim()
78+
if ($single) {
79+
return , @($single)
80+
}
81+
return @()
82+
}
83+
84+
if ($InputObject -is [pscustomobject]) {
85+
foreach ($name in $PropertyNames) {
86+
if ($InputObject.PSObject.Properties.Name -contains $name) {
87+
return ConvertTo-StringList -InputObject $InputObject.$name -PropertyNames $PropertyNames
88+
}
89+
}
90+
foreach ($p in $InputObject.PSObject.Properties) {
91+
$v = $p.Value
92+
if ($null -eq $v) { continue }
93+
if ($v -is [string] -or ($v -is [System.Collections.IEnumerable] -and $v -isnot [hashtable] -and $v -isnot [pscustomobject])) {
94+
return ConvertTo-StringList -InputObject $v -PropertyNames @()
95+
}
96+
}
97+
$single = "$InputObject".Trim()
98+
if ($single) {
99+
return , @($single)
100+
}
101+
return @()
102+
}
103+
104+
$t = "$InputObject".Trim()
105+
if ($t) {
106+
return , @($t)
107+
}
108+
return @()
109+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
function Get-CIPPSchedulerBlockedCommands {
2+
<#
3+
.SYNOPSIS
4+
Returns the list of commands that are blocked from execution via the CIPP scheduler.
5+
.DESCRIPTION
6+
Prevents privilege escalation and credential exfiltration by blocking functions that
7+
return tokens, secrets, keys, credentials, tenant lists, or perform SAM/CPV configuration
8+
from being executed as user-scheduled tasks.
9+
#>
10+
[CmdletBinding()]
11+
param()
12+
13+
return @(
14+
# Token & authentication functions - would exfiltrate access/refresh tokens
15+
'Get-GraphToken'
16+
'Get-GraphTokenFromCert'
17+
'Get-ClassicAPIToken'
18+
'Get-CIPPAzIdentityToken'
19+
'Get-CIPPAuthentication'
20+
'New-CIPPAzServiceSAS'
21+
22+
# Extension authentication tokens
23+
'Get-GradientToken'
24+
'Get-HaloToken'
25+
'Get-NinjaOneToken'
26+
'Get-SherwebAuthentication'
27+
'Get-HIBPAuth'
28+
29+
# Secret & key material
30+
'Get-CippKeyVaultSecret'
31+
'Remove-CippKeyVaultSecret'
32+
'Get-ExtensionAPIKey'
33+
'Set-ExtensionAPIKey'
34+
'Remove-ExtensionAPIKey'
35+
36+
# Tenant enumeration - would reveal full tenant list
37+
'Get-Tenants'
38+
39+
# SAM permission enumeration - exposes which permissions the SAM app holds
40+
'Get-CippSamPermissions'
41+
42+
# Direct storage access - bypasses CIPP data access controls
43+
'Get-CIPPTable'
44+
'Get-CIPPAzDataTableEntity'
45+
'Get-AzDataTableEntity'
46+
'Get-AzDataTable'
47+
'Add-CIPPAzDataTableEntity'
48+
'Add-AzDataTableEntity'
49+
'Update-AzDataTableEntity'
50+
'Remove-AzDataTableEntity'
51+
'Remove-AzDataTable'
52+
53+
# Backup & restore
54+
'Get-CIPPBackup'
55+
)
56+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function Test-CIPPConditionFilter {
2+
<#
3+
.SYNOPSIS
4+
Returns a sanitized PowerShell condition string for an audit log / delta query condition.
5+
.DESCRIPTION
6+
Validates operator and property name against allowlists, sanitizes input values,
7+
then returns a safe condition string suitable for [ScriptBlock]::Create().
8+
9+
This replaces the old Invoke-Expression pattern which was vulnerable to code injection
10+
through unsanitized user-controlled fields.
11+
.PARAMETER Condition
12+
A single condition object with Property.label, Operator.value, and Input.value.
13+
.OUTPUTS
14+
[string] A sanitized PowerShell condition string, or $null if validation fails.
15+
.FUNCTIONALITY
16+
Internal
17+
#>
18+
[CmdletBinding()]
19+
[OutputType([string])]
20+
param(
21+
[Parameter(Mandatory = $true)]
22+
$Condition
23+
)
24+
25+
# Operator allowlist - only these PowerShell comparison operators are permitted
26+
$AllowedOperators = @(
27+
'eq', 'ne', 'like', 'notlike', 'match', 'notmatch',
28+
'gt', 'lt', 'ge', 'le', 'in', 'notin',
29+
'contains', 'notcontains'
30+
)
31+
32+
# Property name validation - only alphanumeric, underscores, and dots allowed
33+
$SafePropertyRegex = [regex]'^[a-zA-Z0-9_.]+$'
34+
35+
# Value sanitization - block characters that enable code injection
36+
$UnsafeValueRegex = [regex]'[;|`\$\{\}]'
37+
38+
$propertyName = $Condition.Property.label
39+
$operatorValue = $Condition.Operator.value.ToLower()
40+
$inputValue = $Condition.Input.value
41+
42+
# Validate operator against allowlist
43+
if ($operatorValue -notin $AllowedOperators) {
44+
Write-Warning "Blocked invalid operator '$($Condition.Operator.value)' in condition for property '$propertyName'"
45+
return $null
46+
}
47+
48+
# Validate property name to prevent injection via property paths
49+
if (-not $SafePropertyRegex.IsMatch($propertyName)) {
50+
Write-Warning "Blocked invalid property name '$propertyName' in condition"
51+
return $null
52+
}
53+
54+
# Build sanitized condition string
55+
if ($inputValue -is [array]) {
56+
# Sanitize each array element
57+
$sanitizedItems = foreach ($item in $inputValue) {
58+
$itemStr = [string]$item
59+
if ($UnsafeValueRegex.IsMatch($itemStr)) {
60+
Write-Warning "Blocked unsafe value in array for property '$propertyName': '$itemStr'"
61+
return $null
62+
}
63+
$itemStr -replace "'", "''"
64+
}
65+
if ($null -eq $sanitizedItems) { return $null }
66+
$arrayAsString = $sanitizedItems | ForEach-Object { "'$_'" }
67+
$value = "@($($arrayAsString -join ', '))"
68+
} else {
69+
$valueStr = [string]$inputValue
70+
if ($UnsafeValueRegex.IsMatch($valueStr)) {
71+
Write-Warning "Blocked unsafe value for property '$propertyName': '$valueStr'"
72+
return $null
73+
}
74+
$value = "'$($valueStr -replace "'", "''")'"
75+
}
76+
77+
return "`$(`$_.$propertyName) -$operatorValue $value"
78+
}

0 commit comments

Comments
 (0)