mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
Add support for detecting javascript assets in .NET projects using libman (#3825)
* add support for .NET libman files Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix when no libman detected Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add libman.json docs Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
43a85dfb85
commit
5c6c6aa123
@ -1008,6 +1008,18 @@ func TestCataloger(t *testing.T) {
|
||||
},
|
||||
assertion: assertAccurateNetRuntimePackage,
|
||||
},
|
||||
{
|
||||
name: "libman support",
|
||||
fixture: "image-net6-asp-libman",
|
||||
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
||||
expectedPkgs: []string{
|
||||
"LibManSample @ 1.0.0 (/app/LibManSample.deps.json)",
|
||||
"jquery @ 3.3.1 (/app/libman.json)",
|
||||
},
|
||||
expectedRels: []string{
|
||||
"jquery @ 3.3.1 (/app/libman.json) [dependency-of] LibManSample @ 1.0.0 (/app/LibManSample.deps.json)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
@ -1134,6 +1146,20 @@ func TestDotnetDepsCataloger_regressions(t *testing.T) {
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "libman support",
|
||||
fixture: "image-net6-asp-libman",
|
||||
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
|
||||
assertion: assertPackages(
|
||||
[]string{
|
||||
"jquery", // a javascript package, not the nuget package
|
||||
},
|
||||
[]string{
|
||||
"vendor", // this is the string reference for a filesystem provider
|
||||
"lodash", // this is from a filesystem provider, which is not supported
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@ -78,6 +78,9 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
var runtimePkgs []*pkg.Package
|
||||
for i := range pkgs {
|
||||
p := &pkgs[i]
|
||||
if p.Type != pkg.DotnetPkg {
|
||||
continue
|
||||
}
|
||||
if isRuntime(p.Name) {
|
||||
existingRuntimeVersions.Add(p.Version)
|
||||
runtimePkgs = append(runtimePkgs, p)
|
||||
@ -290,8 +293,22 @@ func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) (*
|
||||
pkgMap[nameVersion] = *dotnetPkg
|
||||
}
|
||||
}
|
||||
rels := relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
|
||||
|
||||
return rootPkg, pkgs, relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
|
||||
// ensure that any libman packages are associated with the all root packages
|
||||
for _, libmanPkg := range doc.LibmanPackages {
|
||||
pkgs = append(pkgs, libmanPkg)
|
||||
if rootPkg == nil {
|
||||
continue
|
||||
}
|
||||
rels = append(rels, artifact.Relationship{
|
||||
From: libmanPkg,
|
||||
To: *rootPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
|
||||
return rootPkg, pkgs, rels
|
||||
}
|
||||
|
||||
// relationshipsFromLogicalDepsJSON creates relationships from a logicalDepsJSON document for only the given syft packages.
|
||||
@ -389,7 +406,13 @@ func findDepsJSON(resolver file.Resolver) ([]logicalDepsJSON, error, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
depsJSONs = append(depsJSONs, getLogicalDepsJSON(*dj))
|
||||
libman, err := findLibmanJSON(resolver, loc)
|
||||
if err != nil {
|
||||
unknownErr = unknown.Append(unknownErr, loc, err)
|
||||
libman = nil
|
||||
}
|
||||
|
||||
depsJSONs = append(depsJSONs, getLogicalDepsJSON(*dj, libman))
|
||||
}
|
||||
|
||||
return depsJSONs, unknownErr, nil
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type depsJSON struct {
|
||||
@ -147,6 +148,7 @@ type logicalDepsJSON struct {
|
||||
PackagesByNameVersion map[string]logicalDepsJSONPackage
|
||||
PackageNameVersions *strset.Set
|
||||
BundlingDetected bool
|
||||
LibmanPackages []pkg.Package
|
||||
}
|
||||
|
||||
func (l logicalDepsJSON) RootPackage() (logicalDepsJSONPackage, bool) {
|
||||
@ -196,7 +198,7 @@ var knownBundlers = strset.New(
|
||||
"Fody", // IL weaving framework
|
||||
)
|
||||
|
||||
func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
|
||||
func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
|
||||
packageMap := make(map[string]*logicalDepsJSONPackage)
|
||||
nameVersions := strset.New()
|
||||
|
||||
@ -244,6 +246,7 @@ func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
|
||||
PackagesByNameVersion: packages,
|
||||
PackageNameVersions: nameVersions,
|
||||
BundlingDetected: bundlingDetected,
|
||||
LibmanPackages: lm.packages(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
109
syft/pkg/cataloger/dotnet/libman_json.go
Normal file
109
syft/pkg/cataloger/dotnet/libman_json.go
Normal file
@ -0,0 +1,109 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// libmanJSON represents the libman.json file format in ASP.NET projects for describing javascript assets to be downloaded and bundled
|
||||
// see https://github.com/aspnet/LibraryManager/wiki/libman.json-reference
|
||||
type libmanJSON struct {
|
||||
Location file.Location `json:"-"`
|
||||
Version string `json:"version"`
|
||||
DefaultProvider string `json:"defaultProvider"`
|
||||
Libraries []struct {
|
||||
Library string `json:"library"`
|
||||
Files []string `json:"files"`
|
||||
Destination string `json:"destination"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
} `json:"libraries"`
|
||||
}
|
||||
|
||||
func (l *libmanJSON) packages() []pkg.Package {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pkgs []pkg.Package
|
||||
for _, lib := range l.Libraries {
|
||||
if lib.Provider == "filesystem" {
|
||||
// there is no name and version with filesystem providers
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(lib.Library, "@")
|
||||
if len(fields) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
name := fields[0]
|
||||
version := fields[1]
|
||||
|
||||
p := pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Locations: file.NewLocationSet(l.Location),
|
||||
Type: pkg.NpmPkg,
|
||||
PURL: packageurl.NewPackageURL(
|
||||
packageurl.TypeNPM,
|
||||
"",
|
||||
name,
|
||||
version,
|
||||
nil,
|
||||
"",
|
||||
).ToString(),
|
||||
Language: pkg.JavaScript,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func newLibmanJSON(reader file.LocationReadCloser) (*libmanJSON, error) {
|
||||
var doc libmanJSON
|
||||
dec := json.NewDecoder(reader)
|
||||
if err := dec.Decode(&doc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range doc.Libraries {
|
||||
l := &doc.Libraries[i]
|
||||
if l.Provider == "" {
|
||||
l.Provider = doc.DefaultProvider
|
||||
}
|
||||
}
|
||||
|
||||
doc.Location = reader.Location
|
||||
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
func findLibmanJSON(resolver file.Resolver, depsJSON file.Location) (*libmanJSON, error) {
|
||||
parent := path.Dir(depsJSON.RealPath)
|
||||
loc := resolver.RelativeFileByPath(depsJSON, path.Join(parent, "libman.json"))
|
||||
if loc == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
reader, err := resolver.FileContentsByLocation(*loc)
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
lj, err := newLibmanJSON(file.NewLocationReadCloser(*loc, reader))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lj, nil
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
.gitignore
|
||||
Dockerfile
|
||||
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/.gitignore
vendored
Normal file
2
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/app
|
||||
/extract.sh
|
||||
@ -0,0 +1,13 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
ARG RUNTIME=win-x64
|
||||
|
||||
COPY . .
|
||||
WORKDIR /src
|
||||
RUN dotnet restore -r $RUNTIME
|
||||
RUN dotnet publish -c Release --no-restore -o /app
|
||||
|
||||
FROM busybox:latest
|
||||
WORKDIR /app
|
||||
COPY --from=build /app .
|
||||
|
||||
|
||||
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/.gitignore
vendored
Normal file
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
wwwroot/lib/
|
||||
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically (or override with FrameworkReference) -->
|
||||
<!-- <PackageReference Include="Microsoft.AspNetCore.App" /> -->
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LibManSample
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace LibManSample
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.Run(async (context) =>
|
||||
{
|
||||
await context.Response.WriteAsync("Hello World!");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "cdnjs",
|
||||
"libraries": [
|
||||
{
|
||||
"library": "jquery@3.3.1",
|
||||
"files": [
|
||||
"jquery.min.js",
|
||||
"jquery.js",
|
||||
"jquery.min.map"
|
||||
],
|
||||
"destination": "wwwroot/lib/jquery/"
|
||||
},
|
||||
{
|
||||
"provider": "filesystem",
|
||||
"library": "vendor",
|
||||
"files": [
|
||||
"lodash.js",
|
||||
"lodash.min.js"
|
||||
],
|
||||
"destination": "wwwroot/lib/lodash/"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/vendor/lodash.js
vendored
Normal file
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/vendor/lodash.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
// jk! nothing to see here!
|
||||
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/vendor/lodash.min.js
vendored
Normal file
1
syft/pkg/cataloger/dotnet/test-fixtures/image-net6-asp-libman/src/vendor/lodash.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
// still nothing here...
|
||||
Loading…
x
Reference in New Issue
Block a user