Open markusmuellerusi opened 9 months ago
Same for syft 1.0.1
"\packages\Microsoft.Exchange.WebServices.2.2\lib\40\Microsoft.Exchange.WebServices.dll" Package name: Microsoft.Exchange.WebServices Version: 2.2
Additional Information: This assembies are .Net Framework 4.x, not .NetCode or .Net6/7/8. The correct Versions you'll find in packages.config file.
Thanks @markusmuellerusi for the report, we will take a look as soon as we can.
Hey @markusmuellerusi, we've done a bunch of digging here and it looks as though the dll files inside that package have some bad metadata, probably from Microsoft's own build process. We are seeing f#InternalName
in the FileDescription
field for one package, and p(InternalName)
in the same field on the other package. This is coming right from the nuget package.
We suspect that in this case, there is some failed template interpolation going on. We have found that Nuget packages don't really have a standard field name for the package name, unfortunately. We have a heuristic to choose which field to use for the package name, and for most Microsoft-built packages, FileDescription
is the correct one.
Do you by chance have a support agreement with Microsoft at all? Have you seen this sort of behavior on any other Nuget packages? We'll be happy to try to work out a solution.
I think it would be better for this kind of project to analyze the packages.config instead of the files (.dll). Here's a snippet created with CycloneDx-dotnet-tool:
{
"type": "library",
"bom-ref": "pkg:nuget/Microsoft.Exchange.WebServices@2.2",
"author": "Microsoft",
"name": "Microsoft.Exchange.WebServices",
"version": "2.2",
"description": "Exchange Web Services (EWS) Managed API",
"scope": "required",
"hashes": [
{
"alg": "SHA-512",
"content": "1ABEED02B764FFA6A0C5DB2E96071E4FC85489DE91C6210AF8B236CD1824C8063F3DDDF6090156CDD499E0A66FBB49B84F4B0377F83759004DAF465ED49FC8C6"
}
],
"licenses": [
{
"license": {
"name": "Unknown - See URL",
"url": "https://github.com/OfficeDev/ews-managed-api/blob/master/license.txt"
}
}
],
It uses the packages.config file <?xml version="1.0" encoding="utf-8"?>
I can use the "syft:location:" property to search for matching package in that path. Yes we have a support contract with Microsoft and I can raise an issue there. But there are others too not only Microsoft. May be thinking about another way of finding packages, would be an option. Thanks a lot
packages.config file <?xml version="1.0" encoding="utf-8"?>
< ?xml version="1.0" encoding="utf-8"? >
< packages >
< package id="Common.Logging" version="3.3.1" targetFramework="net45" / >
< package id="Common.Logging.Core" version="3.3.1" targetFramework="net45" / >
< package id="Common.Logging.Log4Net.Universal" version="1.0.1" targetFramework="net45" / >
< package id="Iesi.Collections" version="4.0.0.4000" targetFramework="net45" / >
< package id="log4net" version="2.0.8" targetFramework="net45" / >
< package id="Microsoft.Exchange.WebServices" version="2.2" targetFramework="net45" / >
< package id="NHibernate" version="4.1.1.4000" targetFramework="net45" / >
< package id="NHibernate.Caches.SysCache" version="4.0.0.4000" targetFramework="net45" / >
< package id="Spring.Core" version="2.0.1" targetFramework="net45" / >
< package id="Spring.Web" version="2.0.1" targetFramework="net45" / >
< package id="System.Data.SQLite.Core" version="1.0.109.1" targetFramework="net45" requireReinstallation="true" / >
< /packages >
Thanks for the hints, @markusmuellerusi, this is very helpful. Can you help me understand where packages.config fits in a bit better? I unzipped the microsoft.exchange.webservices.2.2.0.nupkg
to poke around after replicating your problem but I don't see a packages.config file in there. Are they a part of Nuget packages at all, or something else? Thanks!
For .Net4.x projects, the packages.config file may be located in the project directory, at the same level as the .csproj file itself. The packages directory may be located in the top-level solutions directory. .Net 6/7/8 projects do not have a separate configuration file, the package references are included in the .csproj file. But it all depends on what kind of setup for the package you want to use. (use within the project or for the solution or globally and use package reference). This task may not be easy to determine.
For folks looking to reproduce the issue:
$ dotnet new console
$ dotnet add package Microsoft.Exchange.WebServices --version 2.2.0
$ dotnet publish -c Release
$ syft . -o table -o json=sbom.json
✔ Indexed file system .
✔ Cataloged contents cdb4ee2aea69cc6a83331bbe96dc2caa9a299d21329efb0336fc02a82e1839a8
├── ✔ Packages [16 packages]
└── ✔ Executables [16 executables]
NAME VERSION TYPE
Microsoft.Exchange.WebServices 2.2.0 dotnet (+1 duplicate)
f#InternalName 15.00.0913.015 dotnet (+1 duplicate)
p(InternalName 15.00.0913.000 dotnet (+1 duplicate)
...
cat sbom.json| jq '.artifacts[] | select(.name == "f#InternalName")'
{
"id": "18551e301b570224",
"name": "f#InternalName",
"version": "15.00.0913.015",
"type": "dotnet",
"foundBy": "dotnet-portable-executable-cataloger",
"locations": [
{
"path": "/bin/Release/net7.0/Microsoft.Exchange.WebServices.dll",
"accessPath": "/bin/Release/net7.0/Microsoft.Exchange.WebServices.dll",
"annotations": {
"evidence": "primary"
}
}
],
"licenses": [],
"language": "dotnet",
"cpes": [
{
"cpe": "cpe:2.3:a:f\\#InternalName:f\\#InternalName:15.00.0913.015:*:*:*:*:*:*:*",
"source": "syft-generated"
}
],
"purl": "pkg:nuget/f%23InternalName@15.00.0913.015",
"metadataType": "dotnet-portable-executable-entry",
"metadata": {
"assemblyVersion": "",
"legalCopyright": "© 2014 Microsoft Corporation. All rights reserved.",
"comments": "Service Pack 0",
"internalName": "Microsoft.Exchange.WebServices.dll",
"companyName": "Microsoft Corporation",
"productName": "Microsoft® Exchange",
"productVersion": "15.00.0913.015"
}
}
{
"id": "683c8944d460183a",
"name": "f#InternalName",
"version": "15.00.0913.015",
"type": "dotnet",
"foundBy": "dotnet-portable-executable-cataloger",
"locations": [
{
"path": "/bin/Release/net7.0/publish/Microsoft.Exchange.WebServices.dll",
"accessPath": "/bin/Release/net7.0/publish/Microsoft.Exchange.WebServices.dll",
"annotations": {
"evidence": "primary"
}
}
],
"licenses": [],
"language": "dotnet",
"cpes": [
{
"cpe": "cpe:2.3:a:f\\#InternalName:f\\#InternalName:15.00.0913.015:*:*:*:*:*:*:*",
"source": "syft-generated"
}
],
"purl": "pkg:nuget/f%23InternalName@15.00.0913.015",
"metadataType": "dotnet-portable-executable-entry",
"metadata": {
"assemblyVersion": "",
"legalCopyright": "© 2014 Microsoft Corporation. All rights reserved.",
"comments": "Service Pack 0",
"internalName": "Microsoft.Exchange.WebServices.dll",
"companyName": "Microsoft Corporation",
"productName": "Microsoft® Exchange",
"productVersion": "15.00.0913.015"
}
}
cat sbom.json| jq '.artifacts[] | select(.name == "p(InternalName")'
{
"id": "7a319297af858a94",
"name": "p(InternalName",
"version": "15.00.0913.000",
"type": "dotnet",
"foundBy": "dotnet-portable-executable-cataloger",
"locations": [
{
"path": "/bin/Release/net7.0/Microsoft.Exchange.WebServices.Auth.dll",
"accessPath": "/bin/Release/net7.0/Microsoft.Exchange.WebServices.Auth.dll",
"annotations": {
"evidence": "primary"
}
}
],
"licenses": [],
"language": "dotnet",
"cpes": [
{
"cpe": "cpe:2.3:a:p\\(InternalName:p\\(InternalName:15.00.0913.000:*:*:*:*:*:*:*",
"source": "syft-generated"
}
],
"purl": "pkg:nuget/p(InternalName@15.00.0913.000",
"metadataType": "dotnet-portable-executable-entry",
"metadata": {
"assemblyVersion": "",
"legalCopyright": "© 2014 Microsoft Corporation. All rights reserved.",
"comments": "Service Pack 0",
"internalName": "Microsoft.Exchange.WebServices.Auth.dll",
"companyName": "Microsoft Corporation",
"productName": "Microsoft® Exchange",
"productVersion": "15.00.0913.000"
}
}
{
"id": "5a0ac610c16eb8f8",
"name": "p(InternalName",
"version": "15.00.0913.000",
"type": "dotnet",
"foundBy": "dotnet-portable-executable-cataloger",
"locations": [
{
"path": "/bin/Release/net7.0/publish/Microsoft.Exchange.WebServices.Auth.dll",
"accessPath": "/bin/Release/net7.0/publish/Microsoft.Exchange.WebServices.Auth.dll",
"annotations": {
"evidence": "primary"
}
}
],
"licenses": [],
"language": "dotnet",
"cpes": [
{
"cpe": "cpe:2.3:a:p\\(InternalName:p\\(InternalName:15.00.0913.000:*:*:*:*:*:*:*",
"source": "syft-generated"
}
],
"purl": "pkg:nuget/p(InternalName@15.00.0913.000",
"metadataType": "dotnet-portable-executable-entry",
"metadata": {
"assemblyVersion": "",
"legalCopyright": "© 2014 Microsoft Corporation. All rights reserved.",
"comments": "Service Pack 0",
"internalName": "Microsoft.Exchange.WebServices.Auth.dll",
"companyName": "Microsoft Corporation",
"productName": "Microsoft® Exchange",
"productVersion": "15.00.0913.000"
}
}
We suspect that in this case, there is some failed template interpolation going on. We have found that Nuget packages don't really have a standard field name for the package name, unfortunately.
I think it's a pretty good guess.
I don't think having a (
or #
is valid within a package name in this ecosystem. What we could do in the meantime is change syft to look for these prefixes and then use the referenced field to find the "real" value. This would be the fastest way to address this issue, however, the only problem is that I haven't been able to find any evidence of an existing convention for this p(
and f#
field indirection for these fields.
An initial look shows we can probably add this "fix" safely.
Though the downside with using this "indirect" value is that they appear to lead to incorrect versions relative to the package manager (nuget in this case): v15.00.0913.000 vs v2.2
I think it would be better for this kind of project to analyze the packages.config instead of the files (.dll)
In the context of the incorrect values described in this issue I think this makes sense, but there is more nuance there when it comes to parsing manifest-like files. This is a little more confusing since .NET core vs .NET have different ways to track project dependencies (package.config vs .csproj). There are also some trade offs.
Let's take a simple project as an example:
$ dotnet new console
$ dotnet add package Microsoft.Exchange.WebServices --version 2.2.0
$ dotnet add package System.Text.Json --version 9.0.0-preview.3.24172.9
Taking a look at the manifest, we will see only direct dependencies (cat syft-2697.csproj
):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>syft_2697</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Exchange.WebServices" Version="2.2.0" />
<PackageReference Include="System.Text.Json" Version="8.0.3" />
</ItemGroup>
</Project>
(I'm using dotnet v7)
This trade off is not the case when looking at the deps.json
, which is output from the build itself (thus will show transitive dependencies):
(this tradeoff also does not exist with the binary version resources section, but the deps.json is easier to read)
In cases where there is only the binary and not the deps.json file, we also want to be accurate. I think there are changes that can be made to the PE cataloger to do that:
syft ./bin -o json | jq '.artifacts[] | select(.foundBy == "dotnet-portable-executable-cataloger") | select(.name == "System.Text.Json")'
{
"id": "23eccff6e0f78092",
"name": "System.Text.Json",
"version": "8.0.324.11423",
"metadata": {
"productVersion": "8.0.3+9f4b1f5d664afdfc80e1508ab7ed099dff210fbd",
"fileVersion": "8.0.324.11423",
"fileDescription": "System.Text.Json",
}
}
{
"id": "0244a7a163f7c50e",
"name": "System.Text.Json",
"version": "8.0.324.11423",
"metadataType": "dotnet-portable-executable-entry",
"metadata": {
"productVersion": "8.0.3+9f4b1f5d664afdfc80e1508ab7ed099dff210fbd",
"fileVersion": "8.0.324.11423",
"fileDescription": "System.Text.Json",
}
}
For these examples it looks like the better version to be using is 8.0.3+9f4b1f5d664afdfc80e1508ab7ed099dff210fbd
or maybe a cleaned up value of 8.0.3
(dropping the semver optional metadata field).
Additionally, maybe the PE cataloger should be deduplicating (merging really) these packages so that only one is shown.
What happened: Package name and purl do not match expectet result. Found
f#internalName
:What you expected to happen: The name should be
Microsoft.Exchange.WebServices
and same for purlSteps to reproduce the issue: Create an SBoM for a dotnet project using this nuget package:
Microsoft.Exchange.WebServices.2.2.nupkg
packages\Microsoft.Exchange.WebServices.2.2\lib\40\Microsoft.Exchange.WebServices.dll
Anything else we need to know?:
Environment:
syft version
: 0.104.0cat /etc/os-release
or similar): Windows 10 Prof