diff --git a/.github/workflows/Conventional_Commits.yml b/.github/workflows/Conventional_Commits.yml index 537e86d6823f9..7f2d86e10d49e 100644 --- a/.github/workflows/Conventional_Commits.yml +++ b/.github/workflows/Conventional_Commits.yml @@ -24,7 +24,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const title = context.payload.pull_request.title || ''; - const pattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .+/; + const pattern = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .+/i; if (pattern.test(title)) { console.log(`✓ PR title follows Conventional Commits format: "${title}"`); diff --git a/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 b/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 index 25481b363bccc..bd1cb3657fe7c 100644 --- a/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPApplicationPermission.ps1 @@ -122,7 +122,7 @@ function Add-CIPPApplicationPermission { if (!$svcPrincipalId) { continue } foreach ($SingleResource in $App.ResourceAccess | Where-Object -Property Type -EQ 'Role') { - if ($SingleResource.id -in $CurrentRoles.appRoleId) { continue } + if ($CurrentRoles | Where-Object { $_.appRoleId -eq $SingleResource.id -and $_.resourceId -eq $svcPrincipalId.id }) { continue } [pscustomobject]@{ principalId = $($ourSVCPrincipal.id) resourceId = $($svcPrincipalId.id) diff --git a/Modules/CIPPCore/Public/Authentication/Get-CIPPRolePermissions.ps1 b/Modules/CIPPCore/Public/Authentication/Get-CIPPRolePermissions.ps1 index dbc1b4759e68e..37f446347aaa1 100644 --- a/Modules/CIPPCore/Public/Authentication/Get-CIPPRolePermissions.ps1 +++ b/Modules/CIPPCore/Public/Authentication/Get-CIPPRolePermissions.ps1 @@ -25,7 +25,13 @@ function Get-CIPPRolePermissions { try { $ValidPermissions = Get-CippHttpPermissions if (@($ValidPermissions).Count -gt 0) { - $Permissions = @($Permissions | Where-Object { $ValidPermissions -contains $_ }) + $ValidBases = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) + foreach ($ValidPermission in $ValidPermissions) { + $null = $ValidBases.Add(($ValidPermission -replace '\.(ReadWrite|Read)$', '')) + } + $Permissions = @($Permissions | Where-Object { + $ValidBases.Contains(($_ -replace '\.(ReadWrite|Read)$', '')) + }) } } catch { Write-Warning "Unable to resolve valid permissions to filter role '$RoleName': $($_.Exception.Message)" diff --git a/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecCompareIntunePolicy.ps1 b/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecCompareIntunePolicy.ps1 index a74d26c71b0a5..8555b7a19f289 100644 --- a/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecCompareIntunePolicy.ps1 +++ b/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecCompareIntunePolicy.ps1 @@ -22,6 +22,7 @@ function Invoke-ExecCompareIntunePolicy { 'windowsQualityUpdatePolicies' = 'windowsQualityUpdatePolicies' 'windowsQualityUpdateProfiles' = 'windowsQualityUpdateProfiles' 'Intents' = 'Intents' + 'ManagedAppPolicies' = 'AppProtection' } try { diff --git a/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntunePolicy.ps1 b/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntunePolicy.ps1 index 94e1bf627f379..2b99a66590ce4 100644 --- a/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntunePolicy.ps1 +++ b/Modules/CIPPHTTP/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ListIntunePolicy.ps1 @@ -225,6 +225,11 @@ function Invoke-ListIntunePolicy { method = 'GET' url = "/deviceManagement/intents?`$top=1000" } + @{ + id = 'ManagedAppPolicies' + method = 'GET' + url = '/deviceAppManagement/managedAppPolicies?$orderby=displayName' + } ) $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter @@ -236,6 +241,7 @@ function Invoke-ListIntunePolicy { $URLName = $_.Id $_.body.Value | ForEach-Object { $AssignmentContext = $_.'assignments@odata.context' + $PolicyODataType = $_.'@odata.type' $policyTypeName = switch -Wildcard ($AssignmentContext) { '*microsoft.graph.windowsIdentityProtectionConfiguration*' { 'Identity Protection' } '*microsoft.graph.windows10EndpointProtectionConfiguration*' { 'Endpoint Protection' } @@ -264,6 +270,14 @@ function Invoke-ListIntunePolicy { $policyTypeName = switch ($URLName) { 'deviceCompliancePolicies' { 'Compliance Policy' } 'Intents' { 'Endpoint Security' } + 'ManagedAppPolicies' { + switch -Wildcard ($PolicyODataType) { + '*iosManagedAppProtection*' { 'iOS App Protection' } + '*androidManagedAppProtection*' { 'Android App Protection' } + '*windowsManagedAppProtection*' { 'Windows App Protection' } + default { 'App Protection' } + } + } default { $AssignmentContext } } } diff --git a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index ae74102230165..e415d4cf580c1 100644 --- a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -1552,6 +1552,16 @@ function Invoke-NinjaOneTenantSync { ### M365 Links Section if ($MappedFields.TenantLinks) { + try { + $SharePointAdminUrl = (Get-SharePointAdminLink -TenantFilter $TenantFilter).AdminUrl + } catch { + $SharePointTenantName = ($Customer.initialDomainName -split '\.')[0] + if ($SharePointTenantName) { + Write-Information "NinjaOneSync: Get-SharePointAdminLink failed for $($Customer.defaultDomainName), using fallback SharePoint admin URL '$SharePointAdminUrl'. Error: $($_.Exception.Message)" + $SharePointAdminUrl = "https://$SharePointTenantName-admin.sharepoint.com" + } + } + $ManagementLinksData = @( @{ Name = 'M365 Admin Portal' @@ -1575,7 +1585,7 @@ function Invoke-NinjaOneTenantSync { }, @{ Name = 'SharePoint Admin' - Link = "https://admin.microsoft.com/Partner/beginclientsession.aspx?CTID=$($Customer.customerId)&CSDEST=SharePoint" + Link = $SharePointAdminUrl ?? "https://$($Customer.defaultDomainName)-admin.sharepoint.com" Icon = 'fas fa-shapes' }, @{ @@ -2241,7 +2251,7 @@ function Invoke-NinjaOneTenantSync { Write-LogMessage -API 'NinjaOneSync' -tenant $TenantFilter -message "CVE sync — skipped $SkippedCount rows (missing deviceName or cveId)" -sev 'Warning' } } - $CsvBytes = New-VulnCsvBytes -TenantFilter $TenantFilter -Rows $CsvRows -Headers @($DeviceIdHeader, $CveIdHeader) + $CsvBytes = New-VulnCsvBytes -Rows $CsvRows -Headers @($DeviceIdHeader, $CveIdHeader) if ($CsvBytes -and $CsvBytes.Length -gt 0) { $UploadUri = "$NinjaBaseUrl/vulnerability/scan-groups/$ResolvedScanGroupId/upload"