Skip to content

Commit 40b0dff

Browse files
committed
feat: Update external package protection guidance and remediation steps for clarity
1 parent 308e47a commit 40b0dff

3 files changed

Lines changed: 17 additions & 16 deletions

File tree

docs/controls.html

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -951,15 +951,16 @@ <h3><span class="id-badge">FEED-02</span><span class="control-name">Feed Creatio
951951
<article class="control-card" id="external-package-protection" data-name="External Package Protection" data-id="FEED-03">
952952
<h3><span class="id-badge">FEED-03</span><span class="control-name">External Package Protection</span></h3>
953953
<div class="control-body">
954-
<p class="description">Reviews Artifacts upstream sources to limit pulling from public registries and protect against dependency-confusion attacks.</p>
954+
<p class="description">Advisory: flags feeds with active upstream sources. Upstreams are usually required; the real mitigation against dependency-confusion is to publish every internal package name to the feed at least once (save-to-feed) so the local copy always wins over public registries.</p>
955955
<details class="steps-details">
956956
<summary>Remediation steps</summary>
957957
<ol>
958-
<li>Navigate to Artifacts &gt; select the feed.</li>
959-
<li>Click the gear icon (Feed settings).</li>
960-
<li>Review Upstream sources.</li>
961-
<li>Disable unnecessary upstream sources.</li>
962-
<li>Enable "Override packages from public sources" protection if available.</li>
958+
<li>Decide whether each upstream (npmjs, nuget.org, Maven Central, etc.) is required. Disable any that aren't.</li>
959+
<li>List every internal package name your org publishes.</li>
960+
<li>Publish each internal name to the feed at least once so it's saved-to-feed.</li>
961+
<li>For npm, use scoped package names (<code>@your-scope/...</code>).</li>
962+
<li>Filter the feed view by <em>Saved</em> periodically to confirm internal names are present.</li>
963+
<li>Document acceptance of FEED-03 in your remediation log once the mitigation is verified.</li>
963964
</ol>
964965
<a class="doc-link" href="https://learn.microsoft.com/en-us/azure/devops/artifacts/concepts/upstream-sources" target="_blank" rel="noopener">Microsoft Learn &rarr;</a>
965966
</details>

invoke-adoqr.ps1

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,31 +3178,31 @@ function Test-OrgFeeds {
31783178
# FEED-02: Feed creation permissions — not easily checked via API
31793179
$results.Add((New-ControlResult -Id "FEED-02" -Status "NOT CHECKED" -Severity "High" -Control "Feed Creation Permissions" -Finding "Manual review required. Check who can create feeds in Organization Settings."))
31803180

3181-
# FEED-03: External package protection — check upstream source settings on each feed
3181+
# FEED-03: External package protection — upstreams can't be assessed reliably via API.
3182+
# The real mitigation (save-to-feed / package source protection) isn't surfaced in the
3183+
# feed payload, so flag feeds with active upstreams as advisory rather than FAIL.
31823184
if ($feeds -and $feeds.value -and $feeds.value.Count -gt 0) {
3183-
$unprotectedFeeds = [System.Collections.Generic.List[string]]::new()
3185+
$feedsWithUpstreams = [System.Collections.Generic.List[string]]::new()
31843186
foreach ($feed in $feeds.value) {
31853187
$upstreamEnabled = Get-SafeProperty $feed 'upstreamEnabled'
31863188
$upstreamSources = Get-SafeProperty $feed 'upstreamSources'
31873189
if ($upstreamEnabled -eq $true -and $upstreamSources) {
3188-
# Check if any upstream source lacks upstream protection
31893190
foreach ($src in $upstreamSources) {
3190-
$protocol = Get-SafeProperty $src 'protocol'
31913191
$status = Get-SafeProperty $src 'status'
31923192
if ($status -ne 'disabled') {
3193-
$unprotectedFeeds.Add($feed.name)
3193+
$feedsWithUpstreams.Add($feed.name)
31943194
break
31953195
}
31963196
}
31973197
}
31983198
}
3199-
if ($unprotectedFeeds.Count -eq 0) {
3200-
$results.Add((New-ControlResult -Id "FEED-03" -Status "PASS" -Severity "High" -Control "External Package Protection" -Finding "All org-level feeds have upstream sources disabled or protected."))
3199+
if ($feedsWithUpstreams.Count -eq 0) {
3200+
$results.Add((New-ControlResult -Id "FEED-03" -Status "PASS" -Severity "Medium" -Control "External Package Protection" -Finding "No org-scoped feeds have active upstream sources."))
32013201
} else {
3202-
$results.Add((New-ControlResult -Id "FEED-03" -Status "FAIL" -Severity "High" -Control "External Package Protection" -Finding "Feeds with active upstream sources: $($unprotectedFeeds -join ', '). Review upstream source protection settings."))
3202+
$results.Add((New-ControlResult -Id "FEED-03" -Status "NOT CHECKED" -Severity "Medium" -Control "External Package Protection" -Finding "Feeds with active upstream sources: $($feedsWithUpstreams -join ', '). Upstreams are typically required for npm/NuGet/Maven; the dependency-confusion mitigation is to publish every internal package name to the feed at least once (save-to-feed makes the local copy win over upstream). Verify each internal package name is saved, and accept this control if the mitigation is in place."))
32033203
}
32043204
} else {
3205-
$results.Add((New-ControlResult -Id "FEED-03" -Status "PASS" -Severity "High" -Control "External Package Protection" -Finding "No org-level feeds found."))
3205+
$results.Add((New-ControlResult -Id "FEED-03" -Status "PASS" -Severity "Medium" -Control "External Package Protection" -Finding "No org-scoped feeds found."))
32063206
}
32073207

32083208
# BADGE-01: Anonymous Badge API — check org pipeline settings

remediation-steps.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@
171171
DocUrl = 'https://learn.microsoft.com/en-us/azure/devops/artifacts/feeds/feed-permissions'
172172
}
173173
'External Package Protection' = @{
174-
Steps = @('Navigate to Artifacts > select the feed.','Click the gear icon (Feed settings).','Review Upstream sources.','Disable unnecessary upstream sources.','Enable "Override packages from public sources" protection if available.')
174+
Steps = @('Decide whether the upstreams (npmjs, nuget.org, Maven Central, etc.) are required. If not, disable them in feed Settings > Upstream sources.','If upstreams are required, apply the dependency-confusion mitigation: list every internal package name your org publishes (e.g., @contoso/utils, Contoso.Common).','Publish each internal package name to the feed at least once. Once a name is saved-to-feed, Azure Artifacts always serves the local copy and never pulls a same-named package from upstream.','For npm, use a scoped package name (@your-scope/...) so public-registry names cannot collide.','Periodically review the feed view filtered by Saved to confirm every internal name is present.','Document acceptance of FEED-03 in your remediation log once the save-to-feed mitigation is verified.','Reach the feed via any project: Artifacts > feed picker > switch to "All feeds in this organization" > select the feed > gear icon.')
175175
DocUrl = 'https://learn.microsoft.com/en-us/azure/devops/artifacts/concepts/upstream-sources'
176176
}
177177
'Maximum PAT Lifetime Policy' = @{

0 commit comments

Comments
 (0)