mirror of
https://github.com/anchore/syft.git
synced 2026-10-22 11:02:24 +02:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab69742d25 | ||
|
|
bf8ded3cf8 | ||
|
|
d53a8d283f | ||
|
|
13c6591cb2 | ||
|
|
4ffde9f891 | ||
|
|
06451adc43 | ||
|
|
ecf6b9eff5 | ||
|
|
d61c2cada3 | ||
|
|
0bbcff1ae3 | ||
|
|
f21e439a63 | ||
|
|
c511d509ef | ||
|
|
fdeb78fc19 | ||
|
|
6ac7afb439 | ||
|
|
b23e461731 | ||
|
|
8c63a2403c | ||
|
|
e4ff7f5973 | ||
|
|
37d45de9ff | ||
|
|
26927eab02 | ||
|
|
1dc16fbac4 | ||
|
|
d0e1e7be05 | ||
|
|
57bd6a5f9c | ||
|
|
c60aabad37 |
@@ -0,0 +1,7 @@
|
||||
# check out text files with lf on every platform (regardless of core.autocrlf), letting git detect binary files
|
||||
* text=auto eol=lf
|
||||
|
||||
# fixtures that must keep their crlf line endings byte for byte
|
||||
syft/linux/testdata/unprintable -text
|
||||
syft/pkg/cataloger/bitnami/testdata/no-rel/opt/bitnami/redis/.spdx-redis.spdx -text
|
||||
syft/pkg/cataloger/java/testdata/jar-metadata/**/META-INF/MANIFEST.MF -text
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
# enough; a 4 vCPU instance can't pull much more than that anyway.
|
||||
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
|
||||
# shared cache backend that the trusted release workflow reads from (cache poisoning).
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=16+32/family=m5+m6+m7+m8+c5+c6+c7+c8/spot=price-capacity-optimized/volume=100gb"
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=16+32/family=m6+m7+m8+c6+c7+c8/spot=price-capacity-optimized/volume=100gb"
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
@@ -49,6 +49,13 @@ jobs:
|
||||
Unit-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [Unit-Test-Linux, Unit-Test-Windows]
|
||||
steps:
|
||||
- run: echo All unit tests passed!
|
||||
|
||||
Unit-Test-Linux:
|
||||
name: "Unit tests (Linux)"
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -68,6 +75,27 @@ jobs:
|
||||
- name: Check for capability drift
|
||||
run: make check-capability-drift
|
||||
|
||||
Unit-Test-Windows:
|
||||
name: "Unit tests (Windows)"
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1
|
||||
with:
|
||||
# keep in sync with the go-version default in ./.github/actions/bootstrap
|
||||
go-version: "1.26.2"
|
||||
|
||||
# the full unit suite leans on linux docker fixtures, so this runs the windows-relevant subset
|
||||
- name: Run unit tests
|
||||
run: make unit:windows
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Integration tests"
|
||||
|
||||
+19
-1
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -52,7 +53,7 @@ func main() {
|
||||
raceFlag = " -race"
|
||||
}
|
||||
Run(
|
||||
"go test -count=1 -timeout=30m -v"+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
"go test -count=1 -timeout=30m"+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
run.Env("GODEBUG", "dontfreezetheworld=1"),
|
||||
)
|
||||
},
|
||||
@@ -96,6 +97,23 @@ func main() {
|
||||
},
|
||||
},
|
||||
|
||||
// windows unit tests: deliberately not hooked into "test" (no RunsOn). The full unit suite leans on
|
||||
// linux docker fixture images and posix-only assumptions, so this covers the packages that own
|
||||
// host path handling (where windows bugs live) and skips the tests that need docker image fixtures.
|
||||
// ponytail: curated package list, widen it as more of the suite is made windows-safe.
|
||||
Task{
|
||||
Name: "unit:windows",
|
||||
Description: "run the windows-relevant subset of unit tests",
|
||||
Run: func() {
|
||||
Run("go test -count=1"+
|
||||
` -skip "Image|Squash|AllLayers|MixFileTypes|GivenCoordinates"`+
|
||||
" ./syft/internal/windows/... ./syft/internal/fileresolver/... ./syft/source/directorysource/... ./syft/source/filesource/..."+
|
||||
" ./syft/file ./syft/file/cataloger/filedigest/... ./syft/file/cataloger/filecontent/...",
|
||||
run.Stdout(os.Stderr),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
|
||||
Task{
|
||||
Name: "default",
|
||||
|
||||
@@ -158,6 +158,11 @@ func (l *backgroundLineReader) read(reader io.Reader, stage *progress.Stage) {
|
||||
|
||||
l.lock.Unlock()
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
log.WithFields("error", err).Debug("unable to read attestation output")
|
||||
// keep draining so the writer never blocks on a full pipe
|
||||
_, _ = io.Copy(io.Discard, reader)
|
||||
}
|
||||
}
|
||||
|
||||
func (l backgroundLineReader) Lines() []string {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/filecataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/file/cataloger/executable"
|
||||
"github.com/anchore/syft/syft/file/cataloger/filecontent"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/cpp"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
|
||||
@@ -145,14 +147,18 @@ func (cfg Catalog) ToFilesConfig() filecataloging.Config {
|
||||
log.WithFields("error", err).Warn("unable to configure file hashers")
|
||||
}
|
||||
|
||||
c := filecataloging.DefaultConfig()
|
||||
c.Selection = cfg.File.Metadata.Selection
|
||||
c.Hashers = hashers
|
||||
c.Content.Globs = cfg.File.Content.Globs
|
||||
c.Content.SkipFilesAboveSize = cfg.File.Content.SkipFilesAboveSize
|
||||
c.Executable.Globs = cfg.File.Executable.Globs
|
||||
|
||||
return c
|
||||
return filecataloging.Config{
|
||||
Selection: cfg.File.Metadata.Selection,
|
||||
Hashers: hashers,
|
||||
Content: filecontent.Config{
|
||||
Globs: cfg.File.Content.Globs,
|
||||
SkipFilesAboveSize: cfg.File.Content.SkipFilesAboveSize,
|
||||
},
|
||||
Executable: executable.Config{
|
||||
MIMETypes: executable.DefaultConfig().MIMETypes,
|
||||
Globs: cfg.File.Executable.Globs,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Catalog) ToLicenseConfig() cataloging.LicenseConfig {
|
||||
|
||||
@@ -64,7 +64,7 @@ func (c *fileConfig) PostLoad() error {
|
||||
}
|
||||
|
||||
func (c *fileConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&c.Metadata.Selection, `select which files should be captured by the file-metadata cataloger and included in the SBOM.
|
||||
descriptions.Add(&c.Metadata.Selection, `select which files should be captured by the file-metadata cataloger and included in the SBOM.
|
||||
Options include:
|
||||
- "all": capture all files from the search space
|
||||
- "owned-by-package": capture only files owned by packages
|
||||
|
||||
@@ -248,7 +248,7 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.4.1 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.2.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.6 // indirect
|
||||
|
||||
@@ -758,8 +758,8 @@ github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+
|
||||
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
|
||||
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
|
||||
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nwaples/rardecode/v2 v2.4.1 h1:F7zNW2LdAuuBThHWXQaiFUGVD/sef299NfWSB1nHAl4=
|
||||
github.com/nwaples/rardecode/v2 v2.4.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||
|
||||
@@ -3,7 +3,7 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.1.11"
|
||||
JSONSchemaVersion = "16.1.10"
|
||||
|
||||
// Changelog
|
||||
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
|
||||
@@ -17,5 +17,4 @@ const (
|
||||
// 16.1.8 - add VcpkgManifest metadata type for vcpkg manifest support
|
||||
// 16.1.9 - add Symbols (grouped by owning package import path) to GolangBinaryBuildinfoEntry metadata
|
||||
// 16.1.10 - add SafeTensorsModelInfo metadata type for the safetensors AI model cataloger
|
||||
// 16.1.11 - add file executable toolchain information
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
// maxScannedLineSize is the longest line NewLineScanner will return before Scan stops with bufio.ErrTooLong.
|
||||
// The buffer only grows to this size when a line needs it.
|
||||
const maxScannedLineSize = 1024 * 1024
|
||||
|
||||
// NewLineScanner returns a bufio.Scanner that accepts lines up to 1MB rather than the 64KB default, since
|
||||
// package metadata files carry long single-line fields (descriptions, license text, dependency lists). Callers
|
||||
// must still check Err() after scanning.
|
||||
func NewLineScanner(r io.Reader) *bufio.Scanner {
|
||||
s := bufio.NewScanner(r)
|
||||
s.Buffer(nil, maxScannedLineSize)
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewLineScanner(t *testing.T) {
|
||||
long := strings.Repeat("a", 100*1024)
|
||||
s := NewLineScanner(strings.NewReader(long + "\nb\n"))
|
||||
require.True(t, s.Scan())
|
||||
require.Equal(t, long, s.Text())
|
||||
require.True(t, s.Scan())
|
||||
require.Equal(t, "b", s.Text())
|
||||
require.False(t, s.Scan())
|
||||
require.NoError(t, s.Err())
|
||||
|
||||
s = NewLineScanner(strings.NewReader(strings.Repeat("a", maxScannedLineSize+1)))
|
||||
require.False(t, s.Scan())
|
||||
require.ErrorIs(t, s.Err(), bufio.ErrTooLong)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package packagemetadata
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// untypedFieldAllowlist holds package metadata fields that are allowed to carry untyped (interface) values.
|
||||
// Values in these fields come straight from whatever a parser decoded, so they are not shaped by syft
|
||||
// and can hold anything an encoder or hasher chokes on. Every entry is part of existing SBOM output and
|
||||
// cannot be retyped without breaking it; new entries need a reason and a review.
|
||||
var untypedFieldAllowlist = map[string]string{
|
||||
"GGUFFileHeader.RemainingKeyValues": "raw GGUF header key-values; retyping changes the header output and metadataHash",
|
||||
}
|
||||
|
||||
// TestNoUntypedMetadataFields fails when a package metadata type gains a field typed any, []any,
|
||||
// map[string]any (or any other interface) that is not on the allowlist.
|
||||
func TestNoUntypedMetadataFields(t *testing.T) {
|
||||
var hits []string
|
||||
for _, ty := range AllTypes() {
|
||||
rt := reflect.TypeOf(ty)
|
||||
collectUntyped(rt, rt.Name(), map[reflect.Type]bool{}, &hits)
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
for _, h := range hits {
|
||||
seen[h] = true
|
||||
if _, ok := untypedFieldAllowlist[h]; !ok {
|
||||
t.Errorf("untyped field %s: use a concrete type, or add it to untypedFieldAllowlist with a reason", h)
|
||||
}
|
||||
}
|
||||
for k := range untypedFieldAllowlist {
|
||||
if !seen[k] {
|
||||
t.Errorf("stale untypedFieldAllowlist entry %s: field no longer exists or is now typed", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collectUntyped records "Struct.Field" for every field whose type reaches an interface, descending
|
||||
// through pointers, slices, arrays, maps and nested syft-owned structs.
|
||||
func collectUntyped(rt reflect.Type, path string, visited map[reflect.Type]bool, hits *[]string) {
|
||||
switch rt.Kind() {
|
||||
case reflect.Interface:
|
||||
*hits = append(*hits, path)
|
||||
case reflect.Pointer, reflect.Slice, reflect.Array:
|
||||
collectUntyped(rt.Elem(), path, visited, hits)
|
||||
case reflect.Map:
|
||||
collectUntyped(rt.Key(), path, visited, hits)
|
||||
collectUntyped(rt.Elem(), path, visited, hits)
|
||||
case reflect.Struct:
|
||||
if visited[rt] {
|
||||
return
|
||||
}
|
||||
if rt.Name() != "" && !strings.HasPrefix(rt.PkgPath(), "github.com/anchore/syft/") {
|
||||
// a third-party type in the model carries whatever shape the library gives it (the same leak as an
|
||||
// interface field), so it counts as untyped. The standard library is left alone.
|
||||
if strings.Contains(strings.Split(rt.PkgPath(), "/")[0], ".") {
|
||||
*hits = append(*hits, path)
|
||||
}
|
||||
return
|
||||
}
|
||||
visited[rt] = true
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
f := rt.Field(i)
|
||||
// unexported embedded structs still promote their exported fields to the encoders and the hasher. A
|
||||
// field is only out of reach when it is neither encoded nor hashed by SetID.
|
||||
if (!f.IsExported() && !f.Anonymous) || (f.Tag.Get("json") == "-" && f.Tag.Get("hash") == "ignore") {
|
||||
continue
|
||||
}
|
||||
collectUntyped(f.Type, rt.Name()+"."+f.Name, visited, hits)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ type LicenseInfo struct {
|
||||
// LicenseByURL returns the license ID and name for a given URL from the SPDX license list.
|
||||
// The URL should match one of the URLs in the seeAlso field of an SPDX license.
|
||||
// The scheme (http:// or https://) is stripped before lookup, so both schemes match.
|
||||
// opensource.org URLs additionally match regardless of the form used (see normalizeOpenSourceOrgURL).
|
||||
func LicenseByURL(url string) (LicenseInfo, bool) {
|
||||
url = strings.TrimSpace(url)
|
||||
url = stripScheme(url)
|
||||
@@ -52,9 +53,51 @@ func LicenseByURL(url string) (LicenseInfo, bool) {
|
||||
ID: id,
|
||||
}, true
|
||||
}
|
||||
if normalized, ok := normalizeOpenSourceOrgURL(url); ok {
|
||||
if id, exists := openSourceOrgURLToLicense[normalized]; exists {
|
||||
return LicenseInfo{
|
||||
ID: id,
|
||||
}, true
|
||||
}
|
||||
}
|
||||
return LicenseInfo{}, false
|
||||
}
|
||||
|
||||
// openSourceOrgURLToLicense indexes the opensource.org entries of urlToLicense by normalized URL.
|
||||
var openSourceOrgURLToLicense = buildOpenSourceOrgURLToLicense(urlToLicense)
|
||||
|
||||
func buildOpenSourceOrgURLToLicense(urls map[string]string) map[string]string {
|
||||
index := make(map[string]string)
|
||||
ambiguous := make(map[string]bool)
|
||||
for url, id := range urls {
|
||||
normalized, ok := normalizeOpenSourceOrgURL(url)
|
||||
if !ok || ambiguous[normalized] {
|
||||
continue
|
||||
}
|
||||
if existing, exists := index[normalized]; exists && existing != id {
|
||||
// don't guess between licenses that only differ by URL form
|
||||
delete(index, normalized)
|
||||
ambiguous[normalized] = true
|
||||
continue
|
||||
}
|
||||
index[normalized] = id
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// normalizeOpenSourceOrgURL reduces a scheme-less opensource.org URL to a canonical form.
|
||||
// SPDX has changed these URLs over time (e.g. 3.29.0 replaced opensource.org/licenses/MIT and
|
||||
// opensource.org/license/mit/ with opensource.org/license/MIT), while package metadata in the wild
|
||||
// still uses the older forms. The www. prefix, /licenses/ vs /license/, case, and trailing slash are ignored.
|
||||
func normalizeOpenSourceOrgURL(url string) (string, bool) {
|
||||
url = strings.TrimPrefix(strings.ToLower(url), "www.")
|
||||
if !strings.HasPrefix(url, "opensource.org/") {
|
||||
return "", false
|
||||
}
|
||||
url = strings.Replace(url, "opensource.org/licenses/", "opensource.org/license/", 1)
|
||||
return strings.TrimSuffix(url, "/"), true
|
||||
}
|
||||
|
||||
// stripScheme removes http:// or https:// prefix from a URL.
|
||||
// This allows a single map entry to match both schemes.
|
||||
func stripScheme(url string) string {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This file was generated using data from https://spdx.org/licenses/licenses.json
|
||||
package spdxlicense
|
||||
|
||||
const Version = "3.28.0"
|
||||
const Version = "3.29.0"
|
||||
|
||||
var licenseIDs = map[string]string{
|
||||
"0bsd": "0BSD",
|
||||
@@ -116,6 +116,7 @@ var licenseIDs = map[string]string{
|
||||
"aswfdigitalassets1.0.0": "ASWF-Digital-Assets-1.0",
|
||||
"aswfdigitalassets1.1": "ASWF-Digital-Assets-1.1",
|
||||
"aswfdigitalassets1.1.0": "ASWF-Digital-Assets-1.1",
|
||||
"atcgame": "atc-game",
|
||||
"baekmuk": "Baekmuk",
|
||||
"bahyph": "Bahyph",
|
||||
"barr": "Barr",
|
||||
@@ -142,8 +143,11 @@ var licenseIDs = map[string]string{
|
||||
"briangladman2.0clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman2clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman3.0.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3.0.0clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"briangladman3.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3.0clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"briangladman3clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"bsd1.0.0clause": "BSD-1-Clause",
|
||||
"bsd1.0clause": "BSD-1-Clause",
|
||||
"bsd1clause": "BSD-1-Clause",
|
||||
@@ -154,6 +158,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2.0.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0.0clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2.0.0clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2.0.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2.0clause": "BSD-2-Clause",
|
||||
"bsd2.0clausedarwin": "BSD-2-Clause-Darwin",
|
||||
@@ -162,6 +167,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2.0clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2clause": "BSD-2-Clause",
|
||||
"bsd2clausedarwin": "BSD-2-Clause-Darwin",
|
||||
@@ -170,6 +176,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd3.0.0clause": "BSD-3-Clause",
|
||||
"bsd3.0.0clauseacpica": "BSD-3-Clause-acpica",
|
||||
@@ -184,6 +191,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3.0.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3.0.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0.0clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3.0.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3.0.0clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd3.0clause": "BSD-3-Clause",
|
||||
@@ -199,6 +207,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3.0clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd3clause": "BSD-3-Clause",
|
||||
@@ -214,6 +223,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd4.0.0clause": "BSD-4-Clause",
|
||||
@@ -232,12 +242,15 @@ var licenseIDs = map[string]string{
|
||||
"bsd4reno": "BSD-4.3RENO",
|
||||
"bsd4tahoe": "BSD-4.3TAHOE",
|
||||
"bsdadvertisingacknowledgement": "BSD-Advertising-Acknowledgement",
|
||||
"bsdasktoendorse": "BSD-ask-to-endorse",
|
||||
"bsdattributionhpnddisclaimer": "BSD-Attribution-HPND-disclaimer",
|
||||
"bsdinfernonettverk": "BSD-Inferno-Nettverk",
|
||||
"bsdmarkmodifications": "BSD-Mark-Modifications",
|
||||
"bsdprotection": "BSD-Protection",
|
||||
"bsdsourcealtgpl": "BSD-Source-alt-GPL",
|
||||
"bsdsourcebeginningfile": "BSD-Source-beginning-file",
|
||||
"bsdsourcecode": "BSD-Source-Code",
|
||||
"bsdsourcecodenodisclaimer": "BSD-Source-Code-no-disclaimer",
|
||||
"bsdsystemics": "BSD-Systemics",
|
||||
"bsdsystemicsw3.0.0works": "BSD-Systemics-W3Works",
|
||||
"bsdsystemicsw3.0works": "BSD-Systemics-W3Works",
|
||||
@@ -246,6 +259,7 @@ var licenseIDs = map[string]string{
|
||||
"bsl1.0": "BSL-1.0",
|
||||
"bsl1.0.0": "BSL-1.0",
|
||||
"buddy": "Buddy",
|
||||
"bugroff": "Bugroff",
|
||||
"busl1": "BUSL-1.1",
|
||||
"busl1.1": "BUSL-1.1",
|
||||
"busl1.1.0": "BUSL-1.1",
|
||||
@@ -316,8 +330,11 @@ var licenseIDs = map[string]string{
|
||||
"ccbync3.0": "CC-BY-NC-3.0",
|
||||
"ccbync3.0.0": "CC-BY-NC-3.0",
|
||||
"ccbync3.0.0de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3.0.0igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync3.0de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3.0igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync3de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync4": "CC-BY-NC-4.0",
|
||||
"ccbync4.0": "CC-BY-NC-4.0",
|
||||
"ccbync4.0.0": "CC-BY-NC-4.0",
|
||||
@@ -592,6 +609,7 @@ var licenseIDs = map[string]string{
|
||||
"fair": "Fair",
|
||||
"fbm": "FBM",
|
||||
"fdkaac": "FDK-AAC",
|
||||
"fdkmpegh": "FDK-MPEG-H",
|
||||
"fergusontwofish": "Ferguson-Twofish",
|
||||
"frameworx1": "Frameworx-1.0",
|
||||
"frameworx1.0": "Frameworx-1.0",
|
||||
@@ -767,6 +785,9 @@ var licenseIDs = map[string]string{
|
||||
"hippocratic2": "Hippocratic-2.1",
|
||||
"hippocratic2.1": "Hippocratic-2.1",
|
||||
"hippocratic2.1.0": "Hippocratic-2.1",
|
||||
"hippocratic3.0.0core": "Hippocratic-3.0-core",
|
||||
"hippocratic3.0core": "Hippocratic-3.0-core",
|
||||
"hippocratic3core": "Hippocratic-3.0-core",
|
||||
"hp1986": "HP-1986",
|
||||
"hp1986.0": "HP-1986",
|
||||
"hp1986.0.0": "HP-1986",
|
||||
@@ -813,6 +834,7 @@ var licenseIDs = map[string]string{
|
||||
"imlib2": "Imlib2",
|
||||
"imlib2.0": "Imlib2",
|
||||
"imlib2.0.0": "Imlib2",
|
||||
"informatica": "Informatica",
|
||||
"infozip": "Info-ZIP",
|
||||
"innernet2": "Inner-Net-2.0",
|
||||
"innernet2.0": "Inner-Net-2.0",
|
||||
@@ -1011,6 +1033,9 @@ var licenseIDs = map[string]string{
|
||||
"mulanpsl2.0.0": "MulanPSL-2.0",
|
||||
"multics": "Multics",
|
||||
"mup": "Mup",
|
||||
"mvt1": "MVT-1.1",
|
||||
"mvt1.1": "MVT-1.1",
|
||||
"mvt1.1.0": "MVT-1.1",
|
||||
"naist2003": "NAIST-2003",
|
||||
"naist2003.0": "NAIST-2003",
|
||||
"naist2003.0.0": "NAIST-2003",
|
||||
@@ -1489,6 +1514,8 @@ var urlToLicense = map[string]string{
|
||||
"cdla.io/permissive-1-0": "CDLA-Permissive-1.0",
|
||||
"cdla.io/sharing-1-0": "CDLA-Sharing-1.0",
|
||||
"celestrak.org/publications/AIAA/2006-6753/faq.php": "SGP4",
|
||||
"cgit.freebsd.org/src/plain/sys/dev/aic7xxx/aic7xxx_osm.c": "BSD-Source-alt-GPL",
|
||||
"cgit.freebsd.org/src/tree/usr.bin/sockstat/sockstat.c": "BSD-2-Clause-pos-unchanged",
|
||||
"cgit.freedesktop.org/libbsd/tree/man/setproctitle.3bsd": "libutil-David-Nugent",
|
||||
"coil.apotheon.org/plaintext/01.0.txt": "COIL-1.0",
|
||||
"core.tcl-lang.org/tk/file?name=compat/unistd.h": "HPND-UC",
|
||||
@@ -1512,6 +1539,7 @@ var urlToLicense = map[string]string{
|
||||
"creativecommons.org/licenses/by-nc/2.0/legalcode": "CC-BY-NC-2.0",
|
||||
"creativecommons.org/licenses/by-nc/2.5/legalcode": "CC-BY-NC-2.5",
|
||||
"creativecommons.org/licenses/by-nc/3.0/de/legalcode": "CC-BY-NC-3.0-DE",
|
||||
"creativecommons.org/licenses/by-nc/3.0/igo/legalcode": "CC-BY-NC-3.0-IGO",
|
||||
"creativecommons.org/licenses/by-nc/3.0/legalcode": "CC-BY-NC-3.0",
|
||||
"creativecommons.org/licenses/by-nc/4.0/legalcode": "CC-BY-NC-4.0",
|
||||
"creativecommons.org/licenses/by-nd-nc/1.0/legalcode": "CC-BY-NC-ND-1.0",
|
||||
@@ -1564,6 +1592,7 @@ var urlToLicense = map[string]string{
|
||||
"directory.fsf.org/wiki/License:Fdk": "FDK-AAC",
|
||||
"doc.qt.io/archives/3.3/license.html": "QPL-1.0",
|
||||
"doc.qt.nokia.com/3.3/license.html": "QPL-1.0",
|
||||
"docs.mvt.re/en/latest/license/": "MVT-1.1",
|
||||
"docs.omniverse.nvidia.com/install-guide/latest/common/licenses.html": "AML-glslang",
|
||||
"docs.oracle.com/cd/E81115_01/html/E81116/licenses.html": "man2html",
|
||||
"docs.python.org/3/license.html": "Python-2.0.1",
|
||||
@@ -1669,6 +1698,7 @@ var urlToLicense = map[string]string{
|
||||
"fedoraproject.org/wiki/Licensing:Mplus?rd=Licensing/mplus": "mplus",
|
||||
"fedoraproject.org/wiki/Licensing:ThorPublicLicense": "TPL-1.0",
|
||||
"firstdonoharm.dev/version/2/1/license.html": "Hippocratic-2.1",
|
||||
"firstdonoharm.dev/version/3/0/core.txt": "Hippocratic-3.0-core",
|
||||
"forge.gouv.qc.ca/licence/fr/liliq-v1-1/": "LiLiQ-P-1.1",
|
||||
"forge.gouv.qc.ca/licence/liliq-p": "LiLiQ-R-1.1",
|
||||
"forge.gouv.qc.ca/licence/liliq-p/": "LiLiQ-P-1.1",
|
||||
@@ -1686,68 +1716,70 @@ var urlToLicense = map[string]string{
|
||||
"ftp://ftp.tin.org/pub/news/utils/newsx/newsx-1.6.tar.gz": "Zeeff",
|
||||
"gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/": "ClArtistic",
|
||||
"git.denx.de/?p=u-boot.git;a=blob;f=arch/powerpc/cpu/ppc4xx/miiphy.c;h=297155fdafa064b955e53e9832de93bfb0cfb85b;hb=9fab4bf4cc077c21e43941866f3f2c196f28670d": "IBM-pibs",
|
||||
"git.enlightenment.org/legacy/imlib2.git/tree/COPYING": "Imlib2",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4": "Linux-man-pages-1-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5": "Linux-man-pages-copyleft-var",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h": "Linux-OpenIB",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19": "HPND-sell-variant",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63": "BSD-4.3RENO",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type=heads#L39-51": "Furuseth",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type=heads#L19-23": "PADL",
|
||||
"git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT": "FTL",
|
||||
"git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h=Version_2_6_13pre": "LOOP",
|
||||
"git.savannah.gnu.org/cgit/gnulib.git/tree/modules/COPYING?id=7b08932179d0d6b017f7df01a2ddf6e096b038e3": "FSFULLRSD",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n1788": "BSD-4.3TAHOE",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n74": "Latex2e-translated-notice",
|
||||
"git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id=0062df360c2d17d57f6af19b0e444c51feb99036": "Kazlib",
|
||||
"git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h=v1.21.3&id=40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6": "FSFAP-no-warranty-disclaimer",
|
||||
"git.sr.ht/~nabijaczleweli/ossp-var": "OSSP",
|
||||
"github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15": "BSD-4.3TAHOE",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt": "ASWF-Digital-Assets-1.0",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt": "ASWF-Digital-Assets-1.1",
|
||||
"github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/xmlada/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/BuildCraft/BuildCraft/blob/623d323b1868712f29f4a8b0979a02e8d1835131/LICENSE": "MMPL-1.0.1",
|
||||
"github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md": "Community-Spec-1.0",
|
||||
"github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt": "TU-Berlin-2.0",
|
||||
"github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md": "LPD-document",
|
||||
"github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf": "etalab-2.0",
|
||||
"github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT": "hdparm",
|
||||
"github.com/Distrotech/transfig/blob/master/transfig/transfig.c": "Xfig",
|
||||
"github.com/DrItanium/maya/blob/master/LICENSE.CLIPS": "Clips",
|
||||
"github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12": "metamail",
|
||||
"github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt": "Hippocratic-2.1",
|
||||
"github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32": "HPND-Fenneberg-Livingston",
|
||||
"github.com/GNOME/gcr/blob/master/docs/COPYING": "GCR-docs",
|
||||
"github.com/GNOME/libxml2/blob/master/dict.c": "ISC-Veillard",
|
||||
"github.com/HDFGroup/hdf5/?tab=License-1-ov-file#readme": "HDF5",
|
||||
"github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt": "MIT-Khronos-old",
|
||||
"github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949": "AML-glslang",
|
||||
"github.com/MariaDB/server/blob/11.6/libmysqld/lib_sql.cc": "Boehm-GC-without-fee",
|
||||
"github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md": "DRL-1.0",
|
||||
"github.com/OGRECave/ogre/blob/master/OgreMain/include/OgreSingleton.h#L28C3-L35C46": "Game-Programming-Gems",
|
||||
"github.com/Perl/perl5/blob/blead/util.c#L6136": "Martin-Birgmeier",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22": "Pixar",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt": "Pixar",
|
||||
"github.com/RTimothyEdwards/magic/blob/master/LICENSE": "HPND-UC-export-US",
|
||||
"github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE": "libselinux-1.0",
|
||||
"github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h": "Brian-Gladman-3-Clause",
|
||||
"github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26": "FBM",
|
||||
"github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c": "dtoa",
|
||||
"github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md": "DRL-1.1",
|
||||
"github.com/Slicer/Slicer/blob/main/License.txt": "3D-Slicer-1.0",
|
||||
"github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT": "UCAR",
|
||||
"github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119": "BSD-3-Clause-acpica",
|
||||
"github.com/adaptivecomputing/torque/blob/master/PBS_License.txt": "OpenPBS-2.3",
|
||||
"github.com/adversary-org/wtfnmf/raw/3f2cd8235a64350a57a51b9739715edaea63ec1a/COPYING.WTFNMFPL-utf8": "WTFNMFPL",
|
||||
"github.com/adversary-org/wtfnmf/raw/refs/tags/1.0/COPYING.WTFNMFPL": "WTFNMFPL",
|
||||
"github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148": "TTWL",
|
||||
"github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29": "Caldera-no-preamble",
|
||||
"github.com/audacious-media-player/audacious/blob/master/COPYING": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/audacious-media-player/audacious/blob/master/src/audacious/main.cc": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"git.enlightenment.org/legacy/imlib2.git/tree/COPYING": "Imlib2",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4": "Linux-man-pages-1-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5": "Linux-man-pages-copyleft-var",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h": "Linux-OpenIB",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19": "HPND-sell-variant",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63": "BSD-4.3RENO",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type=heads#L39-51": "Furuseth",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type=heads#L19-23": "PADL",
|
||||
"git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT": "FTL",
|
||||
"git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h=Version_2_6_13pre": "LOOP",
|
||||
"git.savannah.gnu.org/cgit/gnulib.git/tree/modules/COPYING?id=7b08932179d0d6b017f7df01a2ddf6e096b038e3": "FSFULLRSD",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n1788": "BSD-4.3TAHOE",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n74": "Latex2e-translated-notice",
|
||||
"git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id=0062df360c2d17d57f6af19b0e444c51feb99036": "Kazlib",
|
||||
"git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h=v1.21.3&id=40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6": "FSFAP-no-warranty-disclaimer",
|
||||
"git.sr.ht/~nabijaczleweli/ossp-var": "OSSP",
|
||||
"github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15": "BSD-4.3TAHOE",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt": "ASWF-Digital-Assets-1.0",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt": "ASWF-Digital-Assets-1.1",
|
||||
"github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/xmlada/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/BuildCraft/BuildCraft/blob/623d323b1868712f29f4a8b0979a02e8d1835131/LICENSE": "MMPL-1.0.1",
|
||||
"github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md": "Community-Spec-1.0",
|
||||
"github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt": "TU-Berlin-2.0",
|
||||
"github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md": "LPD-document",
|
||||
"github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf": "etalab-2.0",
|
||||
"github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT": "hdparm",
|
||||
"github.com/Distrotech/transfig/blob/master/transfig/transfig.c": "Xfig",
|
||||
"github.com/DrItanium/maya/blob/master/LICENSE.CLIPS": "Clips",
|
||||
"github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12": "metamail",
|
||||
"github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt": "Hippocratic-2.1",
|
||||
"github.com/Fraunhofer-IIS/mpeghdec/blob/main/LICENSE.txt": "FDK-MPEG-H",
|
||||
"github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32": "HPND-Fenneberg-Livingston",
|
||||
"github.com/GNOME/gcr/blob/master/docs/COPYING": "GCR-docs",
|
||||
"github.com/GNOME/libxml2/blob/master/dict.c": "ISC-Veillard",
|
||||
"github.com/HDFGroup/hdf5/?tab=License-1-ov-file#readme": "HDF5",
|
||||
"github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt": "MIT-Khronos-old",
|
||||
"github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949": "AML-glslang",
|
||||
"github.com/MariaDB/server/blob/11.6/libmysqld/lib_sql.cc": "Boehm-GC-without-fee",
|
||||
"github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md": "DRL-1.0",
|
||||
"github.com/OGRECave/ogre/blob/master/OgreMain/include/OgreSingleton.h#L28C3-L35C46": "Game-Programming-Gems",
|
||||
"github.com/Perl/perl5/blob/blead/util.c#L6136": "Martin-Birgmeier",
|
||||
"github.com/PicoEFI/PicoEFI/blob/2263ac1a2bc98e5534d64a4896d3cfbbae00745a/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt": "BSD-Source-Code-no-disclaimer",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22": "Pixar",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt": "Pixar",
|
||||
"github.com/RTimothyEdwards/magic/blob/master/LICENSE": "HPND-UC-export-US",
|
||||
"github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE": "libselinux-1.0",
|
||||
"github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h": "Brian-Gladman-3-Clause",
|
||||
"github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26": "FBM",
|
||||
"github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c": "dtoa",
|
||||
"github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md": "DRL-1.1",
|
||||
"github.com/Slicer/Slicer/blob/main/License.txt": "3D-Slicer-1.0",
|
||||
"github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT": "UCAR",
|
||||
"github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119": "BSD-3-Clause-acpica",
|
||||
"github.com/adaptivecomputing/torque/blob/master/PBS_License.txt": "OpenPBS-2.3",
|
||||
"github.com/adversary-org/wtfnmf/raw/3f2cd8235a64350a57a51b9739715edaea63ec1a/COPYING.WTFNMFPL-utf8": "WTFNMFPL",
|
||||
"github.com/adversary-org/wtfnmf/raw/refs/tags/1.0/COPYING.WTFNMFPL": "WTFNMFPL",
|
||||
"github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148": "TTWL",
|
||||
"github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29": "Caldera-no-preamble",
|
||||
"github.com/audacious-media-player/audacious/blob/master/COPYING": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/audacious-media-player/audacious/blob/master/src/audacious/main.cc": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/aws/mit-0": "MIT-0",
|
||||
"github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE": "MIT-0",
|
||||
"github.com/bagder/curl/blob/master/COPYING": "curl",
|
||||
@@ -1772,6 +1804,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/file/file/blob/master/COPYING": "BSD-2-Clause-Darwin",
|
||||
"github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_acosh.c": "SunPro",
|
||||
"github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_lgammal.c": "SunPro",
|
||||
"github.com/freebsd/pkg/blob/main/src/add.c": "BSD-2-Clause-pos-unchanged",
|
||||
"github.com/gabegundy/atlserver/blob/master/License.txt": "MS-LPL",
|
||||
"github.com/greymass/swift-eosio/blob/master/LICENSE": "BSD-3-Clause-No-Military-License",
|
||||
"github.com/hamano/man2html/blob/master/man2html.c": "man2html",
|
||||
@@ -1784,6 +1817,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/jsommers/pytricia/blob/master/patricia.c#L33-L67": "BSD-4-Clause",
|
||||
"github.com/kfish/xsel/blob/master/COPYING": "HPND-sell-variant",
|
||||
"github.com/kohler/t1utils/blob/master/LICENSE": "MIT-Click",
|
||||
"github.com/krb5/krb5/blob/kfw-4.0.1-beta1/src/lib/crypto/builtin/aes/aesopt.h": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L111-L133": "HPND-export2-US",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L1157-L1182": "HPND-export-US-modify",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L140-L156": "Brian-Gladman-2-Clause",
|
||||
@@ -1793,6 +1827,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L831-L852": "HPND-export-US-acknowledgement",
|
||||
"github.com/krzyzanowskim/CryptoSwift/blob/main/LICENSE": "CryptoSwift",
|
||||
"github.com/lattera/freebsd/blob/master/sys/cam/cam.c#L4": "BSD-Source-beginning-file",
|
||||
"github.com/libarchive/libarchive/blob/master/libarchive_fe/line_reader.c": "BSD-2-Clause-pos-unchanged",
|
||||
"github.com/libcheck/check/blob/master/checkmk/checkmk.in": "checkmk",
|
||||
"github.com/libjpeg-turbo/libjpeg-turbo/blob/main/README.ijg#L117-L161": "IJG",
|
||||
"github.com/lsof-org/lsof/blob/master/COPYING": "lsof",
|
||||
@@ -1805,6 +1840,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/mm2/Little-CMS/blob/master/src/cmssm.c#L207": "softSurfer",
|
||||
"github.com/mruby/mruby/blob/83d12f8d52522cdb7c8cc46fad34821359f453e6/mrbgems/mruby-dir/src/Win/dirent.c#L127-L140": "HPND-Kevlin-Henney",
|
||||
"github.com/mtoyoda/sl/blob/master/LICENSE": "SL",
|
||||
"github.com/mvt-project/mvt/blob/main/LICENSE": "MVT-1.1",
|
||||
"github.com/n8n-io/n8n/blob/master/LICENSE.md": "SUL-1.0",
|
||||
"github.com/ncbi/datasets/blob/0ea4cd16b61e5b799d9cc55aecfa016d6c9bd2bf/LICENSE.md": "NCBI-PD",
|
||||
"github.com/ncbi/datasets/blob/master/LICENSE.md": "NCBI-PD",
|
||||
@@ -1815,6 +1851,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/novnc/noVNC/blob/master/core/crypto/des.js#L24": "Widget-Workshop",
|
||||
"github.com/oetiker/rrdtool-1.x/blob/master/src/plbasename.c#L8-L11": "gtkbook",
|
||||
"github.com/open-quantum-safe/liboqs/blob/40b01fdbb270f8614fde30e65d30e9da18c02393/src/common/rand/rand_nist.c#L1-L15": "NIST-Software",
|
||||
"github.com/open-webui/open-webui/blob/main/LICENSE": "BSD-3-Clause-OpenWebUI",
|
||||
"github.com/openjdk/jdk/blob/6162e2c5213c5dd7c1127fd9616b543efa898962/LICENSE": "GPL-2.0-or-later",
|
||||
"github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10": "SSH-OpenSSH",
|
||||
"github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h": "SSH-short",
|
||||
@@ -1846,6 +1883,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/sigmavirus24/x11-ssh-askpass/blob/master/dynlist.c": "HPND-sell-variant-MIT-disclaimer-rev",
|
||||
"github.com/signal11/hidapi/blob/master/LICENSE-orig.txt": "HIDAPI",
|
||||
"github.com/slogan621/gtkbook": "gtkbook",
|
||||
"github.com/sudo-project/sudo/blob/c1307ea9ff340ce0538779f8e456501461fc44b7/plugins/sudoers/redblack.c#L24-L43": "BSD-ask-to-endorse",
|
||||
"github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT": "TU-Berlin-1.0",
|
||||
"github.com/tats/w3m/blob/master/COPYING": "w3m",
|
||||
"github.com/tcheneau/Routing/blob/f09f46fcfe636107f22f2c98348188a65a135d98/README.md": "NIST-PD",
|
||||
@@ -1856,6 +1894,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/uim/libgcroots/blob/master/COPYING": "Boehm-GC",
|
||||
"github.com/usnistgov/fipy/blob/86aaa5c2ba2c6f1be19593c5986071cf6568cc34/LICENSE.rst": "NIST-PD-fallback",
|
||||
"github.com/usnistgov/jsip/blob/59700e6926cbe96c5cdae897d9a7d2656b42abe3/LICENSE": "NIST-PD-fallback",
|
||||
"github.com/vattam/BSDGames/blob/master/atc/extern.c#L36": "atc-game",
|
||||
"github.com/vixie/cron/tree/545b3f5246824a9cda5905eeb7cf019c95e66995": "Vixie-Cron",
|
||||
"github.com/vstroebel/jpeg-encoder/blob/main/src/fdct.rs#L1-L72": "IJG",
|
||||
"github.com/wernerd/ZRTPCPP/blob/6b3cd8e6783642292bad0c21e3e5e5ce45ff3e03/cryptcommon/twofish.c#L113C3-L127": "Ferguson-Twofish",
|
||||
@@ -1869,6 +1908,7 @@ var urlToLicense = map[string]string{
|
||||
"gitlab.com/embeddable-common-lisp/ecl/-/blob/develop/src/lsp/loop.lsp": "LOOP",
|
||||
"gitlab.com/libeigen/eigen/-/blob/master/COPYING.MINPACK": "Minpack",
|
||||
"gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/code/loop.lisp": "LOOP",
|
||||
"gitlab.freedesktop.org/dbus/dbus/-/blob/789d97ad53044f94351a958e785c2923643b346a/COPYING#L11-207": "AFL-2.1",
|
||||
"gitlab.freedesktop.org/freetype/freetype/-/raw/master/docs/FTL.TXT": "FTL",
|
||||
"gitlab.freedesktop.org/libopenraw/libopenraw/blob/master/lib/ljpegdecompressor.cpp#L32": "Cornell-Lossless-JPEG",
|
||||
"gitlab.freedesktop.org/mesa/glw/-/blob/master/README?ref_type=heads": "SGI-OpenGL",
|
||||
@@ -1892,6 +1932,7 @@ var urlToLicense = map[string]string{
|
||||
"gitlab.freedesktop.org/xorg/lib/libxinerama/-/blob/cc22c2f60c3862482562955116d5455263b443dc/COPYING#L44-66": "X11-no-permit-persons",
|
||||
"gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type=heads#L108-117": "HPND-doc-sell",
|
||||
"gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type=heads#L70-77": "HPND-doc",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/dd5c2595a42d3ff0c4f18d9b53d1f6c3fd934fd4/COPYING#L365-389": "MIT",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L1781": "HPND-sell-MIT-disclaimer-xserver",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L239": "DEC-3-Clause",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L752": "Adobe-Display-PostScript",
|
||||
@@ -1906,6 +1947,7 @@ var urlToLicense = map[string]string{
|
||||
"heasarc.gsfc.nasa.gov/docs/software/ftools/fv/doc/license.html": "CFITSIO",
|
||||
"helixcommunity.org/content/rpsl": "RPSL-1.0",
|
||||
"hstechdocs.helpsystems.com/manuals/globalscape/archive/cuteftp6/open_ssl_license_agreement.htm": "OpenSSL-standalone",
|
||||
"interoperable-europe.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt": "EUPL-1.2",
|
||||
"java.net/projects/javaeetutorial/pages/BerkeleyLicense": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"javaee.github.io/glassfish/LICENSE": "CDDL-1.1",
|
||||
"jogamp.org/git/?p=gluegen.git;a=blob_plain;f=LICENSE.txt": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
@@ -1924,6 +1966,7 @@ var urlToLicense = map[string]string{
|
||||
"library.netapp.com/ecm/ecm_download_file/ECMP1196395": "OpenSSL-standalone",
|
||||
"license.coscl.org.cn/MulanPSL/": "MulanPSL-1.0",
|
||||
"license.coscl.org.cn/MulanPSL2": "MulanPSL-2.0",
|
||||
"license.mvt.re/1.1/": "MVT-1.1",
|
||||
"lists.gnu.org/archive/html/autoconf/2012-04/msg00061.html": "FSFULLRWD",
|
||||
"lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2002-November/006304.html": "HPND",
|
||||
"mariadb.com/bsl11/": "BUSL-1.1",
|
||||
@@ -1965,99 +2008,105 @@ var urlToLicense = map[string]string{
|
||||
"opensource.linux-mirror.org/licenses/afl-1.1.txt": "AFL-1.1",
|
||||
"opensource.linux-mirror.org/licenses/afl-1.2.txt": "AFL-1.2",
|
||||
"opensource.linux-mirror.org/licenses/afl-2.1.txt": "AFL-2.1",
|
||||
"opensource.org/license/apache-2-0": "Apache-2.0",
|
||||
"opensource.org/license/mit/": "MIT",
|
||||
"opensource.org/license/olfl-1-3/": "OLFL-1.3",
|
||||
"opensource.org/license/osc-license-1-0": "OSC-1.0",
|
||||
"opensource.org/licenses/0BSD": "0BSD",
|
||||
"opensource.org/licenses/AGPL-3.0": "AGPL-3.0-or-later",
|
||||
"opensource.org/licenses/APL-1.0": "APL-1.0",
|
||||
"opensource.org/licenses/Apache-1.1": "Apache-1.1",
|
||||
"opensource.org/licenses/Apache-2.0": "Apache-2.0",
|
||||
"opensource.org/licenses/Artistic-1.0": "Artistic-1.0-cl8",
|
||||
"opensource.org/licenses/BSD-2-Clause": "BSD-2-Clause",
|
||||
"opensource.org/licenses/BSD-3-Clause": "BSD-3-Clause",
|
||||
"opensource.org/licenses/BSDplusPatent": "BSD-2-Clause-Patent",
|
||||
"opensource.org/licenses/BSL-1.0": "BSL-1.0",
|
||||
"opensource.org/licenses/CAL-1.0": "CAL-1.0-Combined-Work-Exception",
|
||||
"opensource.org/licenses/CATOSL-1.1": "CATOSL-1.1",
|
||||
"opensource.org/licenses/CNRI-Python": "CNRI-Python",
|
||||
"opensource.org/licenses/CPAL-1.0": "CPAL-1.0",
|
||||
"opensource.org/licenses/CPL-1.0": "CPL-1.0",
|
||||
"opensource.org/licenses/CUA-OPL-1.0": "CUA-OPL-1.0",
|
||||
"opensource.org/licenses/ECL-1.0": "ECL-1.0",
|
||||
"opensource.org/licenses/ECL-2.0": "ECL-2.0",
|
||||
"opensource.org/licenses/EFL-1.0": "EFL-1.0",
|
||||
"opensource.org/licenses/EFL-2.0": "EFL-2.0",
|
||||
"opensource.org/licenses/EPL-1.0": "EPL-1.0",
|
||||
"opensource.org/licenses/EUDatagrid": "EUDatagrid",
|
||||
"opensource.org/licenses/EUPL-1.1": "EUPL-1.1",
|
||||
"opensource.org/licenses/EUPL-1.2": "EUPL-1.2",
|
||||
"opensource.org/licenses/Entessa": "Entessa",
|
||||
"opensource.org/licenses/Fair": "Fair",
|
||||
"opensource.org/licenses/Frameworx-1.0": "Frameworx-1.0",
|
||||
"opensource.org/licenses/GPL-2.0": "GPL-2.0-or-later",
|
||||
"opensource.org/licenses/GPL-3.0": "GPL-3.0-or-later",
|
||||
"opensource.org/licenses/HPND": "HPND",
|
||||
"opensource.org/licenses/IPA": "IPA",
|
||||
"opensource.org/licenses/IPL-1.0": "IPL-1.0",
|
||||
"opensource.org/licenses/ISC": "ISC",
|
||||
"opensource.org/licenses/Intel": "Intel",
|
||||
"opensource.org/licenses/LGPL-2.1": "LGPL-2.1-or-later",
|
||||
"opensource.org/licenses/LGPL-3.0": "LGPL-3.0-or-later",
|
||||
"opensource.org/licenses/LPL-1.0": "LPL-1.0",
|
||||
"opensource.org/licenses/LPL-1.02": "LPL-1.02",
|
||||
"opensource.org/licenses/LPPL-1.3c": "LPPL-1.3c",
|
||||
"opensource.org/licenses/LiLiQ-P-1.1": "LiLiQ-P-1.1",
|
||||
"opensource.org/licenses/LiLiQ-R-1.1": "LiLiQ-R-1.1",
|
||||
"opensource.org/licenses/LiLiQ-Rplus-1.1": "LiLiQ-Rplus-1.1",
|
||||
"opensource.org/licenses/MIT": "MIT",
|
||||
"opensource.org/licenses/MPL-1.0": "MPL-1.0",
|
||||
"opensource.org/licenses/MPL-1.1": "MPL-1.1",
|
||||
"opensource.org/licenses/MPL-2.0": "MPL-2.0-no-copyleft-exception",
|
||||
"opensource.org/licenses/MS-PL": "MS-PL",
|
||||
"opensource.org/licenses/MS-RL": "MS-RL",
|
||||
"opensource.org/licenses/MirOS": "MirOS",
|
||||
"opensource.org/licenses/Motosoto": "Motosoto",
|
||||
"opensource.org/licenses/Multics": "Multics",
|
||||
"opensource.org/licenses/NASA-1.3": "NASA-1.3",
|
||||
"opensource.org/licenses/NCSA": "NCSA",
|
||||
"opensource.org/licenses/NGPL": "NGPL",
|
||||
"opensource.org/licenses/NOSL3.0": "NPOSL-3.0",
|
||||
"opensource.org/licenses/NTP": "NTP",
|
||||
"opensource.org/licenses/Naumen": "Naumen",
|
||||
"opensource.org/licenses/OCLC-2.0": "OCLC-2.0",
|
||||
"opensource.org/licenses/OFL-1.1": "OFL-1.1-no-RFN",
|
||||
"opensource.org/licenses/OGTSL": "OGTSL",
|
||||
"opensource.org/licenses/OPL-2.1": "OSET-PL-2.1",
|
||||
"opensource.org/licenses/OSL-1.0": "OSL-1.0",
|
||||
"opensource.org/licenses/OSL-2.1": "OSL-2.1",
|
||||
"opensource.org/licenses/OSL-3.0": "OSL-3.0",
|
||||
"opensource.org/licenses/PHP-3.0": "PHP-3.0",
|
||||
"opensource.org/licenses/PostgreSQL": "PostgreSQL",
|
||||
"opensource.org/licenses/Python-2.0": "Python-2.0",
|
||||
"opensource.org/licenses/QPL-1.0": "QPL-1.0",
|
||||
"opensource.org/licenses/RPL-1.1": "RPL-1.1",
|
||||
"opensource.org/licenses/RPL-1.5": "RPL-1.5",
|
||||
"opensource.org/licenses/RPSL-1.0": "RPSL-1.0",
|
||||
"opensource.org/licenses/RSCPL": "RSCPL",
|
||||
"opensource.org/licenses/SISSL": "SISSL",
|
||||
"opensource.org/licenses/SPL-1.0": "SPL-1.0",
|
||||
"opensource.org/licenses/SimPL-2.0": "SimPL-2.0",
|
||||
"opensource.org/licenses/Sleepycat": "Sleepycat",
|
||||
"opensource.org/licenses/UCL-1.0": "UCL-1.0",
|
||||
"opensource.org/licenses/UPL": "UPL-1.0",
|
||||
"opensource.org/licenses/VSL-1.0": "VSL-1.0",
|
||||
"opensource.org/licenses/W3C": "W3C",
|
||||
"opensource.org/licenses/Watcom-1.0": "Watcom-1.0",
|
||||
"opensource.org/licenses/Xnet": "Xnet",
|
||||
"opensource.org/licenses/ZPL-2.0": "ZPL-2.0",
|
||||
"opensource.org/licenses/Zlib": "Zlib",
|
||||
"opensource.org/licenses/afl-3.0": "AFL-3.0",
|
||||
"opensource.org/licenses/artistic-license-2.0": "Artistic-2.0",
|
||||
"opensource.org/licenses/attribution": "AAL",
|
||||
"opensource.org/licenses/cddl1": "CDDL-1.0",
|
||||
"opensource.org/licenses/nokia": "Nokia",
|
||||
"opensource.org/license/0BSD": "0BSD",
|
||||
"opensource.org/license/AAL": "AAL",
|
||||
"opensource.org/license/AFL-3.0": "AFL-3.0",
|
||||
"opensource.org/license/AGPL-3.0": "AGPL-3.0-or-later",
|
||||
"opensource.org/license/AGPL-3.0-only": "AGPL-3.0-only",
|
||||
"opensource.org/license/APL-1.0": "APL-1.0",
|
||||
"opensource.org/license/Artistic-1.0": "Artistic-1.0-cl8",
|
||||
"opensource.org/license/Artistic-2.0": "Artistic-2.0",
|
||||
"opensource.org/license/BSD-2-Clause": "BSD-2-Clause",
|
||||
"opensource.org/license/BSD-2-Clause-Patent": "BSD-2-Clause-Patent",
|
||||
"opensource.org/license/BSD-3-Clause": "BSD-3-Clause",
|
||||
"opensource.org/license/BSL-1.0": "BSL-1.0",
|
||||
"opensource.org/license/CAL-1.0": "CAL-1.0-Combined-Work-Exception",
|
||||
"opensource.org/license/CATOSL-1.1": "CATOSL-1.1",
|
||||
"opensource.org/license/CDDL-1.0": "CDDL-1.0",
|
||||
"opensource.org/license/CNRI-Python": "CNRI-Python",
|
||||
"opensource.org/license/CNRI-Python-GPL-Compatible": "CNRI-Python-GPL-Compatible",
|
||||
"opensource.org/license/CPAL-1.0": "CPAL-1.0",
|
||||
"opensource.org/license/CPL-1.0": "CPL-1.0",
|
||||
"opensource.org/license/CUA-OPL-1.0": "CUA-OPL-1.0",
|
||||
"opensource.org/license/ECL-1.0": "ECL-1.0",
|
||||
"opensource.org/license/ECL-2.0": "ECL-2.0",
|
||||
"opensource.org/license/EFL-1.0": "EFL-1.0",
|
||||
"opensource.org/license/EFL-2.0": "EFL-2.0",
|
||||
"opensource.org/license/EPL-1.0": "EPL-1.0",
|
||||
"opensource.org/license/EUDatagrid": "EUDatagrid",
|
||||
"opensource.org/license/EUPL-1.1": "EUPL-1.1",
|
||||
"opensource.org/license/EUPL-1.2": "EUPL-1.2",
|
||||
"opensource.org/license/Entessa": "Entessa",
|
||||
"opensource.org/license/Fair": "Fair",
|
||||
"opensource.org/license/Frameworx-1.0": "Frameworx-1.0",
|
||||
"opensource.org/license/GPL-2.0": "GPL-2.0-or-later",
|
||||
"opensource.org/license/GPL-2.0-only": "GPL-2.0-only",
|
||||
"opensource.org/license/GPL-3.0": "GPL-3.0-or-later",
|
||||
"opensource.org/license/GPL-3.0-only": "GPL-3.0-only",
|
||||
"opensource.org/license/HPND": "HPND",
|
||||
"opensource.org/license/IPA": "IPA",
|
||||
"opensource.org/license/IPL-1.0": "IPL-1.0",
|
||||
"opensource.org/license/ISC": "ISC",
|
||||
"opensource.org/license/Intel": "Intel",
|
||||
"opensource.org/license/LGPL-2.1": "LGPL-2.1-or-later",
|
||||
"opensource.org/license/LGPL-2.1-only": "LGPL-2.1-only",
|
||||
"opensource.org/license/LGPL-3.0": "LGPL-3.0-or-later",
|
||||
"opensource.org/license/LPL-1.0": "LPL-1.0",
|
||||
"opensource.org/license/LPL-1.02": "LPL-1.02",
|
||||
"opensource.org/license/LPPL-1.3c": "LPPL-1.3c",
|
||||
"opensource.org/license/LiLiQ-P-1.1": "LiLiQ-P-1.1",
|
||||
"opensource.org/license/LiLiQ-R-1.1": "LiLiQ-R-1.1",
|
||||
"opensource.org/license/LiLiQ-Rplus-1.1": "LiLiQ-Rplus-1.1",
|
||||
"opensource.org/license/MIT": "MIT",
|
||||
"opensource.org/license/MPL-1.0": "MPL-1.0",
|
||||
"opensource.org/license/MPL-1.1": "MPL-1.1",
|
||||
"opensource.org/license/MPL-2.0": "MPL-2.0-no-copyleft-exception",
|
||||
"opensource.org/license/MS-PL": "MS-PL",
|
||||
"opensource.org/license/MS-RL": "MS-RL",
|
||||
"opensource.org/license/MirOS": "MirOS",
|
||||
"opensource.org/license/Motosoto": "Motosoto",
|
||||
"opensource.org/license/Multics": "Multics",
|
||||
"opensource.org/license/NASA-1.3": "NASA-1.3",
|
||||
"opensource.org/license/NCSA": "NCSA",
|
||||
"opensource.org/license/NGPL": "NGPL",
|
||||
"opensource.org/license/NPOSL-3.0": "NPOSL-3.0",
|
||||
"opensource.org/license/NTP": "NTP",
|
||||
"opensource.org/license/Naumen": "Naumen",
|
||||
"opensource.org/license/Nokia": "Nokia",
|
||||
"opensource.org/license/OCLC-2.0": "OCLC-2.0",
|
||||
"opensource.org/license/OFL-1.1": "OFL-1.1-no-RFN",
|
||||
"opensource.org/license/OGTSL": "OGTSL",
|
||||
"opensource.org/license/OLFL-1.3": "OLFL-1.3",
|
||||
"opensource.org/license/OSC-1.0": "OSC-1.0",
|
||||
"opensource.org/license/OSET-PL-2.1": "OSET-PL-2.1",
|
||||
"opensource.org/license/OSL-1.0": "OSL-1.0",
|
||||
"opensource.org/license/OSL-2.1": "OSL-2.1",
|
||||
"opensource.org/license/OSL-3.0": "OSL-3.0",
|
||||
"opensource.org/license/PHP-3.0": "PHP-3.0",
|
||||
"opensource.org/license/PSF-2-0": "PSF-2.0",
|
||||
"opensource.org/license/PostgreSQL": "PostgreSQL",
|
||||
"opensource.org/license/Python-2.0": "Python-2.0",
|
||||
"opensource.org/license/Python-2.0.1": "Python-2.0.1",
|
||||
"opensource.org/license/QPL-1.0": "QPL-1.0",
|
||||
"opensource.org/license/RPL-1.1": "RPL-1.1",
|
||||
"opensource.org/license/RPL-1.5": "RPL-1.5",
|
||||
"opensource.org/license/RPSL-1.0": "RPSL-1.0",
|
||||
"opensource.org/license/RSCPL": "RSCPL",
|
||||
"opensource.org/license/SISSL": "SISSL",
|
||||
"opensource.org/license/SPL-1.0": "SPL-1.0",
|
||||
"opensource.org/license/SiMPL-2.0": "SimPL-2.0",
|
||||
"opensource.org/license/Sleepycat": "Sleepycat",
|
||||
"opensource.org/license/UCL-1.0": "UCL-1.0",
|
||||
"opensource.org/license/UPL-1.0": "UPL-1.0",
|
||||
"opensource.org/license/VSL-1.0": "VSL-1.0",
|
||||
"opensource.org/license/W3C": "W3C",
|
||||
"opensource.org/license/Watcom-1.0": "Watcom-1.0",
|
||||
"opensource.org/license/Xnet": "Xnet",
|
||||
"opensource.org/license/ZPL-2.0": "ZPL-2.0",
|
||||
"opensource.org/license/Zlib": "Zlib",
|
||||
"opensource.org/license/apache-1.1": "Apache-1.1",
|
||||
"opensource.org/license/apache-2.0": "Apache-2.0",
|
||||
"opensource.org/license/curl": "curl",
|
||||
"oss.sgi.com/projects/FreeB/": "SGI-B-1.1",
|
||||
"oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html": "SGI-B-1.0",
|
||||
"oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf": "SGI-B-2.0",
|
||||
@@ -2082,37 +2131,40 @@ var urlToLicense = map[string]string{
|
||||
"raw.githubusercontent.com/NTIA/scos-sensor/refs/heads/master/LICENSE.md": "NTIA-PD",
|
||||
"raw.githubusercontent.com/OpenMDW/OpenMDW/refs/heads/main/1.0/LICENSE.openmdw": "OpenMDW-1.0",
|
||||
"raw.githubusercontent.com/blackhole89/notekit/master/data/fonts/Charter%20license.txt": "Bitstream-Charter",
|
||||
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
|
||||
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
"research.scea.com/scea_shared_source_license.html": "SCEA",
|
||||
"romanrm.net/mit-zero": "MIT-0",
|
||||
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
|
||||
"slicer.org/LICENSE": "3D-Slicer-1.0",
|
||||
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
|
||||
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
|
||||
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
|
||||
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
|
||||
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
|
||||
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
|
||||
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
|
||||
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
|
||||
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
|
||||
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
|
||||
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
|
||||
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
|
||||
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
|
||||
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
|
||||
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/mdump.c": "Informatica",
|
||||
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/msend.c": "Informatica",
|
||||
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
|
||||
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
"research.scea.com/scea_shared_source_license.html": "SCEA",
|
||||
"romanrm.net/mit-zero": "MIT-0",
|
||||
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
|
||||
"scancode-licensedb.aboutcode.org/bsd-unchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
|
||||
"slicer.org/LICENSE": "3D-Slicer-1.0",
|
||||
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
|
||||
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
|
||||
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
|
||||
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
|
||||
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
|
||||
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
|
||||
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
|
||||
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
|
||||
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
|
||||
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
|
||||
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
|
||||
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
|
||||
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
|
||||
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
|
||||
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=libiberty/strcasecmp.c;h=131d81c2ce7881fa48c363dc5bf5fb302c61ce0b;hb=HEAD": "BSD-4.3RENO",
|
||||
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=readline/readline/support/wcwidth.c;h=0f5ec995796f4813abbcf4972aec0378ab74722a;hb=HEAD#l55": "HPND-Markus-Kuhn",
|
||||
"sourceware.org/git/?p=bzip2.git;a=blob;f=LICENSE;hb=bzip2-1.0.6": "bzip2-1.0.6",
|
||||
@@ -2129,6 +2181,7 @@ var urlToLicense = map[string]string{
|
||||
"tahoe-lafs.org/trac/tahoe-lafs/browser/trunk/COPYING.TGPPL.rst": "TGPPL-1.0",
|
||||
"ti.arc.nasa.gov/opensource/nosa/": "NASA-1.3",
|
||||
"trac.enlightenment.org/e/browser/trunk/imlib2/COPYING": "Imlib2",
|
||||
"tunes.org/legalese/bugroff.html": "Bugroff",
|
||||
"ubuntu.com/legal/font-licence": "Ubuntu-font-1.0",
|
||||
"unlicense.org/": "Unlicense",
|
||||
"vimdoc.sourceforge.net/htmldoc/uganda.html": "Vim",
|
||||
@@ -2139,6 +2192,7 @@ var urlToLicense = map[string]string{
|
||||
"web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html": "OSL-2.0",
|
||||
"web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm": "OSL-2.1",
|
||||
"web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html": "Interbase-1.0",
|
||||
"web.archive.org/web/20061013013632/http://opensource.linux-mirror.org/licenses/afl-2.1.txt": "AFL-2.1",
|
||||
"web.archive.org/web/20080207013128/http://www.gnome.org/fonts/": "Bitstream-Vera",
|
||||
"web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html": "Zimbra-1.3",
|
||||
"web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
@@ -2243,11 +2297,11 @@ var urlToLicense = map[string]string{
|
||||
"www.jython.org/license.html": "CNRI-Jython",
|
||||
"www.kermitproject.org/ck90.html#source": "HPND-export-US",
|
||||
"www.kernel.org/doc/man-pages/licenses.html": "Linux-man-pages-copyleft",
|
||||
"www.latex-project.org/lppl/LPPL-1.3c.txt": "LPPL-1.3c",
|
||||
"www.latex-project.org/lppl/lppl-1-0.txt": "LPPL-1.0",
|
||||
"www.latex-project.org/lppl/lppl-1-1.txt": "LPPL-1.1",
|
||||
"www.latex-project.org/lppl/lppl-1-2.txt": "LPPL-1.2",
|
||||
"www.latex-project.org/lppl/lppl-1-3a.txt": "LPPL-1.3a",
|
||||
"www.latex-project.org/lppl/lppl-1-3c.txt": "LPPL-1.3c",
|
||||
"www.lemis.com/grog/UNIX/ancient-source-all.pdf": "Caldera",
|
||||
"www.libpng.org/pub/png/src/libpng-LICENSE.txt": "libpng-2.0",
|
||||
"www.mail-archive.com/debian-bugs-rc@lists.debian.org/msg11494.html": "Symlinks",
|
||||
@@ -2269,16 +2323,17 @@ var urlToLicense = map[string]string{
|
||||
"www.netlib.org/lapack/LICENSE.txt": "BSD-3-Clause-Open-MPI",
|
||||
"www.netlib.org/minpack/disclaimer": "Minpack",
|
||||
"www.networkcultures.org/_uploads/%233notebook_telekommunist.pdf": "PPL",
|
||||
"www.oclc.org/research/activities/software/license/v2final.htm": "OCLC-2.0",
|
||||
"www.ogc.org/ogc/software/1.0": "OGC-1.0",
|
||||
"www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2": "CERN-OHL-W-2.0",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1": "CERN-OHL-1.1",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2": "CERN-OHL-1.2",
|
||||
"www.open-mpi.org/community/license.php": "BSD-3-Clause-Open-MPI",
|
||||
"www.opencascade.com/content/occt-public-license": "OCCT-PL",
|
||||
"www.opendatacommons.org/licenses/odbl/1.0/": "ODbL-1.0",
|
||||
"www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt": "OGTSL",
|
||||
"www.openhub.net/licenses/mslpl": "MS-LPL",
|
||||
"www.nist.gov/open/copyright-fair-use-and-licensing-statements-srd-data-software-and-technical-series-publications": "NIST-Software",
|
||||
"www.oclc.org/research/activities/software/license/v2final.htm": "OCLC-2.0",
|
||||
"www.ogc.org/ogc/software/1.0": "OGC-1.0",
|
||||
"www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2": "CERN-OHL-W-2.0",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1": "CERN-OHL-1.1",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2": "CERN-OHL-1.2",
|
||||
"www.open-mpi.org/community/license.php": "BSD-3-Clause-Open-MPI",
|
||||
"www.opencascade.com/content/occt-public-license": "OCCT-PL",
|
||||
"www.opendatacommons.org/licenses/odbl/1.0/": "ODbL-1.0",
|
||||
"www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt": "OGTSL",
|
||||
"www.openhub.net/licenses/mslpl": "MS-LPL",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=1cae062821881f41b73012ba816434897abf4205": "OLDAP-2.6",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=37b4b3f6cc4bf34e1d3dec61e69914b9819d8894": "NBPL-1.0",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=42b0383c50c299977b5893ee695cf4e486fb0dc7": "OLDAP-1.2",
|
||||
@@ -2301,7 +2356,7 @@ var urlToLicense = map[string]string{
|
||||
"www.opensource.apple.com/license/apsl/": "APSL-2.0",
|
||||
"www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE": "APSL-1.1",
|
||||
"www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c": "Spencer-99",
|
||||
"www.opensource.org/licenses/EPL-2.0": "EPL-2.0",
|
||||
"www.opensource.org/license/EPL-2.0": "EPL-2.0",
|
||||
"www.openssl.org/source/license.html": "OpenSSL",
|
||||
"www.osetfoundation.org/public-license": "OSET-PL-2.1",
|
||||
"www.parliament.uk/site-information/copyright-parliament/open-parliament-licence/": "OPL-UK-3.0",
|
||||
|
||||
@@ -123,6 +123,96 @@ func TestLicenseByURL_AlternateScheme(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLicenseByURL_OpenSourceOrgVariants(t *testing.T) {
|
||||
// SPDX has changed the form of opensource.org URLs across list versions (e.g. 3.29.0 moved
|
||||
// opensource.org/licenses/MIT to opensource.org/license/MIT); every form should keep resolving.
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantID string
|
||||
}{
|
||||
{name: "old plural path", url: "http://opensource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "new singular path", url: "https://opensource.org/license/MIT", wantID: "MIT"},
|
||||
{name: "lowercase with trailing slash", url: "https://opensource.org/license/mit/", wantID: "MIT"},
|
||||
{name: "plural path with trailing slash", url: "https://opensource.org/licenses/MIT/", wantID: "MIT"},
|
||||
{name: "www prefix", url: "https://www.opensource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "uppercase host", url: "https://WWW.OpenSource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "old plural path BSD-3-Clause", url: "https://opensource.org/licenses/BSD-3-Clause", wantID: "BSD-3-Clause"},
|
||||
{name: "new singular path BSD-3-Clause", url: "https://opensource.org/license/BSD-3-Clause", wantID: "BSD-3-Clause"},
|
||||
{name: "lowercase BSD-3-Clause", url: "https://opensource.org/licenses/bsd-3-clause", wantID: "BSD-3-Clause"},
|
||||
{name: "old plural path Apache-2.0", url: "http://opensource.org/licenses/Apache-2.0", wantID: "Apache-2.0"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
info, found := LicenseByURL(tt.url)
|
||||
if !found {
|
||||
t.Fatalf("LicenseByURL(%q) found = false, want true", tt.url)
|
||||
}
|
||||
if info.ID != tt.wantID {
|
||||
t.Errorf("LicenseByURL(%q) ID = %v, want %v", tt.url, info.ID, tt.wantID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLicenseByURL_NonOpenSourceOrgStaysExact(t *testing.T) {
|
||||
// the relaxed matching is scoped to opensource.org; other hosts may have case-sensitive paths
|
||||
if _, found := LicenseByURL("https://www.apache.org/licenses/license-2.0"); found {
|
||||
t.Errorf("LicenseByURL() found = true for case-altered non-opensource.org URL, want false")
|
||||
}
|
||||
if _, found := LicenseByURL("https://opensource.org/licenses/not-a-real-license"); found {
|
||||
t.Errorf("LicenseByURL() found = true for unknown opensource.org URL, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeOpenSourceOrgURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
url string
|
||||
want string
|
||||
wantOK bool
|
||||
}{
|
||||
{url: "opensource.org/licenses/MIT", want: "opensource.org/license/mit", wantOK: true},
|
||||
{url: "opensource.org/license/mit/", want: "opensource.org/license/mit", wantOK: true},
|
||||
{url: "www.opensource.org/licenses/EPL-2.0", want: "opensource.org/license/epl-2.0", wantOK: true},
|
||||
{url: "lists.opensource.org/pipermail/license-discuss", wantOK: false},
|
||||
{url: "www.apache.org/licenses/LICENSE-2.0", wantOK: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.url, func(t *testing.T) {
|
||||
got, ok := normalizeOpenSourceOrgURL(tt.url)
|
||||
if ok != tt.wantOK {
|
||||
t.Fatalf("normalizeOpenSourceOrgURL(%q) ok = %v, want %v", tt.url, ok, tt.wantOK)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("normalizeOpenSourceOrgURL(%q) = %q, want %q", tt.url, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOpenSourceOrgURLToLicense(t *testing.T) {
|
||||
index := buildOpenSourceOrgURLToLicense(map[string]string{
|
||||
"opensource.org/licenses/MIT": "MIT",
|
||||
"opensource.org/license/mit/": "MIT",
|
||||
"opensource.org/licenses/Foo": "Foo-1.0",
|
||||
"opensource.org/license/foo": "Foo-2.0",
|
||||
"opensource.org/license/FOO/": "Foo-1.0",
|
||||
"www.apache.org/licenses/Bar": "Bar",
|
||||
})
|
||||
|
||||
if got := index["opensource.org/license/mit"]; got != "MIT" {
|
||||
t.Errorf("index[mit] = %q, want MIT", got)
|
||||
}
|
||||
if got, exists := index["opensource.org/license/foo"]; exists {
|
||||
t.Errorf("index[foo] = %q, want no entry for URL forms mapping to different licenses", got)
|
||||
}
|
||||
if len(index) != 1 {
|
||||
t.Errorf("len(index) = %d, want 1 (non-opensource.org URLs excluded): %v", len(index), index)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripScheme(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.1.11/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.1.10/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -1398,13 +1398,6 @@
|
||||
"elfSecurityFeatures": {
|
||||
"$ref": "#/$defs/ELFSecurityFeatures",
|
||||
"description": "ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF."
|
||||
},
|
||||
"toolchains": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/Toolchain"
|
||||
},
|
||||
"type": "array",
|
||||
"description": "Toolchains captures information about the compiler, linker, runtime, or other toolchains used to build (or otherwise exist within) the executable."
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4457,27 +4450,6 @@
|
||||
],
|
||||
"description": "TerraformLockProviderEntry represents a single provider entry in a Terraform dependency lock file (.terraform.lock.hcl)."
|
||||
},
|
||||
"Toolchain": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name is the name of the toolchain (e.g., \"gcc\", \"clang\", \"rust\", \"lld\", \"GNU AS\", etc.)."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version is the version of the toolchain."
|
||||
},
|
||||
"component": {
|
||||
"type": "string",
|
||||
"description": "Component indicates which part of the toolchain this represents (e.g., compiler, linker, assembler)."
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"component"
|
||||
]
|
||||
},
|
||||
"VcpkgManifest": {
|
||||
"properties": {
|
||||
"description": {
|
||||
|
||||
@@ -26,11 +26,8 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// MIMETypes are the MIME types that will be considered for executable cataloging.
|
||||
MIMETypes []string `json:"mime-types" yaml:"mime-types" mapstructure:"mime-types"`
|
||||
|
||||
// Globs are the glob patterns that will be used to filter which files are cataloged.
|
||||
Globs []string `json:"globs" yaml:"globs" mapstructure:"globs"`
|
||||
Globs []string `json:"globs" yaml:"globs" mapstructure:"globs"`
|
||||
}
|
||||
|
||||
type Cataloger struct {
|
||||
@@ -119,51 +116,6 @@ func processExecutableLocation(loc file.Location, resolver file.Resolver) (exec
|
||||
return processExecutable(loc, uReader)
|
||||
}
|
||||
|
||||
func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file.Executable, error) {
|
||||
data := file.Executable{}
|
||||
|
||||
// determine the executable format
|
||||
|
||||
format, err := findExecutableFormat(reader)
|
||||
if err != nil {
|
||||
log.Debugf("unable to determine executable kind for %v: %v", loc.RealPath, err)
|
||||
return nil, fmt.Errorf("unable to determine executable kind: %w", err)
|
||||
}
|
||||
|
||||
if format == "" {
|
||||
// this is not an "unknown", so just log -- this binary does not have parseable data in it
|
||||
log.Debugf("unable to determine executable format for %q", loc.RealPath)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data.Format = format
|
||||
|
||||
switch format {
|
||||
case file.ELF:
|
||||
if err = findELFFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine ELF features")
|
||||
err = fmt.Errorf("unable to determine ELF features: %w", err)
|
||||
}
|
||||
case file.PE:
|
||||
if err = findPEFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine PE features")
|
||||
err = fmt.Errorf("unable to determine PE features: %w", err)
|
||||
}
|
||||
case file.MachO:
|
||||
if err = findMachoFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine Macho features")
|
||||
err = fmt.Errorf("unable to determine Macho features: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// always allocate collections for presentation
|
||||
if data.ImportedLibraries == nil {
|
||||
data.ImportedLibraries = []string{}
|
||||
}
|
||||
|
||||
return &data, err
|
||||
}
|
||||
|
||||
func catalogingProgress(locations int64) *monitor.TaskProgress {
|
||||
info := monitor.GenericTask{
|
||||
Title: monitor.Title{
|
||||
@@ -210,6 +162,51 @@ func locationMatchesGlob(loc file.Location, globs []string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file.Executable, error) {
|
||||
data := file.Executable{}
|
||||
|
||||
// determine the executable format
|
||||
|
||||
format, err := findExecutableFormat(reader)
|
||||
if err != nil {
|
||||
log.Debugf("unable to determine executable kind for %v: %v", loc.RealPath, err)
|
||||
return nil, fmt.Errorf("unable to determine executable kind: %w", err)
|
||||
}
|
||||
|
||||
if format == "" {
|
||||
// this is not an "unknown", so just log -- this binary does not have parseable data in it
|
||||
log.Debugf("unable to determine executable format for %q", loc.RealPath)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data.Format = format
|
||||
|
||||
switch format {
|
||||
case file.ELF:
|
||||
if err = findELFFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine ELF features")
|
||||
err = fmt.Errorf("unable to determine ELF features: %w", err)
|
||||
}
|
||||
case file.PE:
|
||||
if err = findPEFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine PE features")
|
||||
err = fmt.Errorf("unable to determine PE features: %w", err)
|
||||
}
|
||||
case file.MachO:
|
||||
if err = findMachoFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine Macho features")
|
||||
err = fmt.Errorf("unable to determine Macho features: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// always allocate collections for presentation
|
||||
if data.ImportedLibraries == nil {
|
||||
data.ImportedLibraries = []string{}
|
||||
}
|
||||
|
||||
return &data, err
|
||||
}
|
||||
|
||||
func findExecutableFormat(reader unionreader.UnionReader) (file.ExecutableFormat, error) {
|
||||
// read the first sector of the file
|
||||
buf := make([]byte, 512)
|
||||
|
||||
@@ -35,33 +35,10 @@ func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) erro
|
||||
data.ELFSecurityFeatures = findELFSecurityFeatures(f)
|
||||
data.HasEntrypoint = elfHasEntrypoint(f)
|
||||
data.HasExports = elfHasExports(f)
|
||||
data.Toolchains = elfToolchains(reader, f)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func elfToolchains(reader unionreader.UnionReader, f *elf.File) []file.Toolchain {
|
||||
// parse the .comment section and symbol tables once and share them across the detectors
|
||||
comments := elfComments(f)
|
||||
symbols := elfSymbolSet(f)
|
||||
return includeNoneNil(
|
||||
golangToolchainEvidence(reader),
|
||||
cToolchainEvidence(comments, symbols),
|
||||
rustToolchainEvidence(comments),
|
||||
linkerToolchainEvidence(f, comments),
|
||||
)
|
||||
}
|
||||
|
||||
func includeNoneNil(evidence ...*file.Toolchain) []file.Toolchain {
|
||||
var toolchains []file.Toolchain
|
||||
for _, e := range evidence {
|
||||
if e != nil {
|
||||
toolchains = append(toolchains, *e)
|
||||
}
|
||||
}
|
||||
return toolchains
|
||||
}
|
||||
|
||||
func findELFSecurityFeatures(f *elf.File) *file.ELFSecurityFeatures {
|
||||
return &file.ELFSecurityFeatures{
|
||||
SymbolTableStripped: isElfSymbolTableStripped(f),
|
||||
@@ -105,28 +82,6 @@ func boolRef(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// elfSymbolSet collects the names from the static and dynamic symbol tables into a single set, so
|
||||
// detectors can test for runtime markers without re-reading and re-iterating the symbol tables each
|
||||
// time. It covers both tables since some evidence (e.g. the Fortran MAIN__ entry) lives only in the
|
||||
// static symbol table.
|
||||
func elfSymbolSet(f *elf.File) *strset.Set {
|
||||
set := strset.New()
|
||||
|
||||
if syms, err := f.Symbols(); err == nil {
|
||||
for _, sym := range syms {
|
||||
set.Add(sym.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if dynSyms, err := f.DynamicSymbols(); err == nil {
|
||||
for _, sym := range dynSyms {
|
||||
set.Add(sym.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func checkElfNXProtection(file *elf.File) bool {
|
||||
// find the program headers until you find the GNU_STACK segment
|
||||
for _, prog := range file.Progs {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -315,73 +314,3 @@ func elfWithDynamicSection(t *testing.T, class elf.Class, dynamic []byte) []byte
|
||||
buf.Write(dynamic)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func Test_elfGoToolchainDetection(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("testdata/golang", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
wantPresent bool
|
||||
}{
|
||||
{
|
||||
name: "go binary has toolchain",
|
||||
fixture: "bin/hello_linux",
|
||||
wantPresent: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := readerForFixture(t, tt.fixture)
|
||||
f, err := elf.NewFile(reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
toolchains := elfToolchains(reader, f)
|
||||
assert.Equal(t, tt.wantPresent, hasGoToolchain(toolchains))
|
||||
|
||||
if tt.wantPresent {
|
||||
require.NotEmpty(t, toolchains)
|
||||
assert.Equal(t, "go", toolchains[0].Name)
|
||||
assert.NotEmpty(t, toolchains[0].Version)
|
||||
assert.Equal(t, file.ToolchainComponentCompiler, toolchains[0].Component)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_elfCgoToolchainDetection(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("testdata/golang", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
t.Run("cgo binary has both go and c toolchains", func(t *testing.T) {
|
||||
reader := readerForFixture(t, "bin/hello_linux_cgo")
|
||||
f, err := elf.NewFile(reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
toolchains := elfToolchains(reader, f)
|
||||
|
||||
// versions are dynamic based on Docker image, so we ignore them in comparison
|
||||
want := []file.Toolchain{
|
||||
{Name: "go", Component: file.ToolchainComponentCompiler},
|
||||
{Name: "gcc", Component: file.ToolchainComponentCompiler},
|
||||
}
|
||||
|
||||
if d := cmp.Diff(want, toolchains, cmpopts.IgnoreFields(file.Toolchain{}, "Version")); d != "" {
|
||||
t.Errorf("elfToolchains() mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
|
||||
// verify versions are populated
|
||||
for _, tc := range toolchains {
|
||||
assert.NotEmpty(t, tc.Version, "expected version to be set for %s toolchain", tc.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,8 +48,6 @@ func findMachoFeatures(data *file.Executable, reader unionreader.UnionReader) er
|
||||
if !data.HasExports {
|
||||
data.HasExports = machoHasExports(f)
|
||||
}
|
||||
|
||||
data.Toolchains = machoToolchains(reader)
|
||||
}
|
||||
|
||||
// de-duplicate libraries
|
||||
@@ -58,12 +56,6 @@ func findMachoFeatures(data *file.Executable, reader unionreader.UnionReader) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func machoToolchains(reader unionreader.UnionReader) []file.Toolchain {
|
||||
return includeNoneNil(
|
||||
golangToolchainEvidence(reader),
|
||||
)
|
||||
}
|
||||
|
||||
func machoHasEntrypoint(f *macho.File) bool {
|
||||
// derived from struct entry_point_command found from which explicitly calls out LC_MAIN:
|
||||
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
|
||||
|
||||
@@ -120,39 +120,3 @@ func Test_machoUniversal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_machoGoToolchainDetection(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("testdata/golang", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
wantPresent bool
|
||||
}{
|
||||
{
|
||||
name: "go binary has toolchain",
|
||||
fixture: "bin/hello_mac",
|
||||
wantPresent: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := readerForFixture(t, tt.fixture)
|
||||
|
||||
toolchains := machoToolchains(reader)
|
||||
assert.Equal(t, tt.wantPresent, hasGoToolchain(toolchains))
|
||||
|
||||
if tt.wantPresent {
|
||||
require.NotEmpty(t, toolchains)
|
||||
assert.Equal(t, "go", toolchains[0].Name)
|
||||
assert.NotEmpty(t, toolchains[0].Version)
|
||||
assert.Equal(t, file.ToolchainComponentCompiler, toolchains[0].Component)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ func findPEFeatures(data *file.Executable, reader unionreader.UnionReader) error
|
||||
data.ImportedLibraries = libs
|
||||
data.HasEntrypoint = peHasEntrypoint(f)
|
||||
data.HasExports = peHasExports(f)
|
||||
data.Toolchains = peToolchains(reader)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -83,9 +82,3 @@ func peHasExports(f *pe.File) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func peToolchains(reader unionreader.UnionReader) []file.Toolchain {
|
||||
return includeNoneNil(
|
||||
golangToolchainEvidence(reader),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
@@ -79,39 +78,3 @@ func Test_peHasExports(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_peGoToolchainDetection(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("testdata/golang", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
wantPresent bool
|
||||
}{
|
||||
{
|
||||
name: "go binary has toolchain",
|
||||
fixture: "bin/hello.exe",
|
||||
wantPresent: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := readerForFixture(t, tt.fixture)
|
||||
|
||||
toolchains := peToolchains(reader)
|
||||
assert.Equal(t, tt.wantPresent, hasGoToolchain(toolchains))
|
||||
|
||||
if tt.wantPresent {
|
||||
require.NotEmpty(t, toolchains)
|
||||
assert.Equal(t, "go", toolchains[0].Name)
|
||||
assert.NotEmpty(t, toolchains[0].Version)
|
||||
assert.Equal(t, file.ToolchainComponentCompiler, toolchains[0].Component)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
bin
|
||||
actual_verify
|
||||
Dockerfile.sha256
|
||||
*.fingerprint
|
||||
Dockerfile.sha256
|
||||
@@ -1,18 +0,0 @@
|
||||
FROM golang:1.24
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY main.go cgo_main.go ./
|
||||
|
||||
# pure Go builds (no CGO)
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /hello_linux .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o /hello_mac .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o /hello.exe .
|
||||
|
||||
# CGO-enabled build (Linux only, uses gcc)
|
||||
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o /hello_linux_cgo ./cgo_main.go
|
||||
@@ -1,42 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-golang-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || \
|
||||
(docker build --platform=linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run -i -v $(shell pwd)/$(BIN):/out $(TOOL_IMAGE) sh -c \
|
||||
"cp /hello_linux /hello_mac /hello.exe /hello_linux_cgo /out/"
|
||||
|
||||
debug:
|
||||
docker run -it --rm -v $(shell pwd):/mount -w /mount $(TOOL_IMAGE) sh
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find . -maxdepth 1 -type f \( -name "*.go" -o -name "go.*" -o -name "Dockerfile" -o -name "Makefile" \) \
|
||||
-exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean fixtures fingerprint
|
||||
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int get_length(const char* s) {
|
||||
return strlen(s);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
msg := C.CString("Hello from CGO!")
|
||||
length := C.get_length(msg)
|
||||
fmt.Printf("String length: %d\n", int(length))
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
module x
|
||||
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
golang.org/x/text v0.21.0
|
||||
)
|
||||
@@ -1,4 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
@@ -1,28 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// use stdlib packages
|
||||
fmt.Println("Hello from Go!")
|
||||
fmt.Println(strings.ToUpper("test"))
|
||||
|
||||
// use golang.org/x package
|
||||
tag := language.English
|
||||
fmt.Println(tag.String())
|
||||
|
||||
// use third-party package
|
||||
spew.Dump(os.Args)
|
||||
|
||||
// use encoding/json
|
||||
data, _ := json.Marshal(map[string]string{"hello": "world"})
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
bin/
|
||||
Dockerfile.sha256
|
||||
*.fingerprint
|
||||
@@ -1,39 +0,0 @@
|
||||
# invoke all make files in subdirectories
|
||||
.PHONY: all gcc clang lld mold gold rust fortran gdc gccgo gnat
|
||||
|
||||
all: gcc clang lld mold gold rust fortran gdc gccgo gnat
|
||||
|
||||
gcc:
|
||||
$(MAKE) -C gcc
|
||||
|
||||
clang:
|
||||
$(MAKE) -C clang
|
||||
|
||||
lld:
|
||||
$(MAKE) -C lld
|
||||
|
||||
mold:
|
||||
$(MAKE) -C mold
|
||||
|
||||
gold:
|
||||
$(MAKE) -C gold
|
||||
|
||||
rust:
|
||||
$(MAKE) -C rust
|
||||
|
||||
fortran:
|
||||
$(MAKE) -C fortran
|
||||
|
||||
gdc:
|
||||
$(MAKE) -C gdc
|
||||
|
||||
gccgo:
|
||||
$(MAKE) -C gccgo
|
||||
|
||||
gnat:
|
||||
$(MAKE) -C gnat
|
||||
|
||||
%:
|
||||
@for dir in gcc clang lld mold gold rust fortran gdc gccgo gnat; do \
|
||||
$(MAKE) -C $$dir $@; \
|
||||
done
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM alpine:3.21.7@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
|
||||
|
||||
RUN apk add --no-cache clang18 musl-dev make
|
||||
|
||||
# create symlink so 'clang' command works (Alpine installs as clang-18)
|
||||
RUN ln -s /usr/bin/clang-18 /usr/bin/clang
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-clang-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_clang
|
||||
|
||||
$(BIN)/hello_clang: hello.c
|
||||
clang hello.c -o $(BIN)/hello_clang
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_clang
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
FROM gcc:13.4.0@sha256:d49302cbe2faf1a2dece312e258325e0d39e32874ea4dec122b4ac86f45d5b50
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-fortran-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_fortran
|
||||
|
||||
$(BIN)/hello_fortran: hello.f90
|
||||
gfortran hello.f90 -o $(BIN)/hello_fortran
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_fortran
|
||||
@@ -1,3 +0,0 @@
|
||||
program hello
|
||||
print *, "Hello, World!"
|
||||
end program hello
|
||||
@@ -1 +0,0 @@
|
||||
FROM gcc:13.4.0@sha256:d49302cbe2faf1a2dece312e258325e0d39e32874ea4dec122b4ac86f45d5b50
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-gcc-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_gcc
|
||||
|
||||
$(BIN)/hello_gcc: hello.c
|
||||
gcc hello.c -o $(BIN)/hello_gcc
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_gcc
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM debian:12.14@sha256:49ba348354a28e39c70beffd6cf43bdb8d55d81ce4b746b0428717d054b8bbc4
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gccgo make && rm -rf /var/lib/apt/lists/*
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-gccgo-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_gccgo
|
||||
|
||||
$(BIN)/hello_gccgo: hello.go
|
||||
gccgo hello.go -o $(BIN)/hello_gccgo
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_gccgo
|
||||
@@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("Hello, World!")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM debian:12.14@sha256:49ba348354a28e39c70beffd6cf43bdb8d55d81ce4b746b0428717d054b8bbc4
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gdc make && rm -rf /var/lib/apt/lists/*
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-gdc-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_gdc
|
||||
|
||||
$(BIN)/hello_gdc: hello.d
|
||||
gdc hello.d -o $(BIN)/hello_gdc
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_gdc
|
||||
@@ -1,5 +0,0 @@
|
||||
import std.stdio;
|
||||
|
||||
void main() {
|
||||
writeln("Hello, World!");
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM debian:12.14@sha256:49ba348354a28e39c70beffd6cf43bdb8d55d81ce4b746b0428717d054b8bbc4
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gnat make && rm -rf /var/lib/apt/lists/*
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-gnat-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_gnat
|
||||
|
||||
$(BIN)/hello_gnat: hello.adb
|
||||
gnatmake hello.adb -o $(BIN)/hello_gnat
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_gnat *.o *.ali
|
||||
@@ -1,6 +0,0 @@
|
||||
with Ada.Text_IO;
|
||||
|
||||
procedure Hello is
|
||||
begin
|
||||
Ada.Text_IO.Put_Line ("Hello, World!");
|
||||
end Hello;
|
||||
@@ -1,51 +0,0 @@
|
||||
V "GNAT Lib v12"
|
||||
M P W=b
|
||||
P ZX
|
||||
|
||||
RN
|
||||
RV NO_IO
|
||||
|
||||
U hello%b hello.adb b6dbb388 NE OO SU
|
||||
W ada%s ada.ads ada.ali
|
||||
Z ada.strings.text_buffers%s a-sttebu.adb a-sttebu.ali
|
||||
W ada.text_io%s a-textio.adb a-textio.ali
|
||||
|
||||
D ada.ads 20220819080952 76789da1 ada%s
|
||||
D a-except.ads 20220819080952 2a7d8d70 ada.exceptions%s
|
||||
D a-ioexce.ads 20220819080952 40018c65 ada.io_exceptions%s
|
||||
D a-stream.ads 20220819080952 119b8fb3 ada.streams%s
|
||||
D a-string.ads 20220819080952 90ac6797 ada.strings%s
|
||||
D a-sttebu.ads 20220819080952 18209c64 ada.strings.text_buffers%s
|
||||
D a-stuten.ads 20220819080952 33c54060 ada.strings.utf_encoding%s
|
||||
D a-tags.ads 20220819080952 9eaa38c6 ada.tags%s
|
||||
D a-textio.ads 20220819080952 deada98a ada.text_io%s
|
||||
D a-unccon.ads 20220819080952 0e9b276f ada.unchecked_conversion%s
|
||||
D hello.adb 20260618132941 68761a02 hello%b
|
||||
D interfac.ads 20220819080952 edec285f interfaces%s
|
||||
D i-cstrea.ads 20220819080952 e53d8b8e interfaces.c_streams%s
|
||||
D system.ads 20220819080952 a2da961d system%s
|
||||
D s-crtl.ads 20220819080952 8a0692f8 system.crtl%s
|
||||
D s-exctab.ads 20220819080952 91bef6ef system.exception_table%s
|
||||
D s-ficobl.ads 20220819080952 dc5161d4 system.file_control_block%s
|
||||
D s-parame.ads 20220819080952 d494a4a6 system.parameters%s
|
||||
D s-putima.ads 20220819080952 4e095881 system.put_images%s
|
||||
D s-secsta.ads 20220819080952 62cd9202 system.secondary_stack%s
|
||||
D s-soflin.ads 20220819080952 df77073e system.soft_links%s
|
||||
D s-stache.ads 20220819080952 0b81c1fe system.stack_checking%s
|
||||
D s-stalib.ads 20220819080952 1c9580f6 system.standard_library%s
|
||||
D s-stoele.ads 20220819080952 6d8e1ff4 system.storage_elements%s
|
||||
D s-stoele.adb 20220819080952 8f5a9db6 system.storage_elements%b
|
||||
D s-traent.ads 20220819080952 c81cbf8c system.traceback_entries%s
|
||||
D s-unstyp.ads 20220819080952 fa2a7f59 system.unsigned_types%s
|
||||
D s-wchcon.ads 20220819080952 d9032363 system.wch_con%s
|
||||
D unchconv.ads 20220819080952 ca2f9e18 unchecked_conversion%s
|
||||
G a e
|
||||
G c Z b b [hello standard 3 11 none]
|
||||
X 1 ada.ads
|
||||
16K9*Ada 20e8 11|1r6 5r4
|
||||
X 9 a-textio.ads
|
||||
58K13*Text_IO 727e16 11|1w10 5r8
|
||||
507U14*Put_Line 11|5s16
|
||||
X 11 hello.adb
|
||||
3U11*Hello 3b11 6l5 6t10
|
||||
|
||||
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
FROM alpine:3.21.7@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
|
||||
|
||||
RUN apk add --no-cache gcc binutils-gold musl-dev make
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-gold-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_gold
|
||||
|
||||
$(BIN)/hello_gold: hello.c
|
||||
gcc hello.c -fuse-ld=gold -o $(BIN)/hello_gold
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_gold
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM alpine:3.21.7@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
|
||||
|
||||
RUN apk add --no-cache clang18 lld musl-dev make
|
||||
|
||||
# create symlink so 'clang' command works (Alpine installs as clang-18)
|
||||
RUN ln -s /usr/bin/clang-18 /usr/bin/clang
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-lld-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_lld
|
||||
|
||||
$(BIN)/hello_lld: hello.c
|
||||
clang hello.c -fuse-ld=lld -o $(BIN)/hello_lld
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_lld
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM alpine:3.21.7@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
|
||||
|
||||
RUN apk add --no-cache gcc mold musl-dev make
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-mold-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_mold
|
||||
|
||||
$(BIN)/hello_mold: hello.c
|
||||
gcc hello.c -fuse-ld=mold -o $(BIN)/hello_mold
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_mold
|
||||
@@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
FROM rust:1.83.0@sha256:a45bf1f5d9af0a23b26703b3500d70af1abff7f984a7abef5a104b42c02a292b
|
||||
@@ -1,39 +0,0 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-toolchain-rust-build-tools:latest
|
||||
FINGERPRINT_FILE=$(BIN).fingerprint
|
||||
|
||||
ifndef BIN
|
||||
$(error BIN is not set)
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
# requirement 1: 'fixtures' goal to generate any and all test fixtures
|
||||
fixtures: build
|
||||
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
# requirement 3: we always need to recalculate the fingerprint based on source regardless of any existing fingerprint
|
||||
.PHONY: $(FINGERPRINT_FILE)
|
||||
$(FINGERPRINT_FILE):
|
||||
@find project Dockerfile Makefile -type f -exec sha256sum {} \; | sort -k2 > $(FINGERPRINT_FILE)
|
||||
|
||||
# requirement 4: 'clean' goal to remove all generated test fixtures
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
@@ -1,9 +0,0 @@
|
||||
BIN=../bin
|
||||
|
||||
all: $(BIN)/hello_rust
|
||||
|
||||
$(BIN)/hello_rust: hello.rs
|
||||
rustc hello.rs -o $(BIN)/hello_rust
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_rust
|
||||
@@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
println!("Hello, World!");
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/buildinfo"
|
||||
"debug/elf"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/elfutil"
|
||||
)
|
||||
|
||||
// TODO: additional toolchain detectors we'd like to add. Each entry notes the signal location and the
|
||||
// component it yields.
|
||||
//
|
||||
// ELF (.comment / notes / symbols — same mechanism as the detectors below):
|
||||
// 1. rustc commit hash: scan rodata for "/rustc/<40-hex>/library/" panic paths; resolves rust provenance
|
||||
// for stripped or pre-1.73 binaries (where the .comment producer string is absent). needs a
|
||||
// commit->version lookup table to turn the hash into a semver. component: compiler.
|
||||
// 2. more clang forks: AMD AOCC and Arm Compiler. their producer strings are vendor-prefixed clang
|
||||
// versions (e.g. "AMD clang version 14.0.6 ...") that clangVersionPattern already matches, so today
|
||||
// they are mislabeled as plain "clang"; each needs a vendor pattern matched before clangVersionPattern
|
||||
// (the same ordering used for icx). not yet added because neither ships a freely buildable linux/amd64
|
||||
// image to anchor a real fixture (AOCC is proprietary; Arm Compiler is AArch64-only). component: compiler.
|
||||
// 3. Swift (Linux): presence of "swift5_*" sections marks Swift; version only when a "swiftlang-"
|
||||
// producer string is present (debug builds). component: compiler.
|
||||
// 4. Haskell GHC: RTS symbols (hs_init, stg_*) for identity; derive an approximate version from the
|
||||
// embedded "base_<x.y.z>" package symbol. component: compiler.
|
||||
// 5. identity-only (no reliable version) via runtime symbol prefixes: OCaml (caml_*), Nim (NimMain),
|
||||
// Crystal (__crystal_main), FreePascal (FPC_*), and the LLVM-based D compilers ldc/dmd (_Dmain). the
|
||||
// GCC D frontend (gdc) is already handled via the shared GCC .comment path. component: compiler.
|
||||
// 6. GNU assembler (GNU AS): only present in DWARF .debug_info producer strings, NOT .comment, so it
|
||||
// requires DWARF parsing and is absent in stripped builds. component: assembler.
|
||||
// 7. GNU ld (BFD): leaves no self-identifying marker; can only be inferred by elimination (ELF with a
|
||||
// compiler .comment but no lld/mold/gold evidence) at low confidence and with no version.
|
||||
//
|
||||
// not feasible via this mechanism (recorded so they aren't re-investigated):
|
||||
// - TinyCC (tcc): emits no .comment section, no producer string, and no identifying symbols (stripped
|
||||
// by default), so a tcc-built ELF carries no usable signal here.
|
||||
// - Intel classic icc/ifort, IBM XL, NVIDIA/PGI: distinct producer strings exist but the toolchains are
|
||||
// proprietary/deprecated and not freely dockerizable on linux/amd64 to anchor a real fixture.
|
||||
//
|
||||
// PE (Windows — needs debug/pe, not covered by this ELF-only file):
|
||||
// 8. MSVC link.exe + cl.exe: parse the Rich Header (the "DanS".."Rich" block, XOR-decoded into
|
||||
// ProdID/build pairs); map build numbers to Visual Studio/linker versions. components: linker + compiler.
|
||||
// 9. .NET / CLR: COR20 header (optional-header DataDirectory[14]) marks a managed assembly; read the
|
||||
// "TargetFrameworkAttribute" string (e.g. ".NETCoreApp,Version=v8.0") for the runtime version.
|
||||
// component: runtime. (NativeAOT .NET is a native binary with runtime symbols only and no clean version.)
|
||||
//
|
||||
// Mach-O (macOS — needs debug/macho):
|
||||
// 10. Swift: presence of "__swift5_*" sections; version via the "swiftlang-" producer when present.
|
||||
// component: compiler.
|
||||
|
||||
var (
|
||||
clangVersionPattern = regexp.MustCompile(`clang version (\d+\.\d+\.\d+)`)
|
||||
gccVersionPattern = regexp.MustCompile(`GCC: \([^)]+\) (\d+\.\d+\.\d+)`)
|
||||
// the Intel oneAPI DPC++/C++ compiler (icx/icpx) is an LLVM/clang fork that writes its own producer
|
||||
// string into .comment, e.g. "Intel(R) oneAPI DPC++/C++ Compiler 2024.0.2 (2024.0.2.20231223)" (see
|
||||
// https://briancallahan.net/blog/20240306.html for a real `readelf .comment` dump). since it is a clang
|
||||
// fork it may also emit a plain "clang version" entry, so it is matched before clangVersionPattern to
|
||||
// avoid mislabeling as upstream clang. evidence for the producer string and version scheme:
|
||||
// - https://briancallahan.net/blog/20240306.html
|
||||
// - https://www.intel.com/content/www/us/en/developer/articles/release-notes/oneapi-dpcpp-cpp-compiler-release-notes.html
|
||||
intelVersionPattern = regexp.MustCompile(`Intel\(R\) oneAPI[^0-9]*(\d+\.\d+\.\d+)`)
|
||||
// rustc embeds its own producer string in .comment for binaries built with rust >= 1.73 (e.g.
|
||||
// "rustc version 1.83.0 (90b35a623 2024-11-26)"). The shipped libstd objects instead carry the
|
||||
// "clang LLVM (rustc version ...)" form, so accept both.
|
||||
rustcVersionPattern = regexp.MustCompile(`rustc version (\d+\.\d+\.\d+)`)
|
||||
// LLVM lld writes "Linker: LLD <version>" into .comment (see https://lld.llvm.org/). distros often
|
||||
// inject a vendor prefix (e.g. Debian/Ubuntu emit "Linker: Ubuntu LLD 18.1.3"), so allow one.
|
||||
lldVersionPattern = regexp.MustCompile(`Linker: (?:.*? )?LLD (\d+\.\d+\.\d+)`)
|
||||
// mold writes "mold <version> (...; compatible with GNU ld)" into .comment.
|
||||
moldVersionPattern = regexp.MustCompile(`mold (\d+\.\d+\.\d+)`)
|
||||
// GNU gold writes a "gold <version>" descriptor into the .note.gnu.gold-version note section.
|
||||
goldVersionPattern = regexp.MustCompile(`gold (\d+\.\d+)`)
|
||||
)
|
||||
|
||||
// golangToolchainEvidence attempts to extract Go toolchain information from the binary build info.
|
||||
func golangToolchainEvidence(reader io.ReaderAt) *file.Toolchain {
|
||||
// buildinfo parses ELF with debug/elf directly, so bound the eager section-name table read first
|
||||
// (non-ELF readers pass through untouched).
|
||||
if err := elfutil.CheckSectionNameTable(reader); err != nil {
|
||||
return nil
|
||||
}
|
||||
bi, err := buildinfo.Read(reader) //nolint:gocritic // gated by elfutil.CheckSectionNameTable above
|
||||
if err != nil || bi == nil {
|
||||
// not a golang binary
|
||||
return nil
|
||||
}
|
||||
return &file.Toolchain{
|
||||
Name: "go",
|
||||
Version: bi.GoVersion,
|
||||
Component: file.ToolchainComponentCompiler,
|
||||
}
|
||||
}
|
||||
|
||||
// cToolchainEvidence attempts to extract compiler information from the ELF .comment section for the
|
||||
// GCC and Clang families. This covers upstream GCC/Clang, GCC frontends that share the GCC producer
|
||||
// string but pull in distinct runtime symbols (gfortran, gdc, gccgo, gnat), and the Intel oneAPI
|
||||
// (icx/icpx) clang fork.
|
||||
func cToolchainEvidence(comments []string, symbols *strset.Set) *file.Toolchain {
|
||||
for _, comment := range comments {
|
||||
// Intel oneAPI (icx) is a clang fork whose binaries carry both an "Intel(R) oneAPI ..." producer
|
||||
// string and a plain "clang version" entry, so it must be checked before the generic clang match.
|
||||
if match := intelVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "icx",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentCompiler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, comment := range comments {
|
||||
// check for clang first since clang binaries often have both GCC and clang entries
|
||||
// (clang includes GCC compatibility info)
|
||||
if match := clangVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "clang",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentCompiler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, comment := range comments {
|
||||
if match := gccVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: gccFrontendName(symbols),
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentCompiler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// gccFrontendName disambiguates which GCC frontend produced a binary. gfortran, gdc, gccgo, and gnat
|
||||
// all share the same "GCC: (...) <version>" string in .comment, so the only way to tell them apart from
|
||||
// a plain C/C++ build is by the language-specific runtime symbols they pull in. defaults to "gcc".
|
||||
func gccFrontendName(symbols *strset.Set) string {
|
||||
switch {
|
||||
case hasFortranEvidence(symbols):
|
||||
return "gfortran"
|
||||
case hasDEvidence(symbols):
|
||||
return "gdc"
|
||||
case hasGccgoEvidence(symbols):
|
||||
return "gccgo"
|
||||
case hasAdaEvidence(symbols):
|
||||
return "gnat"
|
||||
default:
|
||||
return "gcc"
|
||||
}
|
||||
}
|
||||
|
||||
// rustToolchainEvidence attempts to extract Rust compiler information from the ELF .comment section.
|
||||
func rustToolchainEvidence(comments []string) *file.Toolchain {
|
||||
for _, comment := range comments {
|
||||
if match := rustcVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "rust",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentCompiler,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// linkerToolchainEvidence attempts to extract linker information from the binary. lld and mold leave a
|
||||
// version string in the ELF .comment section, while gold writes a dedicated .note.gnu.gold-version note.
|
||||
// GNU ld (BFD) leaves no self-identifying marker, so it cannot be detected here.
|
||||
func linkerToolchainEvidence(f *elf.File, comments []string) *file.Toolchain {
|
||||
for _, comment := range comments {
|
||||
if match := lldVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "lld",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentLinker,
|
||||
}
|
||||
}
|
||||
if match := moldVersionPattern.FindStringSubmatch(comment); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "mold",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentLinker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if section := f.Section(".note.gnu.gold-version"); section != nil {
|
||||
data, err := section.Data()
|
||||
if err == nil {
|
||||
if match := goldVersionPattern.FindStringSubmatch(string(data)); match != nil {
|
||||
return &file.Toolchain{
|
||||
Name: "gold",
|
||||
Version: match[1],
|
||||
Component: file.ToolchainComponentLinker,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// elfComments returns the null-delimited strings within the ELF .comment section, which may contain
|
||||
// multiple producer entries (compiler, assembler, linker, etc.).
|
||||
func elfComments(f *elf.File) []string {
|
||||
section := f.Section(".comment")
|
||||
if section == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// the .comment section contains null-terminated strings
|
||||
return strings.Split(string(data), "\x00")
|
||||
}
|
||||
|
||||
// hasFortranEvidence reports whether the binary references the libgfortran runtime, which indicates a
|
||||
// gfortran build (MAIN__ is the real Fortran program entry, and _gfortran_* are runtime calls).
|
||||
func hasFortranEvidence(symbols *strset.Set) bool {
|
||||
return symbols.HasAny("MAIN__", "_gfortran_set_args", "_gfortran_set_options", "_gfortran_st_write")
|
||||
}
|
||||
|
||||
// hasDEvidence reports whether the binary references the gdc (GNU D) runtime. _Dmain is the D program
|
||||
// entry the compiler emits, and __gdc_personality_v0 is gdc's exception-handling personality routine.
|
||||
// see the gdc frontend in gcc: https://github.com/gcc-mirror/gcc/tree/master/gcc/d
|
||||
func hasDEvidence(symbols *strset.Set) bool {
|
||||
return symbols.HasAny("_Dmain", "__gdc_personality_v0")
|
||||
}
|
||||
|
||||
// hasGccgoEvidence reports whether the binary was built by gccgo. the libgo runtime exports __go_*
|
||||
// helpers (e.g. __go_go to start a goroutine) that the gc toolchain does not emit, so these
|
||||
// distinguish gccgo both from a plain gcc C build and from a cgo-enabled gc binary (which also carries
|
||||
// a gcc .comment). see libgo: https://github.com/gcc-mirror/gcc/tree/master/libgo/runtime
|
||||
func hasGccgoEvidence(symbols *strset.Set) bool {
|
||||
return symbols.HasAny("__go_go", "__go_init_main", "__go_register_gc_roots")
|
||||
}
|
||||
|
||||
// hasAdaEvidence reports whether the binary references the GNAT Ada runtime. adainit/adafinal are the
|
||||
// elaboration entry points and __gnat_* are runtime support calls.
|
||||
// see the GNAT runtime (libgnat): https://github.com/gcc-mirror/gcc/tree/master/gcc/ada
|
||||
func hasAdaEvidence(symbols *strset.Set) bool {
|
||||
return symbols.HasAny("__gnat_initialize", "__gnat_runtime_initialize", "adainit", "adafinal")
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
// hasGoToolchain is a test helper to check whether the go toolchain was detected.
|
||||
func hasGoToolchain(toolchains []file.Toolchain) bool {
|
||||
for _, tc := range toolchains {
|
||||
if tc.Name == "go" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Test_elfToolchains(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("testdata/toolchains", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
compiler := file.ToolchainComponentCompiler
|
||||
linker := file.ToolchainComponentLinker
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want []file.Toolchain
|
||||
}{
|
||||
{
|
||||
name: "gcc: compiler only",
|
||||
fixture: "gcc/bin/hello_gcc",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gcc", Version: "13.4.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "clang: compiler only",
|
||||
fixture: "clang/bin/hello_clang",
|
||||
want: []file.Toolchain{
|
||||
{Name: "clang", Version: "18.1.8", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "lld: clang compiler + lld linker",
|
||||
fixture: "lld/bin/hello_lld",
|
||||
want: []file.Toolchain{
|
||||
{Name: "clang", Version: "18.1.8", Component: compiler},
|
||||
{Name: "lld", Version: "19.1.4", Component: linker},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mold: gcc compiler + mold linker",
|
||||
fixture: "mold/bin/hello_mold",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gcc", Version: "14.2.0", Component: compiler},
|
||||
{Name: "mold", Version: "2.34.1", Component: linker},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "gold: gcc compiler + gold linker",
|
||||
fixture: "gold/bin/hello_gold",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gcc", Version: "14.2.0", Component: compiler},
|
||||
{Name: "gold", Version: "1.16", Component: linker},
|
||||
},
|
||||
},
|
||||
{
|
||||
// rust binaries also carry a GCC producer string from the gcc-compiled C runtime glue that is
|
||||
// linked in, so both compilers are reported (akin to how cgo binaries report go and gcc).
|
||||
name: "rust: gcc glue + rustc",
|
||||
fixture: "rust/bin/hello_rust",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gcc", Version: "12.2.0", Component: compiler},
|
||||
{Name: "rust", Version: "1.83.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
// gfortran shares the GCC version string in .comment, so it is only distinguishable from a
|
||||
// C build by the presence of libgfortran runtime symbols (gcc gets relabeled to gfortran).
|
||||
name: "fortran: gfortran compiler only",
|
||||
fixture: "fortran/bin/hello_fortran",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gfortran", Version: "13.4.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
// gdc is a GCC frontend that shares the GCC version string, so it is distinguished from a C
|
||||
// build only by the D runtime symbols (_Dmain, __gdc_personality_v0).
|
||||
name: "gdc: GNU D compiler only",
|
||||
fixture: "gdc/bin/hello_gdc",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gdc", Version: "12.2.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
// gccgo is a GCC frontend (not the gc toolchain, so it has no Go build info); it shares the
|
||||
// GCC version string and is distinguished only by the libgo __go_* runtime symbols.
|
||||
name: "gccgo: GNU Go compiler only",
|
||||
fixture: "gccgo/bin/hello_gccgo",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gccgo", Version: "12.2.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
{
|
||||
// gnat is the GCC Ada frontend; it shares the GCC version string and is distinguished only by
|
||||
// the GNAT runtime symbols (adainit, __gnat_*).
|
||||
name: "gnat: GNU Ada compiler only",
|
||||
fixture: "gnat/bin/hello_gnat",
|
||||
want: []file.Toolchain{
|
||||
{Name: "gnat", Version: "12.2.0", Component: compiler},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reader := readerForFixture(t, tt.fixture)
|
||||
f, err := elf.NewFile(reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
got := elfToolchains(reader, f)
|
||||
|
||||
if d := cmp.Diff(tt.want, got); d != "" {
|
||||
t.Errorf("elfToolchains() mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cToolchainEvidence(t *testing.T) {
|
||||
compiler := file.ToolchainComponentCompiler
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
comments []string
|
||||
symbols []string
|
||||
want *file.Toolchain
|
||||
}{
|
||||
{
|
||||
// icx (Intel oneAPI DPC++/C++) is an LLVM/clang fork. there is no freely buildable linux/amd64
|
||||
// image to anchor a real fixture, so the literal .comment producer string is asserted here. the
|
||||
// exact form is confirmed by a real `readelf -x .comment` dump (which yielded
|
||||
// "Intel(R) oneAPI DPC++/C++ Compiler 2024.0.2 (2024.0.2.20231223)"):
|
||||
// - https://briancallahan.net/blog/20240306.html
|
||||
// - https://www.intel.com/content/www/us/en/developer/articles/release-notes/oneapi-dpcpp/2025.html
|
||||
// because it is a clang fork, the Intel string must be matched before the generic clang pattern;
|
||||
// the second "clang version" entry below guards that ordering (a generic clang match must not win).
|
||||
name: "intel oneAPI icx is matched before clang",
|
||||
comments: []string{
|
||||
"Intel(R) oneAPI DPC++/C++ Compiler 2024.0.2 (2024.0.2.20231223)",
|
||||
"clang version 18.1.0",
|
||||
},
|
||||
want: &file.Toolchain{Name: "icx", Version: "2024.0.2", Component: compiler},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := cToolchainEvidence(tt.comments, strset.New(tt.symbols...))
|
||||
if d := cmp.Diff(tt.want, got); d != "" {
|
||||
t.Errorf("cToolchainEvidence() mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_linkerToolchainEvidence(t *testing.T) {
|
||||
linker := file.ToolchainComponentLinker
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
comments []string
|
||||
want *file.Toolchain
|
||||
}{
|
||||
{
|
||||
// lld writes "Linker: " + getLLDVersion() into .comment. docs and source:
|
||||
// - https://lld.llvm.org/ ("If the string \"Linker: LLD\" is included ... you are using LLD")
|
||||
// - https://github.com/llvm/llvm-project/blob/main/lld/ELF/SyntheticSections.cpp (Twine("Linker: ") + getLLDVersion())
|
||||
name: "vanilla lld",
|
||||
comments: []string{"Linker: LLD 19.1.4"},
|
||||
want: &file.Toolchain{Name: "lld", Version: "19.1.4", Component: linker},
|
||||
},
|
||||
{
|
||||
// debian/ubuntu prepend a vendor to getLLDVersion() (the same packaging mechanism behind clang's
|
||||
// "Ubuntu clang version ..."), so .comment reads "Linker: Ubuntu LLD <version>". reproduced on
|
||||
// ubuntu:24.04: `readelf -p .comment <bin>` => "Linker: Ubuntu LLD 18.1.3".
|
||||
name: "vendor-prefixed lld",
|
||||
comments: []string{"Linker: Ubuntu LLD 18.1.3"},
|
||||
want: &file.Toolchain{Name: "lld", Version: "18.1.3", Component: linker},
|
||||
},
|
||||
{
|
||||
// mold writes "mold <version> (<commit>; compatible with GNU ld)" into .comment. this format is
|
||||
// documented by mold and is also exercised end-to-end by the real fixture in Test_elfToolchains:
|
||||
// - https://github.com/rui314/mold (README: "mold leaves its identification string in .comment")
|
||||
name: "mold",
|
||||
comments: []string{"mold 2.34.1 (compatible with GNU ld)"},
|
||||
want: &file.Toolchain{Name: "mold", Version: "2.34.1", Component: linker},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// f is only consulted for the gold note, which these comment-based cases match before reaching.
|
||||
got := linkerToolchainEvidence(nil, tt.comments)
|
||||
if d := cmp.Diff(tt.want, got); d != "" {
|
||||
t.Errorf("linkerToolchainEvidence() mismatch (-want +got):\n%s", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,9 @@ type (
|
||||
|
||||
// RelocationReadOnly indicates the RELRO security protection level applied to an ELF binary.
|
||||
RelocationReadOnly string
|
||||
|
||||
// ToolchainComponent represents which part of the toolchain an entry describes (e.g. compiler, linker).
|
||||
// This can be expanded in the future to include assemblers, runtimes, and other toolchain components.
|
||||
ToolchainComponent string
|
||||
)
|
||||
|
||||
const (
|
||||
ToolchainComponentCompiler ToolchainComponent = "compiler"
|
||||
ToolchainComponentLinker ToolchainComponent = "linker"
|
||||
|
||||
ELF ExecutableFormat = "elf" // Executable and Linkable Format used on Unix-like systems
|
||||
MachO ExecutableFormat = "macho" // Mach object file format used on macOS and iOS
|
||||
PE ExecutableFormat = "pe" // Portable Executable format used on Windows
|
||||
@@ -41,20 +34,6 @@ type Executable struct {
|
||||
|
||||
// ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF.
|
||||
ELFSecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
|
||||
|
||||
// Toolchains captures information about the compiler, linker, runtime, or other toolchains used to build (or otherwise exist within) the executable.
|
||||
Toolchains []Toolchain `json:"toolchains,omitempty" yaml:"toolchains" mapstructure:"toolchains"`
|
||||
}
|
||||
|
||||
type Toolchain struct {
|
||||
// Name is the name of the toolchain (e.g., "gcc", "clang", "rust", "lld", "GNU AS", etc.).
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
|
||||
// Version is the version of the toolchain.
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version"`
|
||||
|
||||
// Component indicates which part of the toolchain this represents (e.g., compiler, linker, assembler).
|
||||
Component ToolchainComponent `json:"component" yaml:"component" mapstructure:"component"`
|
||||
}
|
||||
|
||||
// ELFSecurityFeatures captures security hardening and protection mechanisms in ELF binaries.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"path"
|
||||
@@ -179,7 +180,8 @@ func toRootRelationships(rootPackage *spdx.Package, packages []*spdx.Package) (o
|
||||
func toRootPackage(s source.Description) *spdx.Package {
|
||||
var prefix string
|
||||
|
||||
name := s.Name
|
||||
// the SPDX package name is mandatory, and a source decoded from another SBOM may not carry one
|
||||
name := cmp.Or(s.Name, imageRepoName(s.Metadata), s.ID, helpers.DocumentName(s))
|
||||
version := s.Version
|
||||
|
||||
var purl *packageurl.PackageURL
|
||||
@@ -210,7 +212,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
checksums = append(checksums, *c)
|
||||
purl = &packageurl.PackageURL{
|
||||
Type: "oci",
|
||||
Name: s.Name,
|
||||
Name: name,
|
||||
Version: m.ManifestDigest,
|
||||
Qualifiers: qualifiers,
|
||||
}
|
||||
@@ -240,7 +242,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
checksums = append(checksums, *c)
|
||||
purl = &packageurl.PackageURL{
|
||||
Type: "oci",
|
||||
Name: s.Name,
|
||||
Name: name,
|
||||
Version: m.ManifestDigest,
|
||||
Qualifiers: qualifiers,
|
||||
}
|
||||
@@ -275,10 +277,6 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
default:
|
||||
prefix = prefixUnknown
|
||||
purpose = spdxPrimaryPurposeOther
|
||||
|
||||
if name == "" {
|
||||
name = s.ID
|
||||
}
|
||||
}
|
||||
|
||||
p := &spdx.Package{
|
||||
@@ -308,6 +306,25 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
return p
|
||||
}
|
||||
|
||||
// imageRepoName returns the repository name from an image or model user input, the same way the image source names
|
||||
// itself, so a source decoded from another SBOM (which may only carry the user input) gets a name without the tag.
|
||||
func imageRepoName(metadata any) string {
|
||||
var userInput string
|
||||
switch m := metadata.(type) {
|
||||
case source.ImageMetadata:
|
||||
userInput = m.UserInput
|
||||
case source.OCIModelMetadata:
|
||||
userInput = m.UserInput
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
ref, _ := reference.Parse(userInput)
|
||||
if named, ok := ref.(reference.Named); ok {
|
||||
return named.Name()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
id := string(identifiable.ID())
|
||||
if after, ok := strings.CutPrefix(id, "SPDXRef-"); ok {
|
||||
@@ -581,10 +598,13 @@ func toPackageChecksums(p pkg.Package) ([]spdx.Checksum, bool) {
|
||||
})
|
||||
case pkg.OpamPackage:
|
||||
for _, checksum := range meta.Checksums {
|
||||
parts := strings.Split(checksum, "=")
|
||||
algorithm, value, ok := strings.Cut(checksum, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
checksums = append(checksums, spdx.Checksum{
|
||||
Algorithm: spdx.ChecksumAlgorithm(strings.ToUpper(parts[0])),
|
||||
Value: parts[1],
|
||||
Algorithm: spdx.ChecksumAlgorithm(strings.ToUpper(algorithm)),
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,20 @@ func Test_toPackageChecksums(t *testing.T) {
|
||||
},
|
||||
filesAnalyzed: false,
|
||||
},
|
||||
{
|
||||
name: "Opam Package with a checksum missing its algorithm separator",
|
||||
pkg: pkg.Package{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Metadata: pkg.OpamPackage{
|
||||
Checksums: []string{"f5f1c0b4", "md5=abc"},
|
||||
},
|
||||
},
|
||||
expected: []spdx.Checksum{
|
||||
{Algorithm: "MD5", Value: "abc"},
|
||||
},
|
||||
filesAnalyzed: false,
|
||||
},
|
||||
{
|
||||
name: "Package with no metadata type",
|
||||
pkg: pkg.Package{
|
||||
@@ -1131,3 +1145,49 @@ func Test_otherLicenses(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toRootPackage_name(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
src source.Description
|
||||
wantName string
|
||||
wantPurl string
|
||||
}{
|
||||
{
|
||||
name: "source name wins",
|
||||
src: source.Description{Name: "named", ID: "id", Metadata: source.ImageMetadata{UserInput: "alpine:3.18"}},
|
||||
wantName: "named",
|
||||
},
|
||||
{
|
||||
// a CycloneDX container component decodes to an image source with only the user input set
|
||||
name: "image user input without a source name",
|
||||
src: source.Description{Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:3.18",
|
||||
ManifestDigest: "sha256:0e4b2e0d6b2f3c2b1d3bb3a1c8f1c4d9e6a9b1e8c6f4d2b3a1f0e9d8c7b6a5f4",
|
||||
}},
|
||||
wantName: "alpine",
|
||||
wantPurl: "pkg:oci/alpine@sha256%3A0e4b2e0d6b2f3c2b1d3bb3a1c8f1c4d9e6a9b1e8c6f4d2b3a1f0e9d8c7b6a5f4?arch=&tag=3.18",
|
||||
},
|
||||
{
|
||||
name: "id without a source name",
|
||||
src: source.Description{ID: "some-id"},
|
||||
wantName: "some-id",
|
||||
},
|
||||
{
|
||||
name: "nothing to name it by",
|
||||
src: source.Description{Metadata: source.DirectoryMetadata{}},
|
||||
wantName: "unknown",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := toRootPackage(tt.src)
|
||||
assert.Equal(t, tt.wantName, p.PackageName)
|
||||
var purl string
|
||||
if len(p.PackageExternalReferences) > 0 {
|
||||
purl = p.PackageExternalReferences[0].Locator
|
||||
}
|
||||
assert.Equal(t, tt.wantPurl, purl)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +147,24 @@ func extractSource(spdxIDMap map[string]any, doc *spdx.Document) source.Descript
|
||||
}
|
||||
|
||||
p := rootPackages[0]
|
||||
rootType := syftRootType(p.PackageSPDXIdentifier)
|
||||
|
||||
switch p.PrimaryPackagePurpose {
|
||||
purpose := p.PrimaryPackagePurpose
|
||||
if purpose == "" {
|
||||
// SPDX 2.1 and 2.2 have no primary package purpose, so fall back to the source type syft puts in the root's ID
|
||||
purpose = syftRootPurpose(rootType)
|
||||
}
|
||||
|
||||
switch purpose {
|
||||
case spdxPrimaryPurposeContainer:
|
||||
src = containerSource(p)
|
||||
case spdxPrimaryPurposeFile:
|
||||
src = fileSource(p)
|
||||
case spdxPrimaryPurposeOther:
|
||||
// syft writes a root package even for a source it knows nothing about, which is not a package in the SBOM
|
||||
if rootType != prefixUnknown {
|
||||
return src
|
||||
}
|
||||
default:
|
||||
return src
|
||||
}
|
||||
@@ -165,6 +177,30 @@ func extractSource(spdxIDMap map[string]any, doc *spdx.Document) source.Descript
|
||||
return src
|
||||
}
|
||||
|
||||
// syftRootType returns the source type syft encodes in a root package ID ("DocumentRoot-<type>-<name>"), or "" for
|
||||
// any other ID.
|
||||
func syftRootType(id spdx.ElementID) string {
|
||||
rest, ok := strings.CutPrefix(string(id), "DocumentRoot-")
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
rootType, _, _ := strings.Cut(rest, "-")
|
||||
return rootType
|
||||
}
|
||||
|
||||
// syftRootPurpose returns the primary package purpose syft writes for a root package of the given source type.
|
||||
func syftRootPurpose(rootType string) string {
|
||||
switch rootType {
|
||||
case prefixImage, prefixOCIModel, prefixSnap:
|
||||
return spdxPrimaryPurposeContainer
|
||||
case prefixDirectory, prefixFile:
|
||||
return spdxPrimaryPurposeFile
|
||||
case prefixUnknown:
|
||||
return spdxPrimaryPurposeOther
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func containerSource(p *spdx.Package) source.Description {
|
||||
id := string(p.PackageSPDXIdentifier)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
syftinternal "github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
@@ -61,7 +62,7 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
var errs []error
|
||||
pkgs := pkg.NewCollection()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner := syftinternal.NewLineScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
@@ -86,6 +87,9 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
p.SetID()
|
||||
pkgs.Add(p)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
|
||||
@@ -114,6 +114,45 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
Subject: testutil.SourcePackagesInput(t, dir),
|
||||
Format: getEncoder(t),
|
||||
UpdateSnapshot: *updateSnapshot,
|
||||
PersistRedactionsInSnapshot: true,
|
||||
IsJSON: true,
|
||||
Redactor: redactor(dir),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder_SpecVersions(t *testing.T) {
|
||||
s := testutil.SourcePackagesInput(t, t.TempDir())
|
||||
tests := []struct {
|
||||
version string
|
||||
want bool
|
||||
}{
|
||||
{version: "1.4", want: false},
|
||||
{version: "1.5", want: false},
|
||||
{version: "1.6", want: true},
|
||||
{version: cyclonedxutil.DefaultVersion, want: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.version, func(t *testing.T) {
|
||||
enc, err := NewFormatEncoderWithConfig(EncoderConfig{Version: tt.version})
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, s))
|
||||
|
||||
// the source-distribution reference type only exists from spec 1.6
|
||||
assert.Equal(t, tt.want, strings.Contains(buf.String(), `"source-distribution"`))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func redactor(values ...string) testutil.Redactor {
|
||||
return testutil.NewRedactions().
|
||||
WithValuesRedacted(values...).
|
||||
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
{
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.7",
|
||||
"serialNumber": "urn:uuid:redacted",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": "timestamp:redacted",
|
||||
"tools": {
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"author": "anchore",
|
||||
"name": "syft",
|
||||
"version": "v0.42.0-bogus"
|
||||
}
|
||||
]
|
||||
},
|
||||
"component": {
|
||||
"bom-ref": "redacted",
|
||||
"type": "file",
|
||||
"name": "some/path"
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"bom-ref": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&package-id=5cc0e8979d7f5115&upstream=attr%401%3A2.4.47-2",
|
||||
"type": "library",
|
||||
"name": "attr",
|
||||
"version": "1:2.4.47-2+b1",
|
||||
"purl": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "dpkg-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "dpkg-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/dpkg/status"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:source",
|
||||
"value": "attr"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:sourceVersion",
|
||||
"value": "1:2.4.47-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&package-id=6008fca05ca7b418&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
"type": "library",
|
||||
"name": "bind-export-libs",
|
||||
"version": "32:9.11.13-3.el8",
|
||||
"purl": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "rpm-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "rpm"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "rpm-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/rpm/Packages"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:epoch",
|
||||
"value": "32"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:release",
|
||||
"value": "3.el8"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:sourceRpm",
|
||||
"value": "bind-9.11.13-3.el8.src.rpm"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3&package-id=aa74af1190de1c40",
|
||||
"type": "library",
|
||||
"name": "busybox",
|
||||
"version": "1.35.0-r17",
|
||||
"purl": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "apk-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "apk"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "apk-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/lib/apk/db/installed"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:originPackage",
|
||||
"value": "busybox"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&package-id=58a2fcf749a9a3cc&upstream=gcc",
|
||||
"type": "library",
|
||||
"name": "gcc-libs",
|
||||
"version": "13.2.1-3",
|
||||
"purl": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "alpm-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "alpm"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "alpm-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/pacman/local/gcc-libs-13.2.1-3/desc"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:architecture",
|
||||
"value": "x86_64"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:basepackage",
|
||||
"value": "gcc"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:package",
|
||||
"value": "gcc-libs"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:version",
|
||||
"value": "13.2.1-3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&package-id=16a8c7b19fde6f4f&upstream=libc-dev",
|
||||
"type": "library",
|
||||
"name": "libc-utils",
|
||||
"version": "0.7.2-r3",
|
||||
"purl": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "apk-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "apk"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "apk-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/lib/apk/db/installed"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:originPackage",
|
||||
"value": "libc-dev"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&package-id=7b4ddc97df57aec4&upstream=pam",
|
||||
"type": "library",
|
||||
"name": "libpam-runtime",
|
||||
"version": "1.5.2-6+deb12u1",
|
||||
"purl": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "dpkg-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "dpkg-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/dpkg/status"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:source",
|
||||
"value": "pam"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -87,6 +87,20 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
Subject: testutil.SourcePackagesInput(t, dir),
|
||||
Format: getEncoder(t),
|
||||
UpdateSnapshot: *updateSnapshot,
|
||||
PersistRedactionsInSnapshot: true,
|
||||
IsJSON: false,
|
||||
Redactor: redactor(dir),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func redactor(values ...string) testutil.Redactor {
|
||||
return testutil.NewRedactions().
|
||||
WithValuesRedacted(values...).
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
|
||||
<metadata>
|
||||
<timestamp>redacted</timestamp>
|
||||
<tools>
|
||||
<components>
|
||||
<component type="application">
|
||||
<author>anchore</author>
|
||||
<name>syft</name>
|
||||
<version>v0.42.0-bogus</version>
|
||||
</component>
|
||||
</components>
|
||||
</tools>
|
||||
<component bom-ref="redacted" type="file">
|
||||
<name>some/path</name>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component bom-ref="pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&package-id=5cc0e8979d7f5115&upstream=attr%401%3A2.4.47-2" type="library">
|
||||
<name>attr</name>
|
||||
<version>1:2.4.47-2+b1</version>
|
||||
<purl>pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:package:metadataType">dpkg-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:source">attr</property>
|
||||
<property name="syft:metadata:sourceVersion">1:2.4.47-2</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&package-id=6008fca05ca7b418&upstream=bind-9.11.13-3.el8.src.rpm" type="library">
|
||||
<name>bind-export-libs</name>
|
||||
<version>32:9.11.13-3.el8</version>
|
||||
<purl>pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">rpm-db-cataloger</property>
|
||||
<property name="syft:package:type">rpm</property>
|
||||
<property name="syft:package:metadataType">rpm-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/rpm/Packages</property>
|
||||
<property name="syft:metadata:epoch">32</property>
|
||||
<property name="syft:metadata:release">3.el8</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
<property name="syft:metadata:sourceRpm">bind-9.11.13-3.el8.src.rpm</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3&package-id=aa74af1190de1c40" type="library">
|
||||
<name>busybox</name>
|
||||
<version>1.35.0-r17</version>
|
||||
<purl>pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">apk-db-cataloger</property>
|
||||
<property name="syft:package:type">apk</property>
|
||||
<property name="syft:package:metadataType">apk-db-entry</property>
|
||||
<property name="syft:location:0:path">/lib/apk/db/installed</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:originPackage">busybox</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&package-id=58a2fcf749a9a3cc&upstream=gcc" type="library">
|
||||
<name>gcc-libs</name>
|
||||
<version>13.2.1-3</version>
|
||||
<purl>pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">alpm-db-cataloger</property>
|
||||
<property name="syft:package:type">alpm</property>
|
||||
<property name="syft:package:metadataType">alpm-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/pacman/local/gcc-libs-13.2.1-3/desc</property>
|
||||
<property name="syft:metadata:architecture">x86_64</property>
|
||||
<property name="syft:metadata:basepackage">gcc</property>
|
||||
<property name="syft:metadata:package">gcc-libs</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
<property name="syft:metadata:version">13.2.1-3</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&package-id=16a8c7b19fde6f4f&upstream=libc-dev" type="library">
|
||||
<name>libc-utils</name>
|
||||
<version>0.7.2-r3</version>
|
||||
<purl>pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">apk-db-cataloger</property>
|
||||
<property name="syft:package:type">apk</property>
|
||||
<property name="syft:package:metadataType">apk-db-entry</property>
|
||||
<property name="syft:location:0:path">/lib/apk/db/installed</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:originPackage">libc-dev</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&package-id=7b4ddc97df57aec4&upstream=pam" type="library">
|
||||
<name>libpam-runtime</name>
|
||||
<version>1.5.2-6+deb12u1</version>
|
||||
<purl>pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:package:metadataType">dpkg-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:source">pam</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
</bom>
|
||||
@@ -2,6 +2,7 @@ package cyclonedxutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
@@ -29,9 +30,35 @@ func NewEncoder(version string, format cyclonedx.BOMFileFormat, pretty bool) (En
|
||||
|
||||
func (e Encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
bom := cyclonedxhelpers.ToFormatModel(s)
|
||||
if e.version < cyclonedx.SpecVersion1_6 {
|
||||
// cyclonedx-go does not downgrade this 1.6+ reference type for older spec versions
|
||||
// (see https://github.com/CycloneDX/cyclonedx-go/issues/291), so drop it rather than emit an invalid document
|
||||
dropExternalReferenceType(bom.Components, cyclonedx.ERTypeSourceDistribution)
|
||||
}
|
||||
enc := cyclonedx.NewBOMEncoder(writer, e.format)
|
||||
enc.SetPretty(e.pretty)
|
||||
enc.SetEscapeHTML(false)
|
||||
|
||||
return enc.EncodeVersion(bom, e.version)
|
||||
}
|
||||
|
||||
func dropExternalReferenceType(components *[]cyclonedx.Component, refType cyclonedx.ExternalReferenceType) {
|
||||
if components == nil {
|
||||
return
|
||||
}
|
||||
for i := range *components {
|
||||
c := &(*components)[i]
|
||||
dropExternalReferenceType(c.Components, refType)
|
||||
if c.ExternalReferences == nil {
|
||||
continue
|
||||
}
|
||||
refs := slices.DeleteFunc(*c.ExternalReferences, func(r cyclonedx.ExternalReference) bool {
|
||||
return r.Type == refType
|
||||
})
|
||||
if len(refs) == 0 {
|
||||
c.ExternalReferences = nil
|
||||
continue
|
||||
}
|
||||
c.ExternalReferences = &refs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/file"
|
||||
syftFile "github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -79,12 +80,162 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
|
||||
}
|
||||
}
|
||||
}
|
||||
if srcRef := encodeSourcePackageExternalReference(p); srcRef != nil {
|
||||
refs = append(refs, *srcRef)
|
||||
}
|
||||
if len(refs) > 0 {
|
||||
return &refs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeSourcePackageExternalReference returns a "source-distribution" reference holding the PURL of the distro
|
||||
// source package that a binary OS package was built from (e.g. libpam-runtime -> pkg:deb/debian/pam@...?arch=source).
|
||||
// It is only emitted when the source differs from the binary package (by name or version) and when the package PURL
|
||||
// is of the type implied by the metadata. The main consumer is vulnerability matching, since distros tend to report
|
||||
// vulnerabilities against the source package rather than each binary package built from it.
|
||||
//
|
||||
// why an external reference and not pedigree: `component.pedigree.ancestors` looks like the obvious spot for "this
|
||||
// binary came from that source", but pedigree describes code lineage (forks, patched variants, and the patches that
|
||||
// separate them). A binary built unmodified from its own distro source package is the same code in a different form,
|
||||
// not a fork, and CycloneDX components are deliberately agnostic to source vs binary form (see the comments from the
|
||||
// CycloneDX maintainers in https://github.com/CycloneDX/specification/issues/612#issuecomment-2958800363). The
|
||||
// ancestor slot is better left for the upstream project a distro patched (e.g. Linux-PAM for Debian's pam), which
|
||||
// syft does not know today.
|
||||
//
|
||||
// why "source-distribution": the spec defines it as "the location where the source code distributable can be
|
||||
// obtained", and a deb/rpm source package is exactly that distributable (a .dsc and tarballs in the Debian source
|
||||
// pool, a .src.rpm in an SRPMS repo). The value is a PURL (an identifier) rather than a download URL, which the
|
||||
// schema allows since external reference URLs are URIs of any scheme, and a CycloneDX maintainer confirmed this exact
|
||||
// shape is valid in https://github.com/CycloneDX/specification/issues/612#issuecomment-5428057874. A first-class
|
||||
// "source" component type was discussed in that issue and deferred to CycloneDX 2.0.
|
||||
func encodeSourcePackageExternalReference(p pkg.Package) *cyclonedx.ExternalReference {
|
||||
if p.PURL == "" {
|
||||
return nil
|
||||
}
|
||||
src := sourcePackageOf(p)
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// the binary PURL is only consulted for the namespace and a small set of qualifiers (e.g. distro), everything
|
||||
// else describes the binary package and must not be restated as a fact about the source package.
|
||||
binPurl, err := packageurl.FromString(p.PURL)
|
||||
if err != nil || binPurl.Type != src.purlType {
|
||||
return nil
|
||||
}
|
||||
|
||||
qualifiers := map[string]string{}
|
||||
for _, q := range binPurl.Qualifiers {
|
||||
if src.keepQualifier(q.Key) {
|
||||
qualifiers[q.Key] = q.Value
|
||||
}
|
||||
}
|
||||
if src.arch != "" {
|
||||
qualifiers[pkg.PURLQualifierArch] = src.arch
|
||||
}
|
||||
|
||||
srcPurl := packageurl.NewPackageURL(
|
||||
src.purlType,
|
||||
binPurl.Namespace,
|
||||
src.name,
|
||||
src.version,
|
||||
packageurl.QualifiersFromMap(qualifiers),
|
||||
"",
|
||||
)
|
||||
|
||||
return &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution,
|
||||
URL: srcPurl.ToString(),
|
||||
}
|
||||
}
|
||||
|
||||
// sourcePackage is the identity of the source package a binary OS package was built from.
|
||||
type sourcePackage struct {
|
||||
purlType string
|
||||
name string
|
||||
version string
|
||||
// arch is the ecosystem's source architecture marker ("source" for deb, "src" or "nosrc" for rpm)
|
||||
arch string
|
||||
}
|
||||
|
||||
func (s sourcePackage) keepQualifier(key string) bool {
|
||||
switch key {
|
||||
case pkg.PURLQualifierDistro:
|
||||
return true
|
||||
case pkg.PURLQualifierEpoch, pkg.PURLQualifierRpmModularity:
|
||||
return s.purlType == pkg.RpmPkg.PackageURLType()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sourcePackageOf returns the source package for p, or nil when there is none or it is the same as the binary.
|
||||
//
|
||||
// only deb and rpm are supported, since those are the ecosystems that publish real source packages with their own
|
||||
// PURL identity (arch=source / arch=src). apk (origin) and alpm (pkgbase) are intentionally left out: there the
|
||||
// "source" is a build recipe rather than a published source distributable, and its name is usually also the name of
|
||||
// a real binary package (e.g. pkg:apk/alpine/libc-dev@0.7.2-r3 is an installable package, not a source archive), so
|
||||
// a "source-distribution" reference would point at a binary package. Whether those ecosystems have a meaningful
|
||||
// source PURL is still an open question, and until then the `upstream` PURL qualifier continues to carry that info.
|
||||
func sourcePackageOf(p pkg.Package) *sourcePackage {
|
||||
var src sourcePackage
|
||||
var binName, binVersion string
|
||||
switch m := p.Metadata.(type) {
|
||||
case pkg.DpkgDBEntry:
|
||||
src = dpkgSourcePackage(m)
|
||||
binName, binVersion = m.Package, m.Version
|
||||
case pkg.DpkgArchiveEntry:
|
||||
src = dpkgSourcePackage(pkg.DpkgDBEntry(m))
|
||||
binName, binVersion = m.Package, m.Version
|
||||
case pkg.RpmDBEntry:
|
||||
src = rpmSourcePackage(m.SourceRpm)
|
||||
binName, binVersion = m.Name, m.Version+"-"+m.Release
|
||||
case pkg.RpmArchive:
|
||||
src = rpmSourcePackage(m.SourceRpm)
|
||||
binName, binVersion = m.Name, m.Version+"-"+m.Release
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if src.name == "" || src.version == "" {
|
||||
return nil
|
||||
}
|
||||
if src.name == binName && src.version == binVersion {
|
||||
return nil
|
||||
}
|
||||
return &src
|
||||
}
|
||||
|
||||
func dpkgSourcePackage(entry pkg.DpkgDBEntry) sourcePackage {
|
||||
version := entry.SourceVersion
|
||||
if version == "" {
|
||||
version = entry.Version
|
||||
}
|
||||
return sourcePackage{purlType: pkg.DebPkg.PackageURLType(), name: entry.Source, version: version, arch: "source"}
|
||||
}
|
||||
|
||||
// rpmSourcePackage parses a source RPM filename of the form <name>-<version>-<release>.(no)src.rpm.
|
||||
func rpmSourcePackage(sourceRpm string) sourcePackage {
|
||||
var arch string
|
||||
switch {
|
||||
case strings.HasSuffix(sourceRpm, ".src.rpm"):
|
||||
arch = "src"
|
||||
case strings.HasSuffix(sourceRpm, ".nosrc.rpm"):
|
||||
arch = "nosrc"
|
||||
default:
|
||||
return sourcePackage{}
|
||||
}
|
||||
nvr := strings.TrimSuffix(sourceRpm, "."+arch+".rpm")
|
||||
release := strings.LastIndex(nvr, "-")
|
||||
if release < 0 {
|
||||
return sourcePackage{}
|
||||
}
|
||||
ver := strings.LastIndex(nvr[:release], "-")
|
||||
if ver < 0 {
|
||||
return sourcePackage{}
|
||||
}
|
||||
return sourcePackage{purlType: pkg.RpmPkg.PackageURLType(), name: nvr[:ver], version: nvr[ver+1:], arch: arch}
|
||||
}
|
||||
|
||||
// supported algorithm in cycloneDX as of 1.4
|
||||
// "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512",
|
||||
// "SHA3-256", "SHA3-384", "SHA3-512", "BLAKE2b-256", "BLAKE2b-384", "BLAKE2b-512", "BLAKE3"
|
||||
|
||||
@@ -144,6 +144,342 @@ func Test_encodeExternalReferences(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encodeSourcePackage(t *testing.T) {
|
||||
rpmEpoch := 32
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
expected *cyclonedx.ExternalReference
|
||||
}{
|
||||
{
|
||||
name: "no metadata",
|
||||
input: pkg.Package{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "no PURL",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
// apk origins are build recipes, not published source packages, so they are not supported
|
||||
name: "from apk is not supported",
|
||||
input: pkg.Package{
|
||||
Name: "libc-utils",
|
||||
Version: "0.7.2-r3",
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "libc-utils",
|
||||
OriginPackage: "libc-dev",
|
||||
Version: "0.7.2-r3",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from deb",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from deb with source version",
|
||||
input: pkg.Package{
|
||||
Name: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Source: "attr",
|
||||
SourceVersion: "1:2.4.47-2",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from deb with empty source",
|
||||
input: pkg.Package{
|
||||
Name: "some-package",
|
||||
Version: "1.0.0",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/some-package@1.0.0?arch=all&distro=debian-12",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "some-package",
|
||||
Version: "1.0.0",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from deb archive",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&upstream=pam",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/pam@1.5.2-6%2Bdeb12u1?arch=source",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm",
|
||||
input: pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm with source = bin",
|
||||
input: pkg.Package{
|
||||
Name: "some-package",
|
||||
Version: "32:1.0.0-1.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/centos/some-package@1.0.0-1.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=some-package-1.0.0-1.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "some-package",
|
||||
Version: "1.0.0",
|
||||
Release: "1.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "some-package-1.0.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm archive",
|
||||
input: pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/bind-export-libs@9.11.13-3.el8?arch=x86_64&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmArchive{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/bind@9.11.13-3.el8?arch=src&epoch=32",
|
||||
},
|
||||
},
|
||||
{
|
||||
// alpm pkgbases are build recipes, not published source packages, so they are not supported
|
||||
name: "from alpm is not supported",
|
||||
input: pkg.Package{
|
||||
Name: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
BasePackage: "gcc",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with dashes in the source name",
|
||||
input: pkg.Package{
|
||||
Name: "perl-Foo-Bar-libs",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/perl-Foo-Bar-libs@1.0-1.el8?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "perl-Foo-Bar-libs",
|
||||
Version: "1.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "perl-Foo-Bar-1.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/perl-Foo-Bar@1.0-1.el8?arch=src&distro=rhel-8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm nosrc",
|
||||
input: pkg.Package{
|
||||
Name: "some-firmware",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/some-firmware@1.0-1.el8?arch=noarch&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "some-firmware",
|
||||
Version: "1.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "firmware-1.0-1.el8.nosrc.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/firmware@1.0-1.el8?arch=nosrc&distro=rhel-8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm keeps rpmmod",
|
||||
input: pkg.Package{
|
||||
Name: "nodejs-libs",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/nodejs-libs@18.0-1.el8?arch=x86_64&distro=rhel-8&rpmmod=nodejs%3A18",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "nodejs-libs",
|
||||
Version: "18.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "nodejs-18.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/nodejs@18.0-1.el8?arch=src&distro=rhel-8&rpmmod=nodejs%3A18",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm with malformed source rpm",
|
||||
input: pkg.Package{
|
||||
Name: "gpg-pubkey",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/gpg-pubkey@1-1?distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "gpg-pubkey",
|
||||
Version: "1",
|
||||
Release: "1",
|
||||
SourceRpm: "(none)",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with non-source rpm filename",
|
||||
input: pkg.Package{
|
||||
Name: "bash",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bash",
|
||||
Version: "5.1",
|
||||
Release: "2",
|
||||
SourceRpm: "bash-5.1-2.x86_64.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with too few dashes",
|
||||
input: pkg.Package{
|
||||
Name: "bash",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bash",
|
||||
Version: "5.1",
|
||||
Release: "2",
|
||||
SourceRpm: "bash-5.1.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "binary qualifiers and subpath are not carried over",
|
||||
input: pkg.Package{
|
||||
Name: "libssl3",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/libssl3@3.0.0-1?arch=amd64&distro=debian-12&checksum=sha256:dead&download_url=https://example.com/x.deb&cpes=cpe:2.3:a:x:x:1:*:*:*:*:*:*:*#sub/dir",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libssl3",
|
||||
Version: "3.0.0-1",
|
||||
Source: "openssl",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/openssl@3.0.0-1?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PURL type does not match metadata",
|
||||
input: pkg.Package{
|
||||
Name: "x",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:npm/x@1",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "x",
|
||||
Version: "1",
|
||||
Source: "lodash",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid PURL",
|
||||
input: pkg.Package{
|
||||
Name: "x",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "not-a-purl",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "x",
|
||||
Version: "1",
|
||||
Source: "y",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, encodeSourcePackageExternalReference(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isValidExternalRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -9,16 +9,21 @@ func DocumentName(src source.Description) string {
|
||||
return src.Name
|
||||
}
|
||||
|
||||
var name string
|
||||
switch metadata := src.Metadata.(type) {
|
||||
case source.ImageMetadata:
|
||||
return metadata.UserInput
|
||||
name = metadata.UserInput
|
||||
case source.OCIModelMetadata:
|
||||
return metadata.UserInput
|
||||
name = metadata.UserInput
|
||||
case source.DirectoryMetadata:
|
||||
return metadata.Path
|
||||
name = metadata.Path
|
||||
case source.FileMetadata:
|
||||
return metadata.Path
|
||||
default:
|
||||
name = metadata.Path
|
||||
}
|
||||
|
||||
// the SPDX document name is mandatory, and a source read from another SBOM may not have one
|
||||
if name == "" {
|
||||
return "unknown"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -73,11 +71,23 @@ func Test_DocumentName(t *testing.T) {
|
||||
},
|
||||
expected: "some/name",
|
||||
},
|
||||
{
|
||||
name: "no name or metadata",
|
||||
srcMetadata: source.Description{},
|
||||
expected: "unknown",
|
||||
},
|
||||
{
|
||||
name: "metadata without a name",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := DocumentName(test.srcMetadata)
|
||||
assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual name %q", actual))
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
// track each scheme tested (passed or not)
|
||||
tracker.Tested(t, test.srcMetadata.Metadata)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
)
|
||||
|
||||
func SourcePackagesInput(t testing.TB, dir string) sbom.SBOM {
|
||||
path := filepath.Join(dir, "some", "path")
|
||||
require.NoError(t, os.MkdirAll(path, 0755))
|
||||
|
||||
src, err := directorysource.New(
|
||||
directorysource.Config{
|
||||
Path: path,
|
||||
Base: dir,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: newSourcePackageCatalog(),
|
||||
},
|
||||
Source: src.Describe(),
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft",
|
||||
Version: "v0.42.0-bogus",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func newSourcePackageCatalog() *pkg.Collection {
|
||||
epoch := 32
|
||||
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
// apk: the origin differs from the package name, but apk is not supported so no source reference is expected
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "libc-utils",
|
||||
Version: "0.7.2-r3",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
|
||||
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "libc-utils",
|
||||
OriginPackage: "libc-dev",
|
||||
Version: "0.7.2-r3",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
// apk: negative control -- the package is its own origin
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "busybox",
|
||||
Version: "1.35.0-r17",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
|
||||
PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "busybox",
|
||||
OriginPackage: "busybox",
|
||||
Version: "1.35.0-r17",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
// deb: source name differs, and the source version is implied by the binary version
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
|
||||
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
})
|
||||
|
||||
// deb: the source version differs from the binary version
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
|
||||
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Source: "attr",
|
||||
SourceVersion: "1:2.4.47-2",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
})
|
||||
|
||||
// rpm: name and version both come out of the source RPM filename, and the epoch is carried over
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
FoundBy: "rpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/rpm/Packages")),
|
||||
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &epoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
})
|
||||
|
||||
// alpm: the base package differs from the package name, but alpm is not supported so no source reference is expected
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/pacman/local/gcc-libs-13.2.1-3/desc")),
|
||||
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
BasePackage: "gcc",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
return catalog
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
syftinternal "github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -61,7 +62,7 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
var errs []error
|
||||
pkgs := pkg.NewCollection()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner := syftinternal.NewLineScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
@@ -84,6 +85,9 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
p.SetID()
|
||||
pkgs.Add(p)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
|
||||
@@ -236,23 +236,6 @@ func TestSupportedVersions(t *testing.T) {
|
||||
subject := testutil.DirectoryInput(t, t.TempDir())
|
||||
dec := NewFormatDecoder()
|
||||
|
||||
relationshipOffsetPerVersion := map[string]int{
|
||||
// the package representing the source gets a relationship from the source package to all other packages found
|
||||
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
|
||||
"2.1": 2,
|
||||
"2.2": 2,
|
||||
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
|
||||
"2.3": 0,
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
pkgCountOffsetPerVersion := map[string]int{
|
||||
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
for _, enc := range encs {
|
||||
t.Run(enc.Version(), func(t *testing.T) {
|
||||
require.Contains(t, versions, enc.Version())
|
||||
@@ -274,13 +257,9 @@ func TestSupportedVersions(t *testing.T) {
|
||||
|
||||
require.NotEmpty(t, s.Artifacts.Packages.PackageCount())
|
||||
|
||||
offset := relationshipOffsetPerVersion[enc.Version()]
|
||||
assert.Equal(t, len(subject.Relationships), len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
assert.Equal(t, len(subject.Relationships)+offset, len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
offset = pkgCountOffsetPerVersion[enc.Version()]
|
||||
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount()+offset, s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount(), s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
t.Logf("expected: %d", subject.Artifacts.Packages.PackageCount())
|
||||
for _, p := range subject.Artifacts.Packages.Sorted() {
|
||||
t.Logf(" - %s", p.String())
|
||||
|
||||
+5
-5
@@ -47,13 +47,13 @@
|
||||
"copyrightText": "NOASSERTION"
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"copyrightText": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"name": "",
|
||||
"name": "unknown",
|
||||
"supplier": "NOASSERTION"
|
||||
}
|
||||
],
|
||||
@@ -82,18 +82,18 @@
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DOCUMENT",
|
||||
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relationshipType": "DESCRIBES"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ package spdxtagvalue
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -143,21 +144,6 @@ func TestSupportedVersions(t *testing.T) {
|
||||
subject := testutil.DirectoryInput(t, t.TempDir())
|
||||
dec := NewFormatDecoder()
|
||||
|
||||
relationshipOffsetPerVersion := map[string]int{
|
||||
// the package representing the source gets a relationship from the source package to all other packages found
|
||||
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
|
||||
"2.1": 2,
|
||||
"2.2": 2,
|
||||
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
|
||||
"2.3": 0,
|
||||
}
|
||||
|
||||
pkgCountOffsetPerVersion := map[string]int{
|
||||
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
|
||||
}
|
||||
|
||||
for _, enc := range encs {
|
||||
t.Run(enc.Version(), func(t *testing.T) {
|
||||
require.Contains(t, versions, enc.Version())
|
||||
@@ -178,13 +164,9 @@ func TestSupportedVersions(t *testing.T) {
|
||||
|
||||
require.NotEmpty(t, s.Artifacts.Packages.PackageCount())
|
||||
|
||||
offset := relationshipOffsetPerVersion[enc.Version()]
|
||||
assert.Equal(t, len(subject.Relationships), len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
assert.Equal(t, len(subject.Relationships)+offset, len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
offset = pkgCountOffsetPerVersion[enc.Version()]
|
||||
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount()+offset, s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount(), s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
t.Logf("expected: %d", subject.Artifacts.Packages.PackageCount())
|
||||
for _, p := range subject.Artifacts.Packages.Sorted() {
|
||||
t.Logf(" - %s", p.String())
|
||||
@@ -209,3 +191,39 @@ func defaultFormatEncoders() []sbom.FormatEncoder {
|
||||
}
|
||||
return encs
|
||||
}
|
||||
|
||||
func TestEncodeDecodeSourceWithoutName(t *testing.T) {
|
||||
// a source read from another SBOM (a purl list, a CycloneDX document without metadata.component) may have no name,
|
||||
// but the SPDX package name is mandatory and the tag-value decoder needs it to start the root package section
|
||||
p := pkg.Package{Name: "left-pad", Version: "1.3.0", Type: pkg.NpmPkg}
|
||||
p.SetID()
|
||||
|
||||
for _, src := range []source.Description{
|
||||
{},
|
||||
{Metadata: source.FileMetadata{}},
|
||||
{Metadata: source.DirectoryMetadata{}},
|
||||
{Metadata: source.ImageMetadata{}},
|
||||
{Metadata: source.OCIModelMetadata{}},
|
||||
{Metadata: source.SnapMetadata{}},
|
||||
} {
|
||||
for _, enc := range defaultFormatEncoders() {
|
||||
t.Run(fmt.Sprintf("%T/%s", src.Metadata, enc.Version()), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, sbom.SBOM{
|
||||
Source: src,
|
||||
Artifacts: sbom.Artifacts{Packages: pkg.NewCollection(p)},
|
||||
}))
|
||||
|
||||
s, _, _, err := NewFormatDecoder().Decode(bytes.NewReader(buf.Bytes()))
|
||||
require.NoError(t, err, "encoded document:\n%s", buf.String())
|
||||
|
||||
var names []string
|
||||
for _, decoded := range s.Artifacts.Packages.Sorted() {
|
||||
names = append(names, decoded.Name)
|
||||
}
|
||||
// the root package describes the source, so it must not come back as a package of the SBOM
|
||||
assert.Equal(t, []string{"left-pad"}, names)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/syftjson"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// TestEncodersHandleUntypedMetadataValues pushes awkward values through the untyped metadata fields
|
||||
// allowlisted in internal/packagemetadata (see untypedFieldAllowlist) and asserts that SetID and every
|
||||
// encoder neither panic nor fail. A new allowlist entry should get a case here too.
|
||||
func TestEncodersHandleUntypedMetadataValues(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
value any
|
||||
// encoding/json rejects non-finite floats, so syft-json returns an error for them. Producers
|
||||
// have to replace these before they reach the model (the GGUF cataloger does); the case stays
|
||||
// here so the expectation flips visibly if syft-json ever learns to encode them.
|
||||
syftJSONRejects bool
|
||||
}{
|
||||
{name: "NaN", value: math.NaN(), syftJSONRejects: true},
|
||||
{name: "+Inf float32", value: float32(math.Inf(1)), syftJSONRejects: true},
|
||||
{name: "-Inf", value: math.Inf(-1), syftJSONRejects: true},
|
||||
{name: "NaN in array", value: []any{1.0, math.NaN()}, syftJSONRejects: true},
|
||||
{name: "invalid utf-8", value: "\xff\xfe"},
|
||||
{name: "NUL and control characters", value: "a\x00b\x1b"},
|
||||
{name: "max uint64", value: uint64(math.MaxUint64)},
|
||||
{name: "deep array", value: nested(2000, func(v any) any { return []any{v} })},
|
||||
{name: "deep map", value: nested(2000, func(v any) any { return map[string]any{"k": v} })},
|
||||
{name: "large array", value: make([]any, 100_000)},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := pkg.Package{
|
||||
Name: "model",
|
||||
Version: "1.0",
|
||||
Type: pkg.ModelPkg,
|
||||
Metadata: pkg.GGUFFileHeader{
|
||||
RemainingKeyValues: map[string]any{"general.value": tt.value},
|
||||
},
|
||||
}
|
||||
if !noPanic(t, "SetID", p.SetID) {
|
||||
return
|
||||
}
|
||||
|
||||
s := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{Packages: pkg.NewCollection(p)},
|
||||
Source: source.Description{Metadata: source.DirectoryMetadata{Path: "/models"}},
|
||||
}
|
||||
for _, enc := range Encoders() {
|
||||
id := fmt.Sprintf("%s@%s", enc.ID(), enc.Version())
|
||||
var buf bytes.Buffer
|
||||
noPanic(t, id, func() {
|
||||
err := enc.Encode(&buf, s)
|
||||
switch {
|
||||
case tt.syftJSONRejects && enc.ID() == syftjson.ID:
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected an encode error for a non-finite float", id)
|
||||
}
|
||||
case err != nil:
|
||||
t.Errorf("%s: encode failed: %v", id, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func nested(depth int, wrap func(any) any) any {
|
||||
var v any = "leaf"
|
||||
for range depth {
|
||||
v = wrap(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func noPanic(t *testing.T, what string, fn func()) (ok bool) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("%s: panic: %v", what, r)
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
return true
|
||||
}
|
||||
@@ -216,5 +216,8 @@ func (r ChrootContext) ToChrootPath(nativePath string) string {
|
||||
responsePath = strings.TrimPrefix(responsePath, prefix)
|
||||
}
|
||||
|
||||
return responsePath
|
||||
// chroot paths are posix regardless of host (these end up as location paths in the SBOM). This uses
|
||||
// filepath (not path) since responsePath is native here (FromPosix above, then trimmed of a native prefix),
|
||||
// and only filepath.ToSlash knows the host separator to convert.
|
||||
return filepath.ToSlash(responsePath)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package fileresolver
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
)
|
||||
|
||||
func Test_ChrootContext_RequestResponse(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
// /
|
||||
// somewhere/
|
||||
// outside.txt
|
||||
@@ -563,6 +567,9 @@ func TestNewChrootContext_BaseValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToNativeGlob(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("chroot context and expectations are posix paths")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
chrootContext ChrootContext
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/internal/windows"
|
||||
)
|
||||
|
||||
type indexerMock struct {
|
||||
@@ -138,6 +138,9 @@ func TestDirectoryIndexer_handleFileAccessErr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_IncludeRootPathInIndex(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("indexes the posix filesystem root")
|
||||
}
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if path != "/" {
|
||||
return fs.SkipDir
|
||||
@@ -206,11 +209,7 @@ func TestDirectoryIndexer_index(t *testing.T) {
|
||||
info, err := os.Stat(test.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// note: the index uses absolute paths, so assertions MUST keep this in mind
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := file.Path(path.Join(cwd, test.path))
|
||||
p := indexedPath(t, test.path)
|
||||
assert.Equal(t, true, tree.HasPath(p))
|
||||
exists, ref, err := tree.File(p)
|
||||
assert.Equal(t, true, exists)
|
||||
@@ -226,6 +225,9 @@ func TestDirectoryIndexer_index(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
// note: this test is testing the effects from NewFromDirectory, indexTree, and addPathToIndex
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
require.True(t, ok)
|
||||
@@ -256,11 +258,7 @@ func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) {
|
||||
info, err := os.Stat(test.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// note: the index uses absolute paths, so assertions MUST keep this in mind
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := file.Path(path.Join(cwd, test.path))
|
||||
p := indexedPath(t, test.path)
|
||||
assert.Equal(t, true, tree.HasPath(p))
|
||||
exists, ref, err := tree.File(p)
|
||||
assert.Equal(t, true, exists)
|
||||
@@ -287,6 +285,9 @@ func TestDirectoryIndexer_index_survive_badSymlink(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("relies on symlinks and posix permissions")
|
||||
}
|
||||
// a symlink that resolves into a directory we don't have permission to traverse should be skipped
|
||||
// with a warning like any other inaccessible path, not abort the entire scan (see #3286).
|
||||
if os.Geteuid() == 0 {
|
||||
@@ -323,6 +324,9 @@ func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T)
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_SkipsAlreadyVisitedLinkDestinations(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
var observedPaths []string
|
||||
pathObserver := func(_, p string, _ os.FileInfo, _ error) error {
|
||||
fields := strings.Split(p, "testdata/symlinks-prune-indexing")
|
||||
@@ -430,6 +434,11 @@ func TestDirectoryIndexer_IndexesAllTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_allContainedPaths(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// ponytail: production passes native windows paths here, which these posix cases don't represent.
|
||||
// Ancestor indexing for a root reached through a symlink is likely broken on windows; fix separately.
|
||||
t.Skip("cases are posix paths")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -485,6 +494,9 @@ func Test_allContainedPaths(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_relativePath(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("exercises a posix-only test helper")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
basePath string
|
||||
@@ -548,3 +560,17 @@ func relativePath(basePath, givenPath string) string {
|
||||
|
||||
return relPath
|
||||
}
|
||||
|
||||
// indexedPath returns how the given cwd-relative path is keyed in the index (absolute and always posix)
|
||||
func indexedPath(t *testing.T, rel string) file.Path {
|
||||
t.Helper()
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
// filepath (not path): cwd is a native host path, so it must be joined with the host separator before
|
||||
// being converted to the posix form the index is keyed on
|
||||
p := filepath.Join(cwd, rel)
|
||||
if windows.HostRunningOnWindows() {
|
||||
p = windows.ToPosix(p)
|
||||
}
|
||||
return file.Path(p)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user