mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
added macOS .app cataloger (#4490)
* added macOS .app cataloger, fixed #4010 Signed-off-by: Rez Moss <hi@rezmoss.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * added macOS .app cataloger, fixed #4010 Signed-off-by: Rez Moss <hi@rezmoss.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * added macOS .app cataloger, fixed #4010 Signed-off-by: Rez Moss <hi@rezmoss.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * address review comments Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * bump schema to 16.1.7 Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * address static analysis failures Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * migrate to testdata Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * expand fields and improve test coverage Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Rez Moss <hi@rezmoss.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package pkg
|
||||
|
||||
// AppleAppBundleEntry represents metadata about an Apple application bundle (CFBundle) extracted from Info.plist files.
|
||||
// These bundles share the same plist structure across macOS, iOS, watchOS, visionOS, and others.
|
||||
type AppleAppBundleEntry struct {
|
||||
// BundleIdentifier is the unique identifier for the bundle (e.g. "com.apple.Safari")
|
||||
BundleIdentifier string `json:"bundleIdentifier,omitempty"`
|
||||
|
||||
// Name is the short name of the bundle
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// DisplayName is the user-facing name of the bundle
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
|
||||
// Executable is the name of the executable within the bundle
|
||||
Executable string `json:"executable,omitempty"`
|
||||
|
||||
// ShortVersion is the release (marketing) version of the bundle
|
||||
ShortVersion string `json:"shortVersion,omitempty"`
|
||||
|
||||
// Version is the build version of the bundle, which often differs from the short version
|
||||
Version string `json:"version,omitempty"`
|
||||
|
||||
// PackageType is the four-letter type code (e.g. "APPL" for apps, "FMWK" for frameworks, "BNDL" for bundles)
|
||||
PackageType string `json:"packageType,omitempty"`
|
||||
|
||||
// SupportedPlatforms lists the platforms the bundle targets (e.g. "MacOSX", "iPhoneOS")
|
||||
SupportedPlatforms []string `json:"supportedPlatforms,omitempty"`
|
||||
|
||||
// MinimumSystemVersion is the minimum macOS version required to run the bundle
|
||||
MinimumSystemVersion string `json:"minimumSystemVersion,omitempty"`
|
||||
|
||||
// MinimumOSVersion is the minimum OS version required for non-macOS platforms (e.g. iOS)
|
||||
MinimumOSVersion string `json:"minimumOSVersion,omitempty"`
|
||||
|
||||
// Copyright is the human-readable copyright notice for the bundle
|
||||
Copyright string `json:"copyright,omitempty"`
|
||||
|
||||
// PlatformName is the platform name of the SDK used to build the bundle (e.g. "macosx")
|
||||
PlatformName string `json:"platformName,omitempty"`
|
||||
|
||||
// SDKName is the name of the SDK used to build the bundle (e.g. "macosx14.0")
|
||||
SDKName string `json:"sdkName,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
# Cataloger capabilities. See ../README.md for documentation.
|
||||
|
||||
catalogers:
|
||||
- ecosystem: apple # MANUAL
|
||||
name: apple-app-bundle-cataloger # AUTO-GENERATED
|
||||
type: generic # AUTO-GENERATED
|
||||
source: # AUTO-GENERATED
|
||||
file: syft/pkg/cataloger/apple/cataloger.go
|
||||
function: NewAppBundleCataloger
|
||||
selectors: # AUTO-GENERATED
|
||||
- apple
|
||||
- directory
|
||||
- image
|
||||
- installed
|
||||
- package
|
||||
parsers: # AUTO-GENERATED structure
|
||||
- function: parseInfoPlist
|
||||
detector: # AUTO-GENERATED
|
||||
method: glob # AUTO-GENERATED
|
||||
criteria: # AUTO-GENERATED
|
||||
- '**/*.app/Contents/Info.plist'
|
||||
metadata_types: # AUTO-GENERATED
|
||||
- pkg.AppleAppBundleEntry
|
||||
package_types: # AUTO-GENERATED
|
||||
- apple-app-bundle
|
||||
json_schema_types: # AUTO-GENERATED
|
||||
- AppleAppBundleEntry
|
||||
capabilities: # MANUAL - preserved across regeneration
|
||||
- name: license
|
||||
default: false
|
||||
- name: dependency.depth
|
||||
default: []
|
||||
- name: dependency.edges
|
||||
default: ""
|
||||
- name: dependency.kinds
|
||||
default: []
|
||||
- name: package_manager.files.listing
|
||||
default: false
|
||||
- name: package_manager.files.digests
|
||||
default: false
|
||||
- name: package_manager.package_integrity_hash
|
||||
default: false
|
||||
@@ -0,0 +1,12 @@
|
||||
package apple
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// NewAppBundleCataloger returns a cataloger for Apple application bundles (.app), reading their Info.plist (CFBundle).
|
||||
func NewAppBundleCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("apple-app-bundle-cataloger").
|
||||
WithParserByGlobs(parseInfoPlist, "**/*.app/Contents/Info.plist")
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package apple
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_AppleAppBundleCataloger_Globs(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "testdata/install-example").
|
||||
ExpectsResolverContentQueries([]string{
|
||||
"Applications/Slack.app/Contents/Info.plist",
|
||||
}).
|
||||
TestCataloger(t, NewAppBundleCataloger())
|
||||
}
|
||||
|
||||
func Test_AppleAppBundleCataloger(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "xml plist (Slack)",
|
||||
fixture: "testdata/install-example",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Slack",
|
||||
Version: "4.50.128",
|
||||
Type: pkg.AppleAppBundlePkg,
|
||||
FoundBy: "apple-app-bundle-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("Applications/Slack.app/Contents/Info.plist").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
PURL: "", // no standard purl type for apple app bundles
|
||||
Metadata: pkg.AppleAppBundleEntry{
|
||||
BundleIdentifier: "com.tinyspeck.slackmacgap",
|
||||
Name: "Slack",
|
||||
DisplayName: "Slack",
|
||||
Executable: "Slack",
|
||||
ShortVersion: "4.50.128",
|
||||
Version: "450000128",
|
||||
PackageType: "APPL",
|
||||
MinimumSystemVersion: "12.0",
|
||||
Copyright: "©2026 Slack Technologies LLC, a Salesforce company. All rights reserved.",
|
||||
SDKName: "macosx15.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// platform/array fields; values are from a real Ghostty.app
|
||||
name: "platform fields (Ghostty)",
|
||||
fixture: "testdata/native-app-example",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "Ghostty",
|
||||
Version: "1.3.1",
|
||||
Type: pkg.AppleAppBundlePkg,
|
||||
FoundBy: "apple-app-bundle-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("Applications/Ghostty.app/Contents/Info.plist").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
PURL: "",
|
||||
Metadata: pkg.AppleAppBundleEntry{
|
||||
BundleIdentifier: "com.mitchellh.ghostty",
|
||||
Name: "Ghostty",
|
||||
DisplayName: "Ghostty",
|
||||
Executable: "ghostty",
|
||||
ShortVersion: "1.3.1",
|
||||
Version: "15212",
|
||||
PackageType: "APPL",
|
||||
SupportedPlatforms: []string{"MacOSX"},
|
||||
MinimumSystemVersion: "13.0",
|
||||
PlatformName: "macosx",
|
||||
SDKName: "macosx26.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, tt.fixture).
|
||||
Expects(tt.expected, nil).
|
||||
TestCataloger(t, NewAppBundleCataloger())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseInfoPlist(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
wantName string
|
||||
wantVersion string
|
||||
wantBundleID string
|
||||
wantNoPackage bool
|
||||
wantParseErr bool
|
||||
}{
|
||||
{
|
||||
name: "name falls back to executable",
|
||||
fixture: "testdata/parse-cases/name-from-executable.plist",
|
||||
wantName: "Exec",
|
||||
wantVersion: "1.0",
|
||||
},
|
||||
{
|
||||
name: "name falls back to bundle identifier",
|
||||
fixture: "testdata/parse-cases/name-from-identifier.plist",
|
||||
wantName: "com.example.app",
|
||||
wantVersion: "1.0",
|
||||
wantBundleID: "com.example.app",
|
||||
},
|
||||
{
|
||||
name: "version falls back to bundle version",
|
||||
fixture: "testdata/parse-cases/version-from-bundle-version.plist",
|
||||
wantName: "Name",
|
||||
wantVersion: "42",
|
||||
},
|
||||
{
|
||||
name: "no package without a name",
|
||||
fixture: "testdata/parse-cases/no-name.plist",
|
||||
wantNoPackage: true,
|
||||
},
|
||||
{
|
||||
name: "no package without a version",
|
||||
fixture: "testdata/parse-cases/no-version.plist",
|
||||
wantNoPackage: true,
|
||||
},
|
||||
{
|
||||
name: "malformed plist returns an error",
|
||||
fixture: "testdata/parse-cases/malformed.plist",
|
||||
wantParseErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgs, _, err := parseFixture(t, tt.fixture)
|
||||
if tt.wantParseErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantNoPackage {
|
||||
assert.Empty(t, pkgs)
|
||||
return
|
||||
}
|
||||
|
||||
require.Len(t, pkgs, 1)
|
||||
assert.Equal(t, tt.wantName, pkgs[0].Name)
|
||||
assert.Equal(t, tt.wantVersion, pkgs[0].Version)
|
||||
assert.Equal(t, pkg.AppleAppBundlePkg, pkgs[0].Type)
|
||||
assert.Empty(t, pkgs[0].PURL)
|
||||
meta, ok := pkgs[0].Metadata.(pkg.AppleAppBundleEntry)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, tt.wantBundleID, meta.BundleIdentifier)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parseFixture(t *testing.T, path string) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
t.Helper()
|
||||
f, err := os.Open(path)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = f.Close() })
|
||||
return parseInfoPlist(context.Background(), nil, nil, file.NewLocationReadCloser(file.NewLocation(path), f))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package apple
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newAppBundlePackage(name, version string, metadata pkg.AppleAppBundleEntry, location file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Type: pkg.AppleAppBundlePkg,
|
||||
// note: there is no standard purl type for Apple app bundles, so we intentionally leave the PURL empty.
|
||||
Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package apple
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"howett.net/plist"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// infoPlist captures the raw CFBundle keys read from an Info.plist. It is kept separate from the public
|
||||
// pkg.AppleAppBundleEntry so the parser can read fields without committing to expressing all of them.
|
||||
type infoPlist struct {
|
||||
CFBundleIdentifier string `plist:"CFBundleIdentifier"`
|
||||
CFBundleName string `plist:"CFBundleName"`
|
||||
CFBundleDisplayName string `plist:"CFBundleDisplayName"`
|
||||
CFBundleExecutable string `plist:"CFBundleExecutable"`
|
||||
CFBundleShortVersionString string `plist:"CFBundleShortVersionString"`
|
||||
CFBundleVersion string `plist:"CFBundleVersion"`
|
||||
CFBundlePackageType string `plist:"CFBundlePackageType"`
|
||||
CFBundleSupportedPlatforms []string `plist:"CFBundleSupportedPlatforms"`
|
||||
LSMinimumSystemVersion string `plist:"LSMinimumSystemVersion"`
|
||||
MinimumOSVersion string `plist:"MinimumOSVersion"`
|
||||
NSHumanReadableCopyright string `plist:"NSHumanReadableCopyright"`
|
||||
DTPlatformName string `plist:"DTPlatformName"`
|
||||
DTSDKName string `plist:"DTSDKName"`
|
||||
}
|
||||
|
||||
func parseInfoPlist(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
// 5MB cap matches the convention in syft/linux; Info.plist files are far smaller
|
||||
data, err := io.ReadAll(io.LimitReader(reader, 5*1024*1024))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to read file: %w", err)
|
||||
}
|
||||
|
||||
var info infoPlist
|
||||
if _, err := plist.Unmarshal(data, &info); err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to parse plist: %w", err)
|
||||
}
|
||||
|
||||
name := info.CFBundleDisplayName
|
||||
if name == "" {
|
||||
name = info.CFBundleName
|
||||
}
|
||||
if name == "" {
|
||||
name = info.CFBundleExecutable
|
||||
}
|
||||
if name == "" {
|
||||
// last-resort name so identifiable bundles aren't dropped (e.g. com.apple.Safari)
|
||||
name = info.CFBundleIdentifier
|
||||
}
|
||||
|
||||
version := info.CFBundleShortVersionString
|
||||
if version == "" {
|
||||
version = info.CFBundleVersion
|
||||
}
|
||||
|
||||
if name == "" || version == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pkgs := []pkg.Package{
|
||||
newAppBundlePackage(name, version, info.toEntry(), reader.Location),
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// toEntry maps the parsed plist into the public metadata type.
|
||||
func (i infoPlist) toEntry() pkg.AppleAppBundleEntry {
|
||||
return pkg.AppleAppBundleEntry{
|
||||
BundleIdentifier: i.CFBundleIdentifier,
|
||||
Name: i.CFBundleName,
|
||||
DisplayName: i.CFBundleDisplayName,
|
||||
Executable: i.CFBundleExecutable,
|
||||
ShortVersion: i.CFBundleShortVersionString,
|
||||
Version: i.CFBundleVersion,
|
||||
PackageType: i.CFBundlePackageType,
|
||||
SupportedPlatforms: i.CFBundleSupportedPlatforms,
|
||||
MinimumSystemVersion: i.LSMinimumSystemVersion,
|
||||
MinimumOSVersion: i.MinimumOSVersion,
|
||||
Copyright: i.NSHumanReadableCopyright,
|
||||
PlatformName: i.DTPlatformName,
|
||||
SDKName: i.DTSDKName,
|
||||
}
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Slack</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Slack</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Slack</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.tinyspeck.slackmacgap</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.50.128</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>450000128</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>©2026 Slack Technologies LLC, a Salesforce company. All rights reserved.</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx15.5</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>Ghostty</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Ghostty</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>ghostty</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.mitchellh.ghostty</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>15212</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.0</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx26.2</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
this is not a valid plist
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Exec</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.example.app</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>Name</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>Name</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>42</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -54,6 +54,7 @@ const (
|
||||
TerraformPkg Type = "terraform"
|
||||
WordpressPluginPkg Type = "wordpress-plugin"
|
||||
HomebrewPkg Type = "homebrew"
|
||||
AppleAppBundlePkg Type = "apple-app-bundle"
|
||||
ModelPkg Type = "model"
|
||||
)
|
||||
|
||||
@@ -99,6 +100,7 @@ var AllPkgs = []Type{
|
||||
TerraformPkg,
|
||||
WordpressPluginPkg,
|
||||
HomebrewPkg,
|
||||
AppleAppBundlePkg,
|
||||
ModelPkg,
|
||||
}
|
||||
|
||||
|
||||
@@ -160,8 +160,9 @@ func TestTypeFromPURL(t *testing.T) {
|
||||
expectedTypes.Remove(string(HomebrewPkg))
|
||||
expectedTypes.Remove(string(TerraformPkg))
|
||||
expectedTypes.Remove(string(GraalVMNativeImagePkg))
|
||||
expectedTypes.Remove(string(ModelPkg)) // no valid purl for ai artifacts currently
|
||||
expectedTypes.Remove(string(PhpPeclPkg)) // we should always consider this a pear package
|
||||
expectedTypes.Remove(string(ModelPkg)) // no valid purl for ai artifacts currently
|
||||
expectedTypes.Remove(string(AppleAppBundlePkg)) // no standard purl type for apple app bundles
|
||||
expectedTypes.Remove(string(PhpPeclPkg)) // we should always consider this a pear package
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(string(test.expected), func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user