Skip to content

Commit bc6173a

Browse files
authored
Fix Public vs Private project Baselines and Rules #45 (#91)
* Fix Public vs Private project Baselines and Rules #45 * Add missing rules
1 parent bfc3fee commit bc6173a

12 files changed

Lines changed: 301 additions & 3 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ in building the ruleset for this module.
203203
- [Azure.DevOps.Pipelines.Settings.RequireCommentForPullRequestFromFork](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Pipelines.Settings.RequireCommentForPullRequestFromFork.md)
204204
- [Azure.DevOps.Pipelines.Settings.RestrictSecretsForPullRequestFromFork](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Pipelines.Settings.RestrictSecretsForPullRequestFromFork.md)
205205
- [Azure.DevOps.Pipelines.Settings.SanitizeShellTaskArguments](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Pipelines.Settings.SanitizeShellTaskArguments.md)
206+
- [Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate.md)
207+
- [Azure.DevOps.Project.Visibility](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Project.Visibility.md)
206208
- [Azure.DevOps.Repos.Branch.BranchPolicyAllowSelfApproval](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.Branch.BranchPolicyAllowSelfApproval.md)
207209
- [Azure.DevOps.Repos.Branch.BranchPolicyCommentResolution](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.Branch.BranchPolicyCommentResolution.md)
208210
- [Azure.DevOps.Repos.Branch.BranchPolicyEnforceLinkedWorkItems](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.Branch.BranchPolicyEnforceLinkedWorkItems.md)
@@ -226,6 +228,8 @@ in building the ruleset for this module.
226228
- [Azure.DevOps.Repos.InheritedPermissions](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.InheritedPermissions.md)
227229
- [Azure.DevOps.Repos.License](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.License.md)
228230
- [Azure.DevOps.Repos.Readme](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Repos.Readme.md)
231+
- [Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays.md)
232+
- [Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays.md)
229233
- [Azure.DevOps.ServiceConnections.ClassicAzure](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.ServiceConnections.ClassicAzure.md)
230234
- [Azure.DevOps.ServiceConnections.Description](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.ServiceConnections.Description.md)
231235
- [Azure.DevOps.ServiceConnections.GitHubPAT](./src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.ServiceConnections.GitHubPAT.md)

src/PSRule.Rules.AzureDevOps/Functions/Common.ps1

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,45 @@ function Get-AzDevOpsProject {
149149
}
150150
}
151151
# End of Function Get-AzDevOpsProject
152+
153+
<#
154+
.SYNOPSIS
155+
Export the Azure DevOps Project
156+
157+
.DESCRIPTION
158+
Export the Azure DevOps Project using Azure DevOps Rest API to a JSON file
159+
160+
.EXAMPLE
161+
Export-AzDevOpsProject -Project $Project -OutputPath $OutputPath
162+
163+
.NOTES
164+
The output file will be named $Project.prj.ado.json
165+
166+
#>
167+
function Export-AzDevOpsProject {
168+
[CmdletBinding()]
169+
param (
170+
[Parameter(Mandatory=$true)]
171+
[string]
172+
$Project,
173+
[Parameter(Mandatory=$true)]
174+
[string]
175+
$OutputPath
176+
)
177+
if ($null -eq $script:connection) {
178+
throw "Not connected to Azure DevOps. Run Connect-AzDevOps first"
179+
}
180+
$header = $script:connection.GetHeader()
181+
$Organization = $script:connection.Organization
182+
Write-Verbose "Getting project $Project for organization $Organization"
183+
try {
184+
$response = Get-AzDevOpsProject -Project $Project
185+
$response | Add-Member -MemberType NoteProperty -Name ObjectType -Value "Azure.DevOps.Project"
186+
$response | Add-Member -MemberType NoteProperty -Name ObjectName -Value "$Organization.$Project"
187+
}
188+
catch {
189+
throw "Failed to get project $Project from Azure DevOps"
190+
}
191+
$response | ConvertTo-Json | Out-File -FilePath "$OutputPath/$Project.prj.ado.json"
192+
}
193+
# End of Function Export-AzDevOpsProject

src/PSRule.Rules.AzureDevOps/PSRule.Rules.AzureDevOps.psm1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ Function Export-AzDevOpsRuleData {
4141
$OutputPath
4242
)
4343
Write-Verbose "Exporting rule data for project $Project to $OutputPath"
44+
Write-Verbose "Exporting project"
45+
Export-AzDevOpsProject -Project $Project -OutputPath $OutputPath
4446
Write-Verbose "Exporting repos and branch policies"
4547
Export-AzDevOpsReposAndBranchPolicies -Project $Project -OutputPath $OutputPath
4648
Write-Verbose "Exporting environment checks"
@@ -98,6 +100,7 @@ Export-ModuleMember -Function Export-AzDevOpsOrganizationRuleData
98100
# End of Function Export-AzDevOpsOrganizationRuleData
99101

100102
Export-ModuleMember -Function Get-AzDevOpsProject
103+
Export-ModuleMember -Function Export-AzDevOpsProject
101104
Export-ModuleMember -Function Connect-AzDevOps
102105
Export-ModuleMember -Function Disconnect-AzDevOps
103106

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
category: Microsoft Azure DevOps Pipelines
3+
severity: Severe
4+
online version: https://github.com/cloudyspells/PSRule.Rules.AzureDevOps/blob/main/src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate.md
5+
---
6+
7+
# Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate
8+
9+
## SYNOPSIS
10+
11+
Status badges should not be publicly accessible.
12+
13+
## DESCRIPTION
14+
15+
Status badges are publicly accessible by default. This means anyone with the URL can view
16+
the status of a pipeline. Consider restricting access to status badges to prevent
17+
unauthorized access.
18+
19+
Mininum TokenType: `ReadOnly`
20+
21+
## RECOMMENDATION
22+
23+
Consider restricting access to status badges to prevent unauthorized access.
24+
25+
## LINKS
26+
27+
- [Azure DevOps Security best practices](https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-best-practices?view=azure-devops#tasks)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
category: Microsoft Azure DevOps Projects
3+
severity: Critical
4+
online version: https://github.com/cloudyspells/PSRule.Rules.AzureDevOps/blob/main/src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.Project.Visibility.md
5+
---
6+
7+
# Azure.DevOps.Project.Visibility
8+
9+
## SYNOPSIS
10+
11+
Projects should not be publicly accessible.
12+
13+
## DESCRIPTION
14+
15+
Projects can be configured to be publicly accessible. This means anyone with the URL can
16+
view the project. Consider restricting access to projects to prevent unauthorized access.
17+
18+
Mininum TokenType: `ReadOnly`
19+
20+
## RECOMMENDATION
21+
22+
Consider restricting access to projects to prevent unauthorized access.
23+
24+
## LINKS
25+
26+
- [Azure DevOps Security best practices](https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-best-practices?view=azure-devops#tasks)

src/PSRule.Rules.AzureDevOps/rules/AzureDevOps.Pipelines.Settings.Rule.ps1

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,17 @@ Rule 'Azure.DevOps.Pipelines.Settings.SanitizeShellTaskArguments' `
9797
$Assert.HasField($TargetObject, "enableShellTasksArgsSanitizing", $true)
9898
$Assert.HasFieldValue($TargetObject, "enableShellTasksArgsSanitizing", $true)
9999
}
100+
101+
# Synopsis: Status badges should be private
102+
Rule 'Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate' `
103+
-Ref 'ADO-PLS-008' `
104+
-Type 'Azure.DevOps.Pipelines.Settings' `
105+
-Tag @{ release = 'GA'} `
106+
-Level Warning {
107+
# Description: Status badges should be private.
108+
Reason 'Status badges are not private.'
109+
Recommend 'Enable `Status badges should be private` in Project pipelines settings.'
110+
# Links: https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-best-practices?view=azure-devops#policies
111+
$Assert.HasField($TargetObject, "statusBadgesArePrivate", $true)
112+
$Assert.HasFieldValue($TargetObject, "statusBadgesArePrivate", $true)
113+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# PSRule rule definitions for Azure DevOps Pipelines definitions
2+
3+
# Synopsis: Pipelines should use YAML definitions
4+
Rule 'Azure.DevOps.Project.Visibility' `
5+
-Ref 'ADO-PRJ-001' `
6+
-Type 'Azure.DevOps.Project' `
7+
-Tag @{ release = 'GA'} `
8+
-Level Warning {
9+
# Description "Projects should not be public"
10+
Reason "The project is public"
11+
Recommend "Consider making the project private"
12+
# Links "https://learn.microsoft.com/en-us/azure/devops/organizations/security/security-best-practices?view=azure-devops#definitions"
13+
AllOf {
14+
$Assert.HasField($TargetObject, "visibility", $true)
15+
$Assert.HasFieldValue($TargetObject, "visibility", "private")
16+
}
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: github.com/microsoft/PSRule/v1
2+
kind: Baseline
3+
metadata:
4+
name: Baseline.PublicProject
5+
spec:
6+
rule:
7+
exclude:
8+
- 'Azure.DevOps.Project.Visibility'
9+
- 'Azure.DevOps.Pipelines.Settings.StatusBadgesPrivate'
10+
tag:
11+
release: GA
12+
configuration:
13+
ghasEnabled: true
14+
ghasBlockPushesEnabled: true
15+
branchMinimumApproverCount: 1
16+
releaseMinimumProductionApproverCount: 1

tests/Common.Tests.ps1

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,56 @@ Describe "Functions: Common.Tests" {
235235
} | Should -Throw
236236
}
237237
}
238+
239+
Context " Export-AzDevOpsProject without a connection" {
240+
It " should throw an error" {
241+
{
242+
Disconnect-AzDevOps
243+
Export-AzDevOpsProject -Project $env:ADO_PROJECT -OutputPath $env:ADO_EXPORT_DIR
244+
} | Should -Throw "Not connected to Azure DevOps. Run Connect-AzDevOps first"
245+
}
246+
}
247+
248+
Context " Export-AzDevOpsProject" {
249+
BeforeAll {
250+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT $env:ADO_PAT
251+
$project = Get-AzDevOpsProject -Project $env:ADO_PROJECT
252+
Export-AzDevOpsProject -Project $project.name -OutputPath $env:ADO_EXPORT_DIR
253+
}
254+
255+
It " The output folder should contain a file named $env:ADO_PROJECT.prj.ado.json" {
256+
$file = Join-Path -Path $env:ADO_EXPORT_DIR -ChildPath "$env:ADO_PROJECT.prj.ado.json"
257+
Test-Path -Path $file | Should -Be $true
258+
}
259+
260+
It " The output folder should contain a file named $env:ADO_PROJECT.prj.ado.json with a size greater than 0" {
261+
$file = Join-Path -Path $env:ADO_EXPORT_DIR -ChildPath "$env:ADO_PROJECT.prj.ado.json"
262+
(Get-Item $file).length | Should -BeGreaterThan 0
263+
}
264+
265+
It " Should throw on a non-existing project" {
266+
{
267+
Export-AzDevOpsProject -Project 'wrong' -OutputPath $env:ADO_EXPORT_DIR
268+
} | Should -Throw
269+
}
270+
271+
It " The operation should fail with a wrong Organization" {
272+
{
273+
Disconnect-AzDevOps
274+
Connect-AzDevOps -Organization 'wrong' -PAT $env:ADO_PAT
275+
Export-AzDevOpsProject -Project $env:ADO_PROJECT -OutputPath $env:ADO_EXPORT_DIR -ErrorAction Stop
276+
} | Should -Throw
277+
}
278+
279+
It " The operation should fail with a wrong PAT" {
280+
{
281+
Disconnect-AzDevOps
282+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT 'wrong'
283+
Export-AzDevOpsProject -Project $env:ADO_PROJECT -OutputPath $env:ADO_EXPORT_DIR -ErrorAction Stop
284+
} | Should -Throw
285+
}
286+
}
238287
}
239288

240289
AfterAll {
241-
242290
}

tests/Rules.Common.Tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ BeforeAll {
4242

4343
Describe "PSRule.Rules.AzureDevOps Rules" {
4444
Context ' Base rules' {
45-
It ' should contain 59 rules' {
45+
It ' should contain 61 rules' {
4646
$rules = Get-PSRule -Module PSRule.Rules.AzureDevOps
47-
$rules.Count | Should -Be 59
47+
$rules.Count | Should -Be 61
4848
}
4949

5050
It ' should contain a markdown help file for each rule' {

0 commit comments

Comments
 (0)