Skip to content

Commit f97eb56

Browse files
authored
Merge branch 'main' into fixDotNetPathRebase
2 parents a6aa0a4 + 6f2cda9 commit f97eb56

File tree

4 files changed

+187
-2
lines changed

4 files changed

+187
-2
lines changed

src/Microsoft.ComponentDetection.Common/Telemetry/Records/LinuxScannerSyftTelemetryRecord.cs

+2
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ public class LinuxScannerSyftTelemetryRecord : BaseDetectionTelemetryRecord
77
public string LinuxComponents { get; set; }
88

99
public string Exception { get; set; }
10+
11+
public string[] Mariner2ComponentsRemoved { get; set; }
1012
}

src/Microsoft.ComponentDetection.Detectors/linux/LinuxContainerDetector.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public LinuxContainerDetector(ILinuxScanner linuxScanner, IDockerService dockerS
3535

3636
public IEnumerable<ComponentType> SupportedComponentTypes => [ComponentType.Linux];
3737

38-
public int Version => 5;
38+
public int Version => 6;
3939

4040
public bool NeedsAutomaticRootDependencyCalculation => false;
4141

src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs

+36-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,42 @@ public async Task<IEnumerable<LayerMappedLinuxComponents>> ScanLinuxAsync(string
104104
try
105105
{
106106
var syftOutput = JsonConvert.DeserializeObject<SyftOutput>(stdout);
107-
var linuxComponentsWithLayers = syftOutput.Artifacts
107+
108+
// This workaround ignores some packages that originate from the mariner 2.0 image that
109+
// have not been properly tagged with the release and epoch fields in their versions. This
110+
// was fixed for azurelinux 3.0 with https://github.com/microsoft/azurelinux/pull/10405, but
111+
// 2.0 no longer recieves non-security updates and will be deprecated in July 2025.
112+
// This became a problem after the Syft update https://github.com/anchore/syft/pull/3008 which
113+
// allowed for Syft to respect the package type that is listed in the ELF notes file. Since
114+
// mariner 2.0 lists the packages as RPMs, Syft categorizes them as such.
115+
var validArtifacts = syftOutput.Artifacts.ToList();
116+
if (syftOutput.Distro.Id == "mariner" && syftOutput.Distro.VersionId == "2.0")
117+
{
118+
var elfVersionsWithoutRelease = validArtifacts
119+
.Where(artifact =>
120+
artifact.FoundBy == "elf-binary-package-cataloger" // a number of detectors execute with Syft, this one can container invalid results
121+
&& !artifact.Version.Contains('-', StringComparison.OrdinalIgnoreCase)) // dash character indicates that the release version was properly appended to the version, so allow these
122+
.ToList();
123+
124+
// Confirms that the package version was detected elsewhere in the image before removing,
125+
// such as the rpmmanifest
126+
var elfVersionsRemoved = new List<string>();
127+
foreach (var elfArtifact in elfVersionsWithoutRelease)
128+
{
129+
if (validArtifacts.Any(artifact =>
130+
artifact.Name == elfArtifact.Name
131+
&& artifact.Version.StartsWith(elfArtifact.Version)
132+
&& artifact.FoundBy != "elf-binary-package-cataloger"))
133+
{
134+
elfVersionsRemoved.Add(elfArtifact.Name + " " + elfArtifact.Version);
135+
validArtifacts.Remove(elfArtifact);
136+
}
137+
}
138+
139+
syftTelemetryRecord.Mariner2ComponentsRemoved = [.. elfVersionsRemoved];
140+
}
141+
142+
var linuxComponentsWithLayers = validArtifacts
108143
.DistinctBy(artifact => (artifact.Name, artifact.Version))
109144
.Where(artifact => AllowedArtifactTypes.Contains(artifact.Type))
110145
.Select(artifact =>

test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs

+148
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,116 @@ public class LinuxScannerTests
9595
]
9696
}";
9797

98+
private const string SyftOutputIgnoreInvalidMarinerPackages = @"{
99+
""distro"": {
100+
""prettyName"": ""CBL-Mariner/Linux"",
101+
""name"": ""Common Base Linux Mariner"",
102+
""id"": ""mariner"",
103+
""version"": ""2.0.20250304"",
104+
""versionID"": ""2.0"",
105+
},
106+
""artifacts"": [
107+
{
108+
""id"": ""4af20256df269904"",
109+
""name"": ""busybox"",
110+
""version"": ""1.35.0"",
111+
""type"": ""rpm"",
112+
""foundBy"": ""elf-binary-package-cataloger"",
113+
""locations"": [
114+
{
115+
""path"": ""/usr/sbin/busybox"",
116+
""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"",
117+
""accessPath"": ""/usr/sbin/busybox"",
118+
""annotations"": { ""evidence"": ""primary"" }
119+
}
120+
],
121+
""cpes"": [
122+
{
123+
""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*"",
124+
""source"": ""syft-generated""
125+
}
126+
],
127+
""purl"": ""pkg:rpm/mariner/[email protected]?distro=mariner-2.0"",
128+
""metadataType"": ""elf-binary-package-note-json-payload"",
129+
""metadata"": { ""type"": ""rpm"", ""os"": ""mariner"", ""osVersion"": ""2.0"" }
130+
},
131+
{
132+
""id"": ""45849b2d67d236b0"",
133+
""name"": ""busybox"",
134+
""version"": ""1.35.0-13.cm2"",
135+
""type"": ""rpm"",
136+
""foundBy"": ""rpm-db-cataloger"",
137+
""locations"": [
138+
{
139+
""path"": ""/var/lib/rpmmanifest/container-manifest-2"",
140+
""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"",
141+
""accessPath"": ""/var/lib/rpmmanifest/container-manifest-2"",
142+
""annotations"": { ""evidence"": ""primary"" }
143+
}
144+
],
145+
""cpes"": [
146+
{
147+
""cpe"": ""cpe:2.3:a:microsoftcorporation:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*"",
148+
""source"": ""syft-generated""
149+
},
150+
{
151+
""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*"",
152+
""source"": ""syft-generated""
153+
}
154+
],
155+
""purl"": ""pkg:rpm/[email protected]?arch=x86_64&upstream=busybox-1.35.0-13.cm2.src.rpm"",
156+
""metadataType"": ""rpm-db-entry"",
157+
""metadata"": {
158+
""name"": ""busybox"",
159+
""version"": ""1.35.0"",
160+
""epoch"": null,
161+
""architecture"": ""x86_64"",
162+
""release"": ""13.cm2"",
163+
""sourceRpm"": ""busybox-1.35.0-13.cm2.src.rpm"",
164+
""size"": 3512551,
165+
""vendor"": ""Microsoft Corporation"",
166+
""files"": null
167+
}
168+
},
169+
]
170+
}";
171+
172+
private const string SyftOutputKeepNonduplicatedMarinerPackages = @"{
173+
""distro"": {
174+
""prettyName"": ""CBL-Mariner/Linux"",
175+
""name"": ""Common Base Linux Mariner"",
176+
""id"": ""mariner"",
177+
""version"": ""2.0.20250304"",
178+
""versionID"": ""2.0"",
179+
},
180+
""artifacts"": [
181+
{
182+
""id"": ""4af20256df269904"",
183+
""name"": ""busybox"",
184+
""version"": ""1.35.0"",
185+
""type"": ""rpm"",
186+
""foundBy"": ""elf-binary-package-cataloger"",
187+
""locations"": [
188+
{
189+
""path"": ""/usr/sbin/busybox"",
190+
""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"",
191+
""accessPath"": ""/usr/sbin/busybox"",
192+
""annotations"": { ""evidence"": ""primary"" }
193+
}
194+
],
195+
""cpes"": [
196+
{
197+
""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*"",
198+
""source"": ""syft-generated""
199+
}
200+
],
201+
""purl"": ""pkg:rpm/mariner/[email protected]?distro=mariner-2.0"",
202+
""metadataType"": ""elf-binary-package-note-json-payload"",
203+
""metadata"": { ""type"": ""rpm"", ""os"": ""mariner"", ""osVersion"": ""2.0"" }
204+
},
205+
]
206+
}";
207+
98208
private readonly LinuxScanner linuxScanner;
99209
private readonly Mock<IDockerService> mockDockerService;
100210
private readonly Mock<ILogger<LinuxScanner>> mockLogger;
@@ -149,4 +259,42 @@ public async Task TestLinuxScanner_ReturnsNullAuthorAndLicense_Async(string syft
149259
package.Author.Should().Be(null);
150260
package.License.Should().Be(null);
151261
}
262+
263+
[TestMethod]
264+
[DataRow(SyftOutputIgnoreInvalidMarinerPackages)]
265+
public async Task TestLinuxScanner_SyftOutputIgnoreInvalidMarinerPackages_Async(string syftOutput)
266+
{
267+
this.mockDockerService.Setup(service => service.CreateAndRunContainerAsync(It.IsAny<string>(), It.IsAny<List<string>>(), It.IsAny<CancellationToken>()))
268+
.ReturnsAsync((syftOutput, string.Empty));
269+
270+
var result = (await this.linuxScanner.ScanLinuxAsync("fake_hash", [new DockerLayer { LayerIndex = 0, DiffId = "sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6" }], 0)).First().LinuxComponents;
271+
272+
result.Should().ContainSingle();
273+
var package = result.First();
274+
package.Name.Should().Be("busybox");
275+
package.Version.Should().Be("1.35.0-13.cm2");
276+
package.Release.Should().Be("2.0");
277+
package.Distribution.Should().Be("mariner");
278+
package.Author.Should().Be(null);
279+
package.License.Should().Be(null);
280+
}
281+
282+
[TestMethod]
283+
[DataRow(SyftOutputKeepNonduplicatedMarinerPackages)]
284+
public async Task TestLinuxScanner_SyftOutputKeepNonduplicatedMarinerPackages_Async(string syftOutput)
285+
{
286+
this.mockDockerService.Setup(service => service.CreateAndRunContainerAsync(It.IsAny<string>(), It.IsAny<List<string>>(), It.IsAny<CancellationToken>()))
287+
.ReturnsAsync((syftOutput, string.Empty));
288+
289+
var result = (await this.linuxScanner.ScanLinuxAsync("fake_hash", [new DockerLayer { LayerIndex = 0, DiffId = "sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6" }], 0)).First().LinuxComponents;
290+
291+
result.Should().ContainSingle();
292+
var package = result.First();
293+
package.Name.Should().Be("busybox");
294+
package.Version.Should().Be("1.35.0");
295+
package.Release.Should().Be("2.0");
296+
package.Distribution.Should().Be("mariner");
297+
package.Author.Should().Be(null);
298+
package.License.Should().Be(null);
299+
}
152300
}

0 commit comments

Comments
 (0)