mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Update java generic cataloger (#1329)
* remove centralize pURL generation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * port java cataloger to new generic cataloger pattern Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove common.GenericCataloger Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update format test fixtures to reflect ID updates Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix package sort instability for encode-decode-encode cycles Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
f3528132a7
commit
d7a51a69dd
2
go.mod
2
go.mod
@ -7,7 +7,6 @@ require (
|
|||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||||
github.com/acobaugh/osrelease v0.1.0
|
github.com/acobaugh/osrelease v0.1.0
|
||||||
github.com/adrg/xdg v0.3.3
|
github.com/adrg/xdg v0.3.3
|
||||||
github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074
|
|
||||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||||
@ -57,6 +56,7 @@ require (
|
|||||||
github.com/docker/docker v20.10.17+incompatible
|
github.com/docker/docker v20.10.17+incompatible
|
||||||
github.com/google/go-containerregistry v0.11.0
|
github.com/google/go-containerregistry v0.11.0
|
||||||
github.com/in-toto/in-toto-golang v0.4.1-0.20221018183522-731d0640b65f
|
github.com/in-toto/in-toto-golang v0.4.1-0.20221018183522-731d0640b65f
|
||||||
|
github.com/invopop/jsonschema v0.7.0
|
||||||
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce
|
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/sassoftware/go-rpmutils v0.2.0
|
github.com/sassoftware/go-rpmutils v0.2.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -223,8 +223,6 @@ github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U=
|
|||||||
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
|
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074 h1:Lw9q+WyJLFOR+AULchS5/2GKfM+6gOh4szzizdfH3MU=
|
|
||||||
github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
|
|
||||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@ -1220,6 +1218,8 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
|
|||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
||||||
|
github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So=
|
||||||
|
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||||
github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
|
github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
|
||||||
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
|
|||||||
@ -6,5 +6,5 @@ const (
|
|||||||
|
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// 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.
|
// 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 = "5.0.1"
|
JSONSchemaVersion = "5.1.0"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/jsonschema"
|
"github.com/invopop/jsonschema"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
syftjsonModel "github.com/anchore/syft/syft/formats/syftjson/model"
|
syftjsonModel "github.com/anchore/syft/syft/formats/syftjson/model"
|
||||||
@ -56,7 +56,7 @@ func main() {
|
|||||||
func build() *jsonschema.Schema {
|
func build() *jsonschema.Schema {
|
||||||
reflector := &jsonschema.Reflector{
|
reflector := &jsonschema.Reflector{
|
||||||
AllowAdditionalProperties: true,
|
AllowAdditionalProperties: true,
|
||||||
TypeNamer: func(r reflect.Type) string {
|
Namer: func(r reflect.Type) string {
|
||||||
return strings.TrimPrefix(r.Name(), "JSON")
|
return strings.TrimPrefix(r.Name(), "JSON")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func build() *jsonschema.Schema {
|
|||||||
}
|
}
|
||||||
for _, name := range metadataNames {
|
for _, name := range metadataNames {
|
||||||
metadataTypes = append(metadataTypes, map[string]string{
|
metadataTypes = append(metadataTypes, map[string]string{
|
||||||
"$ref": fmt.Sprintf("#/definitions/%s", name),
|
"$ref": fmt.Sprintf("#/$defs/%s", name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1511
schema/json/schema-5.1.0.json
Normal file
1511
schema/json/schema-5.1.0.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"bomFormat": "CycloneDX",
|
"bomFormat": "CycloneDX",
|
||||||
"specVersion": "1.4",
|
"specVersion": "1.4",
|
||||||
"serialNumber": "urn:uuid:f426926b-4867-4b52-9142-23997f685f2c",
|
"serialNumber": "urn:uuid:cfd602eb-022b-4a61-84d4-50d34612bd84",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2022-10-24T09:54:37-04:00",
|
"timestamp": "2022-11-07T09:11:21-05:00",
|
||||||
"tools": [
|
"tools": [
|
||||||
{
|
{
|
||||||
"vendor": "anchore",
|
"vendor": "anchore",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"bom-ref": "e624319940d8d36a",
|
"bom-ref": "1b1d0be59ac59d2c",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -57,7 +57,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=b8645f4ac2a0891e",
|
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"bomFormat": "CycloneDX",
|
"bomFormat": "CycloneDX",
|
||||||
"specVersion": "1.4",
|
"specVersion": "1.4",
|
||||||
"serialNumber": "urn:uuid:41bbbcc7-694d-4b07-a678-0afb67dabdf9",
|
"serialNumber": "urn:uuid:ced49687-6384-48fa-a930-ef83a258bf77",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2022-10-24T09:54:37-04:00",
|
"timestamp": "2022-11-07T09:11:21-05:00",
|
||||||
"tools": [
|
"tools": [
|
||||||
{
|
{
|
||||||
"vendor": "anchore",
|
"vendor": "anchore",
|
||||||
@ -21,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"bom-ref": "5ffee24fb164cffc",
|
"bom-ref": "66ba429119b8bec6",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=8b16570b2b4155c3",
|
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:19df9583-d8b7-4683-81a6-e57cc8841321" version="1">
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:2939b822-b9cb-489d-8a8b-4431b755031d" version="1">
|
||||||
<metadata>
|
<metadata>
|
||||||
<timestamp>2022-10-24T09:54:54-04:00</timestamp>
|
<timestamp>2022-11-07T09:11:06-05:00</timestamp>
|
||||||
<tools>
|
<tools>
|
||||||
<tool>
|
<tool>
|
||||||
<vendor>anchore</vendor>
|
<vendor>anchore</vendor>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</component>
|
</component>
|
||||||
</metadata>
|
</metadata>
|
||||||
<components>
|
<components>
|
||||||
<component bom-ref="e624319940d8d36a" type="library">
|
<component bom-ref="1b1d0be59ac59d2c" type="library">
|
||||||
<name>package-1</name>
|
<name>package-1</name>
|
||||||
<version>1.0.1</version>
|
<version>1.0.1</version>
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<property name="syft:location:0:path">/some/path/pkg1</property>
|
<property name="syft:location:0:path">/some/path/pkg1</property>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=b8645f4ac2a0891e" type="library">
|
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3" type="library">
|
||||||
<name>package-2</name>
|
<name>package-2</name>
|
||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:5342511c-3580-4cae-b373-20bbf14ba7a3" version="1">
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:2896b5ce-2016-49e8-a422-239d662846c7" version="1">
|
||||||
<metadata>
|
<metadata>
|
||||||
<timestamp>2022-10-24T09:54:54-04:00</timestamp>
|
<timestamp>2022-11-07T09:11:06-05:00</timestamp>
|
||||||
<tools>
|
<tools>
|
||||||
<tool>
|
<tool>
|
||||||
<vendor>anchore</vendor>
|
<vendor>anchore</vendor>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</component>
|
</component>
|
||||||
</metadata>
|
</metadata>
|
||||||
<components>
|
<components>
|
||||||
<component bom-ref="5ffee24fb164cffc" type="library">
|
<component bom-ref="66ba429119b8bec6" type="library">
|
||||||
<name>package-1</name>
|
<name>package-1</name>
|
||||||
<version>1.0.1</version>
|
<version>1.0.1</version>
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<property name="syft:location:0:path">/somefile-1.txt</property>
|
<property name="syft:location:0:path">/somefile-1.txt</property>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=8b16570b2b4155c3" type="library">
|
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4" type="library">
|
||||||
<name>package-2</name>
|
<name>package-2</name>
|
||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"name": "/some/path",
|
"name": "/some/path",
|
||||||
"spdxVersion": "SPDX-2.2",
|
"spdxVersion": "SPDX-2.2",
|
||||||
"creationInfo": {
|
"creationInfo": {
|
||||||
"created": "2022-10-24T13:54:19.225779Z",
|
"created": "2022-11-07T14:11:33.934417Z",
|
||||||
"creators": [
|
"creators": [
|
||||||
"Organization: Anchore, Inc",
|
"Organization: Anchore, Inc",
|
||||||
"Tool: syft-v0.42.0-bogus"
|
"Tool: syft-v0.42.0-bogus"
|
||||||
@ -11,10 +11,10 @@
|
|||||||
"licenseListVersion": "3.18"
|
"licenseListVersion": "3.18"
|
||||||
},
|
},
|
||||||
"dataLicense": "CC0-1.0",
|
"dataLicense": "CC0-1.0",
|
||||||
"documentNamespace": "https://anchore.com/syft/dir/some/path-cd89c782-240b-461e-81a1-63863e02642f",
|
"documentNamespace": "https://anchore.com/syft/dir/some/path-938e09e5-4fcc-4e3a-8833-a8b59e923bdc",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-e624319940d8d36a",
|
"SPDXID": "SPDXRef-1b1d0be59ac59d2c",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"licenseConcluded": "MIT",
|
"licenseConcluded": "MIT",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
"versionInfo": "1.0.1"
|
"versionInfo": "1.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-b8645f4ac2a0891e",
|
"SPDXID": "SPDXRef-db4abfe497c180d3",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"licenseConcluded": "NONE",
|
"licenseConcluded": "NONE",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"name": "user-image-input",
|
"name": "user-image-input",
|
||||||
"spdxVersion": "SPDX-2.2",
|
"spdxVersion": "SPDX-2.2",
|
||||||
"creationInfo": {
|
"creationInfo": {
|
||||||
"created": "2022-10-24T13:54:19.477217Z",
|
"created": "2022-11-07T14:11:33.941498Z",
|
||||||
"creators": [
|
"creators": [
|
||||||
"Organization: Anchore, Inc",
|
"Organization: Anchore, Inc",
|
||||||
"Tool: syft-v0.42.0-bogus"
|
"Tool: syft-v0.42.0-bogus"
|
||||||
@ -11,10 +11,10 @@
|
|||||||
"licenseListVersion": "3.18"
|
"licenseListVersion": "3.18"
|
||||||
},
|
},
|
||||||
"dataLicense": "CC0-1.0",
|
"dataLicense": "CC0-1.0",
|
||||||
"documentNamespace": "https://anchore.com/syft/image/user-image-input-0b40ce75-7e54-4760-bd9d-4fa833b352dd",
|
"documentNamespace": "https://anchore.com/syft/image/user-image-input-797c013c-1d76-4d3e-9391-521152bfc87d",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-5ffee24fb164cffc",
|
"SPDXID": "SPDXRef-66ba429119b8bec6",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"licenseConcluded": "MIT",
|
"licenseConcluded": "MIT",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
"versionInfo": "1.0.1"
|
"versionInfo": "1.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-8b16570b2b4155c3",
|
"SPDXID": "SPDXRef-958443e2d9304af4",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"licenseConcluded": "NONE",
|
"licenseConcluded": "NONE",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"name": "user-image-input",
|
"name": "user-image-input",
|
||||||
"spdxVersion": "SPDX-2.2",
|
"spdxVersion": "SPDX-2.2",
|
||||||
"creationInfo": {
|
"creationInfo": {
|
||||||
"created": "2022-10-24T13:54:19.48428Z",
|
"created": "2022-11-07T14:11:33.947742Z",
|
||||||
"creators": [
|
"creators": [
|
||||||
"Organization: Anchore, Inc",
|
"Organization: Anchore, Inc",
|
||||||
"Tool: syft-v0.42.0-bogus"
|
"Tool: syft-v0.42.0-bogus"
|
||||||
@ -11,10 +11,10 @@
|
|||||||
"licenseListVersion": "3.18"
|
"licenseListVersion": "3.18"
|
||||||
},
|
},
|
||||||
"dataLicense": "CC0-1.0",
|
"dataLicense": "CC0-1.0",
|
||||||
"documentNamespace": "https://anchore.com/syft/image/user-image-input-1a4dc179-1222-463c-b4e9-619131af7e97",
|
"documentNamespace": "https://anchore.com/syft/image/user-image-input-30117c3a-546f-45b7-a1a8-91f2ecd8d2aa",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-5ffee24fb164cffc",
|
"SPDXID": "SPDXRef-66ba429119b8bec6",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"licenseConcluded": "MIT",
|
"licenseConcluded": "MIT",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -44,7 +44,7 @@
|
|||||||
"versionInfo": "1.0.1"
|
"versionInfo": "1.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SPDXID": "SPDXRef-8b16570b2b4155c3",
|
"SPDXID": "SPDXRef-958443e2d9304af4",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"licenseConcluded": "NONE",
|
"licenseConcluded": "NONE",
|
||||||
"downloadLocation": "NOASSERTION",
|
"downloadLocation": "NOASSERTION",
|
||||||
@ -118,32 +118,32 @@
|
|||||||
],
|
],
|
||||||
"relationships": [
|
"relationships": [
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c"
|
"relatedSpdxElement": "SPDXRef-5265a4dde3edbf7c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-839d99ee67d9d174"
|
"relatedSpdxElement": "SPDXRef-839d99ee67d9d174"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-9c2f7510199b17f6"
|
"relatedSpdxElement": "SPDXRef-9c2f7510199b17f6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-c641caa71518099f"
|
"relatedSpdxElement": "SPDXRef-c641caa71518099f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-c6f5b29dca12661f"
|
"relatedSpdxElement": "SPDXRef-c6f5b29dca12661f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"spdxElementId": "SPDXRef-5ffee24fb164cffc",
|
"spdxElementId": "SPDXRef-66ba429119b8bec6",
|
||||||
"relationshipType": "CONTAINS",
|
"relationshipType": "CONTAINS",
|
||||||
"relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd"
|
"relatedSpdxElement": "SPDXRef-f9e49132a4b96ccd"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2
|
|||||||
DataLicense: CC0-1.0
|
DataLicense: CC0-1.0
|
||||||
SPDXID: SPDXRef-DOCUMENT
|
SPDXID: SPDXRef-DOCUMENT
|
||||||
DocumentName: .
|
DocumentName: .
|
||||||
DocumentNamespace: https://anchore.com/syft/dir/4593d944-756e-49aa-af4e-b1a5acf09b97
|
DocumentNamespace: https://anchore.com/syft/dir/a4820ad7-d106-497f-bda7-e694e9ad1050
|
||||||
LicenseListVersion: 3.18
|
LicenseListVersion: 3.18
|
||||||
Creator: Organization: Anchore, Inc
|
Creator: Organization: Anchore, Inc
|
||||||
Creator: Tool: syft-v0.42.0-bogus
|
Creator: Tool: syft-v0.42.0-bogus
|
||||||
Created: 2022-10-24T13:53:53Z
|
Created: 2022-11-07T14:11:42Z
|
||||||
|
|
||||||
##### Package: @at-sign
|
##### Package: @at-sign
|
||||||
|
|
||||||
PackageName: @at-sign
|
PackageName: @at-sign
|
||||||
SPDXID: SPDXRef-Package---at-sign-fe69bc18c2698fc4
|
SPDXID: SPDXRef-Package---at-sign-3732f7a5679bdec4
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
PackageLicenseConcluded: NONE
|
PackageLicenseConcluded: NONE
|
||||||
@ -21,7 +21,7 @@ PackageCopyrightText: NOASSERTION
|
|||||||
##### Package: some/slashes
|
##### Package: some/slashes
|
||||||
|
|
||||||
PackageName: some/slashes
|
PackageName: some/slashes
|
||||||
SPDXID: SPDXRef-Package--some-slashes-57ed206c09e6e5f4
|
SPDXID: SPDXRef-Package--some-slashes-1345166d4801153b
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
PackageLicenseConcluded: NONE
|
PackageLicenseConcluded: NONE
|
||||||
@ -31,7 +31,7 @@ PackageCopyrightText: NOASSERTION
|
|||||||
##### Package: under_scores
|
##### Package: under_scores
|
||||||
|
|
||||||
PackageName: under_scores
|
PackageName: under_scores
|
||||||
SPDXID: SPDXRef-Package--under-scores-8b7505907fdaf19d
|
SPDXID: SPDXRef-Package--under-scores-290d5c77210978c1
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
PackageLicenseConcluded: NONE
|
PackageLicenseConcluded: NONE
|
||||||
|
|||||||
@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2
|
|||||||
DataLicense: CC0-1.0
|
DataLicense: CC0-1.0
|
||||||
SPDXID: SPDXRef-DOCUMENT
|
SPDXID: SPDXRef-DOCUMENT
|
||||||
DocumentName: /some/path
|
DocumentName: /some/path
|
||||||
DocumentNamespace: https://anchore.com/syft/dir/some/path-a4e58523-00d0-4135-9d21-cf586fbd340c
|
DocumentNamespace: https://anchore.com/syft/dir/some/path-01844fe2-60ea-45fd-bb92-df9f5660330d
|
||||||
LicenseListVersion: 3.18
|
LicenseListVersion: 3.18
|
||||||
Creator: Organization: Anchore, Inc
|
Creator: Organization: Anchore, Inc
|
||||||
Creator: Tool: syft-v0.42.0-bogus
|
Creator: Tool: syft-v0.42.0-bogus
|
||||||
Created: 2022-10-24T13:53:52Z
|
Created: 2022-11-07T14:11:42Z
|
||||||
|
|
||||||
##### Package: package-2
|
##### Package: package-2
|
||||||
|
|
||||||
PackageName: package-2
|
PackageName: package-2
|
||||||
SPDXID: SPDXRef-Package-deb-package-2-b8645f4ac2a0891e
|
SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3
|
||||||
PackageVersion: 2.0.1
|
PackageVersion: 2.0.1
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
@ -24,7 +24,7 @@ ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
|
|||||||
##### Package: package-1
|
##### Package: package-1
|
||||||
|
|
||||||
PackageName: package-1
|
PackageName: package-1
|
||||||
SPDXID: SPDXRef-Package-python-package-1-e624319940d8d36a
|
SPDXID: SPDXRef-Package-python-package-1-1b1d0be59ac59d2c
|
||||||
PackageVersion: 1.0.1
|
PackageVersion: 1.0.1
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
|
|||||||
@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2
|
|||||||
DataLicense: CC0-1.0
|
DataLicense: CC0-1.0
|
||||||
SPDXID: SPDXRef-DOCUMENT
|
SPDXID: SPDXRef-DOCUMENT
|
||||||
DocumentName: user-image-input
|
DocumentName: user-image-input
|
||||||
DocumentNamespace: https://anchore.com/syft/image/user-image-input-639f628a-5f8b-4050-a69e-90c85f0d7837
|
DocumentNamespace: https://anchore.com/syft/image/user-image-input-32ec506e-82d8-4da6-991d-e1000e4e562e
|
||||||
LicenseListVersion: 3.18
|
LicenseListVersion: 3.18
|
||||||
Creator: Organization: Anchore, Inc
|
Creator: Organization: Anchore, Inc
|
||||||
Creator: Tool: syft-v0.42.0-bogus
|
Creator: Tool: syft-v0.42.0-bogus
|
||||||
Created: 2022-10-24T13:53:53Z
|
Created: 2022-11-07T14:11:42Z
|
||||||
|
|
||||||
##### Package: package-2
|
##### Package: package-2
|
||||||
|
|
||||||
PackageName: package-2
|
PackageName: package-2
|
||||||
SPDXID: SPDXRef-Package-deb-package-2-8b16570b2b4155c3
|
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
|
||||||
PackageVersion: 2.0.1
|
PackageVersion: 2.0.1
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
@ -24,7 +24,7 @@ ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
|
|||||||
##### Package: package-1
|
##### Package: package-1
|
||||||
|
|
||||||
PackageName: package-1
|
PackageName: package-1
|
||||||
SPDXID: SPDXRef-Package-python-package-1-5ffee24fb164cffc
|
SPDXID: SPDXRef-Package-python-package-1-66ba429119b8bec6
|
||||||
PackageVersion: 1.0.1
|
PackageVersion: 1.0.1
|
||||||
PackageDownloadLocation: NOASSERTION
|
PackageDownloadLocation: NOASSERTION
|
||||||
FilesAnalyzed: false
|
FilesAnalyzed: false
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "e624319940d8d36a",
|
"id": "1b1d0be59ac59d2c",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "b8645f4ac2a0891e",
|
"id": "db4abfe497c180d3",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
@ -89,7 +89,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "8373dcf05581b932",
|
"id": "304a5a8e5958a49d",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c3d4da40f387eec7",
|
"id": "9fd0b9f41034991d",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
@ -185,7 +185,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"id": "5ffee24fb164cffc",
|
"id": "66ba429119b8bec6",
|
||||||
"name": "package-1",
|
"name": "package-1",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
@ -32,7 +32,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8b16570b2b4155c3",
|
"id": "958443e2d9304af4",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"type": "deb",
|
"type": "deb",
|
||||||
@ -112,7 +112,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,11 +67,6 @@ func Catalog(resolver source.FileResolver, release *linux.Release, catalogers ..
|
|||||||
// we might have binary classified CPE already with the package so we want to append here
|
// we might have binary classified CPE already with the package so we want to append here
|
||||||
p.CPEs = append(p.CPEs, cpe.Generate(p)...)
|
p.CPEs = append(p.CPEs, cpe.Generate(p)...)
|
||||||
|
|
||||||
// generate PURL (note: this is excluded from package ID, so is safe to mutate)
|
|
||||||
if p.PURL == "" {
|
|
||||||
p.PURL = pkg.URL(p, release)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we were not able to identify the language we have an opportunity
|
// if we were not able to identify the language we have an opportunity
|
||||||
// to try and get this value from the PURL. Worst case we assert that
|
// to try and get this value from the PURL. Worst case we assert that
|
||||||
// we could not identify the language at either stage and set UnknownLanguage
|
// we could not identify the language at either stage and set UnknownLanguage
|
||||||
|
|||||||
@ -181,6 +181,10 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GroupIDsFromJavaMetadata(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupIDsFromJavaMetadata(metadata pkg.JavaMetadata) (groupIDs []string) {
|
||||||
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
||||||
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
||||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...)
|
groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...)
|
||||||
|
|||||||
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
Package common provides generic utilities used by multiple catalogers.
|
|
||||||
*/
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenericCataloger implements the Catalog interface and is responsible for dispatching the proper parser function for
|
|
||||||
// a given path or glob pattern. This is intended to be reusable across many package cataloger types.
|
|
||||||
type GenericCataloger struct {
|
|
||||||
globParsers map[string]ParserFn
|
|
||||||
pathParsers map[string]ParserFn
|
|
||||||
postProcessors []PostProcessFunc
|
|
||||||
upstreamCataloger string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PostProcessFunc func(resolver source.FileResolver, location source.Location, p *pkg.Package) error
|
|
||||||
|
|
||||||
// NewGenericCataloger if provided path-to-parser-function and glob-to-parser-function lookups creates a GenericCataloger
|
|
||||||
func NewGenericCataloger(pathParsers map[string]ParserFn, globParsers map[string]ParserFn, upstreamCataloger string, postProcessors ...PostProcessFunc) *GenericCataloger {
|
|
||||||
return &GenericCataloger{
|
|
||||||
globParsers: globParsers,
|
|
||||||
pathParsers: pathParsers,
|
|
||||||
postProcessors: postProcessors,
|
|
||||||
upstreamCataloger: upstreamCataloger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns a string that uniquely describes the upstream cataloger that this Generic Cataloger represents.
|
|
||||||
func (c *GenericCataloger) Name() string {
|
|
||||||
return c.upstreamCataloger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing the catalog source.
|
|
||||||
func (c *GenericCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
|
||||||
var packages []pkg.Package
|
|
||||||
var relationships []artifact.Relationship
|
|
||||||
|
|
||||||
for location, parser := range c.selectFiles(resolver) {
|
|
||||||
contentReader, err := resolver.FileContentsByLocation(location)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: fail or log?
|
|
||||||
return nil, nil, fmt.Errorf("unable to fetch contents at location=%v: %w", location, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
discoveredPackages, discoveredRelationships, err := parser(location.RealPath, contentReader)
|
|
||||||
internal.CloseAndLogError(contentReader, location.VirtualPath)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: should we fail? or only log?
|
|
||||||
log.Warnf("cataloger '%s' failed to parse entries at location=%+v: %+v", c.upstreamCataloger, location, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgsForRemoval := make(map[artifact.ID]struct{})
|
|
||||||
var cleanedRelationships []artifact.Relationship
|
|
||||||
for _, p := range discoveredPackages {
|
|
||||||
p.FoundBy = c.upstreamCataloger
|
|
||||||
p.Locations.Add(location)
|
|
||||||
p.SetID()
|
|
||||||
// doing it here so all packages have an ID,
|
|
||||||
// IDs are later used to remove relationships
|
|
||||||
if !pkg.IsValid(p) {
|
|
||||||
pkgsForRemoval[p.ID()] = struct{}{}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, postProcess := range c.postProcessors {
|
|
||||||
err = postProcess(resolver, location, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packages = append(packages, *p)
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanedRelationships = removeRelationshipsWithArtifactIDs(pkgsForRemoval, discoveredRelationships)
|
|
||||||
relationships = append(relationships, cleanedRelationships...)
|
|
||||||
}
|
|
||||||
return packages, relationships, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeRelationshipsWithArtifactIDs(artifactsToExclude map[artifact.ID]struct{}, relationships []artifact.Relationship) []artifact.Relationship {
|
|
||||||
if len(artifactsToExclude) == 0 || len(relationships) == 0 {
|
|
||||||
// no removal to do
|
|
||||||
return relationships
|
|
||||||
}
|
|
||||||
|
|
||||||
var cleanedRelationships []artifact.Relationship
|
|
||||||
for _, r := range relationships {
|
|
||||||
_, removeTo := artifactsToExclude[r.To.ID()]
|
|
||||||
_, removaFrom := artifactsToExclude[r.From.ID()]
|
|
||||||
if !removeTo && !removaFrom {
|
|
||||||
cleanedRelationships = append(cleanedRelationships, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cleanedRelationships
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
|
|
||||||
func (c *GenericCataloger) selectFiles(resolver source.FilePathResolver) map[source.Location]ParserFn {
|
|
||||||
var parserByLocation = make(map[source.Location]ParserFn)
|
|
||||||
|
|
||||||
// select by exact path
|
|
||||||
for path, parser := range c.pathParsers {
|
|
||||||
files, err := resolver.FilesByPath(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("cataloger failed to select files by path: %+v", err)
|
|
||||||
}
|
|
||||||
for _, f := range files {
|
|
||||||
parserByLocation[f] = parser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// select by glob pattern
|
|
||||||
for globPattern, parser := range c.globParsers {
|
|
||||||
fileMatches, err := resolver.FilesByGlob(globPattern)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to find files by glob: %s", globPattern)
|
|
||||||
}
|
|
||||||
for _, f := range fileMatches {
|
|
||||||
parserByLocation[f] = parser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parserByLocation
|
|
||||||
}
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGenericCataloger(t *testing.T) {
|
|
||||||
allParsedPathes := make(map[string]bool)
|
|
||||||
parser := func(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
|
||||||
allParsedPathes[path] = true
|
|
||||||
contents, err := ioutil.ReadAll(reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
p := &pkg.Package{Name: string(contents)}
|
|
||||||
r := artifact.Relationship{From: p, To: p,
|
|
||||||
Type: artifact.ContainsRelationship,
|
|
||||||
}
|
|
||||||
|
|
||||||
return []*pkg.Package{p}, []artifact.Relationship{r}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
globParsers := map[string]ParserFn{
|
|
||||||
"**/a-path.txt": parser,
|
|
||||||
"**/empty.txt": parser,
|
|
||||||
}
|
|
||||||
pathParsers := map[string]ParserFn{
|
|
||||||
"test-fixtures/another-path.txt": parser,
|
|
||||||
"test-fixtures/last/path.txt": parser,
|
|
||||||
}
|
|
||||||
upstream := "some-other-cataloger"
|
|
||||||
|
|
||||||
expectedSelection := []string{"test-fixtures/last/path.txt", "test-fixtures/another-path.txt", "test-fixtures/a-path.txt", "test-fixtures/empty.txt"}
|
|
||||||
resolver := source.NewMockResolverForPaths(expectedSelection...)
|
|
||||||
cataloger := NewGenericCataloger(pathParsers, globParsers, upstream)
|
|
||||||
|
|
||||||
actualPkgs, relationships, err := cataloger.Catalog(resolver)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
expectedPkgs := make(map[string]pkg.Package)
|
|
||||||
for _, path := range expectedSelection {
|
|
||||||
require.True(t, allParsedPathes[path])
|
|
||||||
expectedPkgs[path] = pkg.Package{
|
|
||||||
FoundBy: upstream,
|
|
||||||
Name: fmt.Sprintf("%s file contents!", path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Len(t, allParsedPathes, len(expectedSelection))
|
|
||||||
// empty.txt won't become a package
|
|
||||||
assert.Len(t, actualPkgs, len(expectedPkgs)-1)
|
|
||||||
// right now, a relationship is created for each package, but if the relationship includes an invalid package it should be dropped.
|
|
||||||
assert.Len(t, relationships, len(actualPkgs))
|
|
||||||
|
|
||||||
for _, p := range actualPkgs {
|
|
||||||
ref := p.Locations.ToSlice()[0]
|
|
||||||
exP, ok := expectedPkgs[ref.RealPath]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("missing expected pkg: ref=%+v", ref)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.FoundBy != exP.FoundBy {
|
|
||||||
t.Errorf("bad upstream: %s", p.FoundBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
if exP.Name != p.Name {
|
|
||||||
t.Errorf("bad contents mapping: %+v", p.Locations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_removeRelationshipsWithArtifactIDs(t *testing.T) {
|
|
||||||
one := &pkg.Package{Name: "one", Version: "1.0"}
|
|
||||||
two := &pkg.Package{Name: "two", Version: "1.0"}
|
|
||||||
three := &pkg.Package{Name: "three", Version: "1.0"}
|
|
||||||
four := &pkg.Package{Name: "four", Version: "bla"}
|
|
||||||
five := &pkg.Package{Name: "five", Version: "1.0"}
|
|
||||||
|
|
||||||
pkgs := make([]artifact.Identifiable, 0)
|
|
||||||
for _, p := range []*pkg.Package{one, two, three, four, five} {
|
|
||||||
// IDs are necessary for comparison
|
|
||||||
p.SetID()
|
|
||||||
pkgs = append(pkgs, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
remove map[artifact.ID]struct{}
|
|
||||||
relationships []artifact.Relationship
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []artifact.Relationship
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "nothing-to-remove",
|
|
||||||
args: args{
|
|
||||||
relationships: []artifact.Relationship{
|
|
||||||
{From: one, To: two},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []artifact.Relationship{
|
|
||||||
{From: one, To: two},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "remove-all-relationships",
|
|
||||||
args: args{
|
|
||||||
remove: map[artifact.ID]struct{}{
|
|
||||||
one.ID(): {},
|
|
||||||
three.ID(): {},
|
|
||||||
},
|
|
||||||
relationships: []artifact.Relationship{
|
|
||||||
{From: one, To: two},
|
|
||||||
{From: two, To: three},
|
|
||||||
{From: three, To: four},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []artifact.Relationship(nil),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "remove-half-of-relationships",
|
|
||||||
args: args{
|
|
||||||
remove: map[artifact.ID]struct{}{
|
|
||||||
one.ID(): {},
|
|
||||||
},
|
|
||||||
relationships: []artifact.Relationship{
|
|
||||||
{From: one, To: two},
|
|
||||||
{From: one, To: three},
|
|
||||||
{From: two, To: three},
|
|
||||||
{From: three, To: four},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []artifact.Relationship{
|
|
||||||
{From: two, To: three},
|
|
||||||
{From: three, To: four},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "remove-repeated-relationships",
|
|
||||||
args: args{
|
|
||||||
remove: map[artifact.ID]struct{}{
|
|
||||||
one.ID(): {},
|
|
||||||
two.ID(): {},
|
|
||||||
},
|
|
||||||
relationships: []artifact.Relationship{
|
|
||||||
{From: one, To: two},
|
|
||||||
{From: one, To: three},
|
|
||||||
{From: two, To: three},
|
|
||||||
{From: two, To: three},
|
|
||||||
{From: three, To: four},
|
|
||||||
{From: four, To: five},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: []artifact.Relationship{
|
|
||||||
{From: three, To: four},
|
|
||||||
{From: four, To: five},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
assert.Equalf(t, tt.want, removeRelationshipsWithArtifactIDs(tt.args.remove, tt.args.relationships), "removeRelationshipsWithArtifactIDs(%v, %v)", tt.args.remove, tt.args.relationships)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParserFn standardizes a function signature for parser functions that accept the virtual file path (not usable for file reads) and contents and return any discovered packages from that file
|
|
||||||
type ParserFn func(string, io.Reader) ([]*pkg.Package, []artifact.Relationship, error)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
test-fixtures/a-path.txt file contents!
|
|
||||||
@ -1 +0,0 @@
|
|||||||
test-fixtures/another-path.txt file contents!
|
|
||||||
@ -1 +0,0 @@
|
|||||||
test-fixtures/last/path.txt file contents!
|
|
||||||
@ -33,13 +33,15 @@ type CatalogTester struct {
|
|||||||
|
|
||||||
func NewCatalogTester() *CatalogTester {
|
func NewCatalogTester() *CatalogTester {
|
||||||
return &CatalogTester{
|
return &CatalogTester{
|
||||||
wantErr: require.NoError,
|
wantErr: require.NoError,
|
||||||
locationComparer: func(x, y source.Location) bool {
|
locationComparer: DefaultLocationComparer,
|
||||||
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DefaultLocationComparer(x, y source.Location) bool {
|
||||||
|
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester {
|
func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
@ -147,6 +149,7 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi
|
|||||||
|
|
||||||
p.compareOptions = append(p.compareOptions,
|
p.compareOptions = append(p.compareOptions,
|
||||||
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
||||||
|
cmpopts.SortSlices(pkg.Less),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
func(x, y source.LocationSet) bool {
|
func(x, y source.LocationSet) bool {
|
||||||
xs := x.ToSlice()
|
xs := x.ToSlice()
|
||||||
@ -186,3 +189,32 @@ func TestFileParserWithEnv(t *testing.T, fixturePath string, parser generic.Pars
|
|||||||
|
|
||||||
NewCatalogTester().FromFile(t, fixturePath).WithEnv(env).Expects(expectedPkgs, expectedRelationships).TestParser(t, parser)
|
NewCatalogTester().FromFile(t, fixturePath).WithEnv(env).Expects(expectedPkgs, expectedRelationships).TestParser(t, parser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
|
t.Helper()
|
||||||
|
opts := []cmp.Option{
|
||||||
|
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y source.LocationSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !DefaultLocationComparer(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(a, b, opts...); diff != "" {
|
||||||
|
t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package java
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@ -13,11 +12,11 @@ import (
|
|||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
syftFile "github.com/anchore/syft/syft/file"
|
syftFile "github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
// integrity check
|
var _ generic.Parser = parseJavaArchive
|
||||||
var _ common.ParserFn = parseJavaArchive
|
|
||||||
|
|
||||||
var archiveFormatGlobs = []string{
|
var archiveFormatGlobs = []string{
|
||||||
"**/*.jar",
|
"**/*.jar",
|
||||||
@ -44,7 +43,7 @@ var javaArchiveHashes = []crypto.Hash{
|
|||||||
|
|
||||||
type archiveParser struct {
|
type archiveParser struct {
|
||||||
fileManifest file.ZipFileManifest
|
fileManifest file.ZipFileManifest
|
||||||
virtualPath string
|
location source.Location
|
||||||
archivePath string
|
archivePath string
|
||||||
contentPath string
|
contentPath string
|
||||||
fileInfo archiveFilename
|
fileInfo archiveFilename
|
||||||
@ -52,8 +51,8 @@ type archiveParser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseJavaArchive is a parser function for java archive contents, returning all Java libraries and nested archives.
|
// parseJavaArchive is a parser function for java archive contents, returning all Java libraries and nested archives.
|
||||||
func parseJavaArchive(virtualPath string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
func parseJavaArchive(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
parser, cleanupFn, err := newJavaArchiveParser(virtualPath, reader, true)
|
parser, cleanupFn, err := newJavaArchiveParser(reader, true)
|
||||||
// note: even on error, we should always run cleanup functions
|
// note: even on error, we should always run cleanup functions
|
||||||
defer cleanupFn()
|
defer cleanupFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -72,9 +71,9 @@ func uniquePkgKey(p *pkg.Package) string {
|
|||||||
|
|
||||||
// newJavaArchiveParser returns a new java archive parser object for the given archive. Can be configured to discover
|
// newJavaArchiveParser returns a new java archive parser object for the given archive. Can be configured to discover
|
||||||
// and parse nested archives or ignore them.
|
// and parse nested archives or ignore them.
|
||||||
func newJavaArchiveParser(virtualPath string, reader io.Reader, detectNested bool) (*archiveParser, func(), error) {
|
func newJavaArchiveParser(reader source.LocationReadCloser, detectNested bool) (*archiveParser, func(), error) {
|
||||||
// fetch the last element of the virtual path
|
// fetch the last element of the virtual path
|
||||||
virtualElements := strings.Split(virtualPath, ":")
|
virtualElements := strings.Split(reader.AccessPath(), ":")
|
||||||
currentFilepath := virtualElements[len(virtualElements)-1]
|
currentFilepath := virtualElements[len(virtualElements)-1]
|
||||||
|
|
||||||
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(currentFilepath, reader)
|
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(currentFilepath, reader)
|
||||||
@ -89,7 +88,7 @@ func newJavaArchiveParser(virtualPath string, reader io.Reader, detectNested boo
|
|||||||
|
|
||||||
return &archiveParser{
|
return &archiveParser{
|
||||||
fileManifest: fileManifest,
|
fileManifest: fileManifest,
|
||||||
virtualPath: virtualPath,
|
location: reader.Location,
|
||||||
archivePath: archivePath,
|
archivePath: archivePath,
|
||||||
contentPath: contentPath,
|
contentPath: contentPath,
|
||||||
fileInfo: newJavaArchiveFilename(currentFilepath),
|
fileInfo: newJavaArchiveFilename(currentFilepath),
|
||||||
@ -98,14 +97,14 @@ func newJavaArchiveParser(virtualPath string, reader io.Reader, detectNested boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse the loaded archive and return all packages found.
|
// parse the loaded archive and return all packages found.
|
||||||
func (j *archiveParser) parse() ([]*pkg.Package, []artifact.Relationship, error) {
|
func (j *archiveParser) parse() ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
var pkgs []*pkg.Package
|
var pkgs []pkg.Package
|
||||||
var relationships []artifact.Relationship
|
var relationships []artifact.Relationship
|
||||||
|
|
||||||
// find the parent package from the java manifest
|
// find the parent package from the java manifest
|
||||||
parentPkg, err := j.discoverMainPackage()
|
parentPkg, err := j.discoverMainPackage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not generate package from %s: %w", j.virtualPath, err)
|
return nil, nil, fmt.Errorf("could not generate package from %s: %w", j.location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// find aux packages from pom.properties/pom.xml and potentially modify the existing parentPkg
|
// find aux packages from pom.properties/pom.xml and potentially modify the existing parentPkg
|
||||||
@ -128,14 +127,14 @@ func (j *archiveParser) parse() ([]*pkg.Package, []artifact.Relationship, error)
|
|||||||
|
|
||||||
// lastly, add the parent package to the list (assuming the parent exists)
|
// lastly, add the parent package to the list (assuming the parent exists)
|
||||||
if parentPkg != nil {
|
if parentPkg != nil {
|
||||||
pkgs = append([]*pkg.Package{parentPkg}, pkgs...)
|
pkgs = append([]pkg.Package{*parentPkg}, pkgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add pURLs to all packages found
|
// add pURLs to all packages found
|
||||||
// note: since package information may change after initial creation when parsing multiple locations within the
|
// note: since package information may change after initial creation when parsing multiple locations within the
|
||||||
// jar, we wait until the conclusion of the parsing process before synthesizing pURLs.
|
// jar, we wait until the conclusion of the parsing process before synthesizing pURLs.
|
||||||
for _, p := range pkgs {
|
for i, p := range pkgs {
|
||||||
addPURL(p)
|
pkgs[i].PURL = packageURL(p.Name, p.Version, p.Metadata.(pkg.JavaMetadata))
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkgs, relationships, nil
|
return pkgs, relationships, nil
|
||||||
@ -156,14 +155,14 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||||||
// fetch the manifest file
|
// fetch the manifest file
|
||||||
contents, err := file.ContentsFromZip(j.archivePath, manifestMatches...)
|
contents, err := file.ContentsFromZip(j.archivePath, manifestMatches...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to extract java manifests (%s): %w", j.virtualPath, err)
|
return nil, fmt.Errorf("unable to extract java manifests (%s): %w", j.location, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the manifest file into a rich object
|
// parse the manifest file into a rich object
|
||||||
manifestContents := contents[manifestMatches[0]]
|
manifestContents := contents[manifestMatches[0]]
|
||||||
manifest, err := parseJavaManifest(j.archivePath, strings.NewReader(manifestContents))
|
manifest, err := parseJavaManifest(j.archivePath, strings.NewReader(manifestContents))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to parse java manifest (%s): %+v", j.virtualPath, err)
|
log.Warnf("failed to parse java manifest (%s): %+v", j.location, err)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,10 +182,11 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||||||
Name: selectName(manifest, j.fileInfo),
|
Name: selectName(manifest, j.fileInfo),
|
||||||
Version: selectVersion(manifest, j.fileInfo),
|
Version: selectVersion(manifest, j.fileInfo),
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
|
Locations: source.NewLocationSet(j.location),
|
||||||
Type: j.fileInfo.pkgType(),
|
Type: j.fileInfo.pkgType(),
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
VirtualPath: j.virtualPath,
|
VirtualPath: j.location.AccessPath(),
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
ArchiveDigests: digests,
|
ArchiveDigests: digests,
|
||||||
},
|
},
|
||||||
@ -197,21 +197,21 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
|||||||
// parent package, returning all listed Java packages found for each pom
|
// parent package, returning all listed Java packages found for each pom
|
||||||
// properties discovered and potentially updating the given parentPkg with new
|
// properties discovered and potentially updating the given parentPkg with new
|
||||||
// data.
|
// data.
|
||||||
func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([]*pkg.Package, error) {
|
func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([]pkg.Package, error) {
|
||||||
if parentPkg == nil {
|
if parentPkg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkgs []*pkg.Package
|
var pkgs []pkg.Package
|
||||||
|
|
||||||
// pom.properties
|
// pom.properties
|
||||||
properties, err := pomPropertiesByParentPath(j.archivePath, j.virtualPath, j.fileManifest.GlobMatch(pomPropertiesGlob))
|
properties, err := pomPropertiesByParentPath(j.archivePath, j.location, j.fileManifest.GlobMatch(pomPropertiesGlob))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// pom.xml
|
// pom.xml
|
||||||
projects, err := pomProjectByParentPath(j.archivePath, j.virtualPath, j.fileManifest.GlobMatch(pomXMLGlob))
|
projects, err := pomProjectByParentPath(j.archivePath, j.location, j.fileManifest.GlobMatch(pomXMLGlob))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -222,41 +222,41 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
|
|||||||
pomProject = &proj
|
pomProject = &proj
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgFromPom := newPackageFromMavenData(propertiesObj, pomProject, parentPkg, j.virtualPath)
|
pkgFromPom := newPackageFromMavenData(propertiesObj, pomProject, parentPkg, j.location)
|
||||||
if pkgFromPom != nil {
|
if pkgFromPom != nil {
|
||||||
pkgs = append(pkgs, pkgFromPom)
|
pkgs = append(pkgs, *pkgFromPom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkgs, nil
|
return pkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]*pkg.Package, []artifact.Relationship, error) {
|
func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
// we know that all java archives are zip formatted files, so we can use the shared zip helper
|
// we know that all java archives are zip formatted files, so we can use the shared zip helper
|
||||||
return discoverPkgsFromZip(j.virtualPath, j.archivePath, j.contentPath, j.fileManifest, parentPkg)
|
return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// discoverPkgsFromZip finds Java archives within Java archives, returning all listed Java packages found and
|
// discoverPkgsFromZip finds Java archives within Java archives, returning all listed Java packages found and
|
||||||
// associating each discovered package to the given parent package.
|
// associating each discovered package to the given parent package.
|
||||||
func discoverPkgsFromZip(virtualPath, archivePath, contentPath string, fileManifest file.ZipFileManifest, parentPkg *pkg.Package) ([]*pkg.Package, []artifact.Relationship, error) {
|
func discoverPkgsFromZip(location source.Location, archivePath, contentPath string, fileManifest file.ZipFileManifest, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
// search and parse pom.properties files & fetch the contents
|
// search and parse pom.properties files & fetch the contents
|
||||||
openers, err := file.ExtractFromZipToUniqueTempFile(archivePath, contentPath, fileManifest.GlobMatch(archiveFormatGlobs...)...)
|
openers, err := file.ExtractFromZipToUniqueTempFile(archivePath, contentPath, fileManifest.GlobMatch(archiveFormatGlobs...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to extract files from zip: %w", err)
|
return nil, nil, fmt.Errorf("unable to extract files from zip: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return discoverPkgsFromOpeners(virtualPath, openers, parentPkg)
|
return discoverPkgsFromOpeners(location, openers, parentPkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// discoverPkgsFromOpeners finds Java archives within the given files and associates them with the given parent package.
|
// discoverPkgsFromOpeners finds Java archives within the given files and associates them with the given parent package.
|
||||||
func discoverPkgsFromOpeners(virtualPath string, openers map[string]file.Opener, parentPkg *pkg.Package) ([]*pkg.Package, []artifact.Relationship, error) {
|
func discoverPkgsFromOpeners(location source.Location, openers map[string]file.Opener, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
var pkgs []*pkg.Package
|
var pkgs []pkg.Package
|
||||||
var relationships []artifact.Relationship
|
var relationships []artifact.Relationship
|
||||||
|
|
||||||
for pathWithinArchive, archiveOpener := range openers {
|
for pathWithinArchive, archiveOpener := range openers {
|
||||||
nestedPkgs, nestedRelationships, err := discoverPkgsFromOpener(virtualPath, pathWithinArchive, archiveOpener)
|
nestedPkgs, nestedRelationships, err := discoverPkgsFromOpener(location, pathWithinArchive, archiveOpener)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to discover java packages from opener (%s): %+v", virtualPath, err)
|
log.WithFields("location", location.AccessPath()).Warnf("unable to discover java packages from opener: %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ func discoverPkgsFromOpeners(virtualPath string, openers map[string]file.Opener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// discoverPkgsFromOpener finds Java archives within the given file.
|
// discoverPkgsFromOpener finds Java archives within the given file.
|
||||||
func discoverPkgsFromOpener(virtualPath, pathWithinArchive string, archiveOpener file.Opener) ([]*pkg.Package, []artifact.Relationship, error) {
|
func discoverPkgsFromOpener(location source.Location, pathWithinArchive string, archiveOpener file.Opener) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
archiveReadCloser, err := archiveOpener.Open()
|
archiveReadCloser, err := archiveOpener.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to open archived file from tempdir: %w", err)
|
return nil, nil, fmt.Errorf("unable to open archived file from tempdir: %w", err)
|
||||||
@ -289,8 +289,13 @@ func discoverPkgsFromOpener(virtualPath, pathWithinArchive string, archiveOpener
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
nestedPath := fmt.Sprintf("%s:%s", virtualPath, pathWithinArchive)
|
nestedPath := fmt.Sprintf("%s:%s", location.AccessPath(), pathWithinArchive)
|
||||||
nestedPkgs, nestedRelationships, err := parseJavaArchive(nestedPath, archiveReadCloser)
|
nestedLocation := source.NewLocationFromCoordinates(location.Coordinates)
|
||||||
|
nestedLocation.VirtualPath = nestedPath
|
||||||
|
nestedPkgs, nestedRelationships, err := parseJavaArchive(nil, nil, source.LocationReadCloser{
|
||||||
|
Location: nestedLocation,
|
||||||
|
ReadCloser: archiveReadCloser,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to process nested java archive (%s): %w", pathWithinArchive, err)
|
return nil, nil, fmt.Errorf("unable to process nested java archive (%s): %w", pathWithinArchive, err)
|
||||||
}
|
}
|
||||||
@ -298,7 +303,7 @@ func discoverPkgsFromOpener(virtualPath, pathWithinArchive string, archiveOpener
|
|||||||
return nestedPkgs, nestedRelationships, nil
|
return nestedPkgs, nestedRelationships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pomPropertiesByParentPath(archivePath, virtualPath string, extractPaths []string) (map[string]pkg.PomProperties, error) {
|
func pomPropertiesByParentPath(archivePath string, location source.Location, extractPaths []string) (map[string]pkg.PomProperties, error) {
|
||||||
contentsOfMavenPropertiesFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
contentsOfMavenPropertiesFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
||||||
@ -308,7 +313,7 @@ func pomPropertiesByParentPath(archivePath, virtualPath string, extractPaths []s
|
|||||||
for filePath, fileContents := range contentsOfMavenPropertiesFiles {
|
for filePath, fileContents := range contentsOfMavenPropertiesFiles {
|
||||||
pomProperties, err := parsePomProperties(filePath, strings.NewReader(fileContents))
|
pomProperties, err := parsePomProperties(filePath, strings.NewReader(fileContents))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to parse pom.properties virtualPath=%q path=%q: %+v", virtualPath, filePath, err)
|
log.WithFields("contents-path", filePath, "location", location.AccessPath()).Warnf("failed to parse pom.properties: %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +332,7 @@ func pomPropertiesByParentPath(archivePath, virtualPath string, extractPaths []s
|
|||||||
return propertiesByParentPath, nil
|
return propertiesByParentPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []string) (map[string]pkg.PomProject, error) {
|
func pomProjectByParentPath(archivePath string, location source.Location, extractPaths []string) (map[string]pkg.PomProject, error) {
|
||||||
contentsOfMavenProjectFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
contentsOfMavenProjectFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
||||||
@ -337,7 +342,7 @@ func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []stri
|
|||||||
for filePath, fileContents := range contentsOfMavenProjectFiles {
|
for filePath, fileContents := range contentsOfMavenProjectFiles {
|
||||||
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
|
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to parse pom.xml virtualPath=%q path=%q: %+v", virtualPath, filePath, err)
|
log.WithFields("contents-path", filePath, "location", location.AccessPath()).Warnf("failed to parse pom.xml: %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,18 +362,19 @@ func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []stri
|
|||||||
|
|
||||||
// packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
// packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
||||||
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
|
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
|
||||||
func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, virtualPath string) *pkg.Package {
|
func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location source.Location) *pkg.Package {
|
||||||
// keep the artifact name within the virtual path if this package does not match the parent package
|
// keep the artifact name within the virtual path if this package does not match the parent package
|
||||||
vPathSuffix := ""
|
vPathSuffix := ""
|
||||||
if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) {
|
if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) {
|
||||||
vPathSuffix += ":" + pomProperties.ArtifactID
|
vPathSuffix += ":" + pomProperties.ArtifactID
|
||||||
}
|
}
|
||||||
virtualPath += vPathSuffix
|
virtualPath := location.AccessPath() + vPathSuffix
|
||||||
|
|
||||||
// discovered props = new package
|
// discovered props = new package
|
||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: pomProperties.ArtifactID,
|
Name: pomProperties.ArtifactID,
|
||||||
Version: pomProperties.Version,
|
Version: pomProperties.Version,
|
||||||
|
Locations: source.NewLocationSet(location),
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pomProperties.PkgTypeIndicated(),
|
Type: pomProperties.PkgTypeIndicated(),
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
@ -434,17 +440,3 @@ func updateParentPackage(p pkg.Package, parentPkg *pkg.Package) {
|
|||||||
parentPkg.Metadata = parentMetadata
|
parentPkg.Metadata = parentMetadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPURL(p *pkg.Package) {
|
|
||||||
purl := packageURL(*p)
|
|
||||||
if purl == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata, ok := p.Metadata.(pkg.JavaMetadata)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
metadata.PURL = purl
|
|
||||||
p.Metadata = metadata
|
|
||||||
}
|
|
||||||
|
|||||||
@ -12,12 +12,13 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
|
||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateJavaBuildFixture(t *testing.T, fixturePath string) {
|
func generateJavaBuildFixture(t *testing.T, fixturePath string) {
|
||||||
@ -97,6 +98,7 @@ func TestParseJar(t *testing.T) {
|
|||||||
"example-jenkins-plugin": {
|
"example-jenkins-plugin": {
|
||||||
Name: "example-jenkins-plugin",
|
Name: "example-jenkins-plugin",
|
||||||
Version: "1.0-SNAPSHOT",
|
Version: "1.0-SNAPSHOT",
|
||||||
|
PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JenkinsPluginPkg,
|
Type: pkg.JenkinsPluginPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
@ -135,9 +137,7 @@ func TestParseJar(t *testing.T) {
|
|||||||
GroupID: "io.jenkins.plugins",
|
GroupID: "io.jenkins.plugins",
|
||||||
ArtifactID: "example-jenkins-plugin",
|
ArtifactID: "example-jenkins-plugin",
|
||||||
Version: "1.0-SNAPSHOT",
|
Version: "1.0-SNAPSHOT",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
PURL: "pkg:maven/io.jenkins.plugins/example-jenkins-plugin@1.0-SNAPSHOT",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -148,6 +148,7 @@ func TestParseJar(t *testing.T) {
|
|||||||
"example-java-app-gradle": {
|
"example-java-app-gradle": {
|
||||||
Name: "example-java-app-gradle",
|
Name: "example-java-app-gradle",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
|
PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
@ -158,7 +159,6 @@ func TestParseJar(t *testing.T) {
|
|||||||
"Manifest-Version": "1.0",
|
"Manifest-Version": "1.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PURL: "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -173,6 +173,7 @@ func TestParseJar(t *testing.T) {
|
|||||||
"example-java-app-maven": {
|
"example-java-app-maven": {
|
||||||
Name: "example-java-app-maven",
|
Name: "example-java-app-maven",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
|
PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
@ -194,14 +195,13 @@ func TestParseJar(t *testing.T) {
|
|||||||
GroupID: "org.anchore",
|
GroupID: "org.anchore",
|
||||||
ArtifactID: "example-java-app-maven",
|
ArtifactID: "example-java-app-maven",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
PURL: "pkg:maven/org.anchore/example-java-app-maven@0.1.0",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"joda-time": {
|
"joda-time": {
|
||||||
Name: "joda-time",
|
Name: "joda-time",
|
||||||
Version: "2.9.2",
|
Version: "2.9.2",
|
||||||
|
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
@ -214,7 +214,6 @@ func TestParseJar(t *testing.T) {
|
|||||||
GroupID: "joda-time",
|
GroupID: "joda-time",
|
||||||
ArtifactID: "joda-time",
|
ArtifactID: "joda-time",
|
||||||
Version: "2.9.2",
|
Version: "2.9.2",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
PomProject: &pkg.PomProject{
|
PomProject: &pkg.PomProject{
|
||||||
Path: "META-INF/maven/joda-time/joda-time/pom.xml",
|
Path: "META-INF/maven/joda-time/joda-time/pom.xml",
|
||||||
@ -225,7 +224,6 @@ func TestParseJar(t *testing.T) {
|
|||||||
Description: "Date and time library to replace JDK date handling",
|
Description: "Date and time library to replace JDK date handling",
|
||||||
URL: "http://www.joda.org/joda-time/",
|
URL: "http://www.joda.org/joda-time/",
|
||||||
},
|
},
|
||||||
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -238,20 +236,23 @@ func TestParseJar(t *testing.T) {
|
|||||||
generateJavaBuildFixture(t, test.fixture)
|
generateJavaBuildFixture(t, test.fixture)
|
||||||
|
|
||||||
fixture, err := os.Open(test.fixture)
|
fixture, err := os.Open(test.fixture)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("failed to open fixture: %+v", err)
|
|
||||||
|
for k := range test.expected {
|
||||||
|
p := test.expected[k]
|
||||||
|
p.Locations.Add(source.NewLocation(test.fixture))
|
||||||
|
test.expected[k] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
parser, cleanupFn, err := newJavaArchiveParser(fixture.Name(), fixture, false)
|
parser, cleanupFn, err := newJavaArchiveParser(source.LocationReadCloser{
|
||||||
|
Location: source.NewLocation(fixture.Name()),
|
||||||
|
ReadCloser: fixture,
|
||||||
|
}, false)
|
||||||
defer cleanupFn()
|
defer cleanupFn()
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("should not have filed... %+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, _, err := parser.parse()
|
actual, _, err := parser.parse()
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("failed to parse java archive: %+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(actual) != len(test.expected) {
|
if len(actual) != len(test.expected) {
|
||||||
for _, a := range actual {
|
for _, a := range actual {
|
||||||
@ -262,8 +263,9 @@ func TestParseJar(t *testing.T) {
|
|||||||
|
|
||||||
var parent *pkg.Package
|
var parent *pkg.Package
|
||||||
for _, a := range actual {
|
for _, a := range actual {
|
||||||
|
a := a
|
||||||
if strings.Contains(a.Name, "example-") {
|
if strings.Contains(a.Name, "example-") {
|
||||||
parent = a
|
parent = &a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,13 +303,7 @@ func TestParseJar(t *testing.T) {
|
|||||||
// write censored data back
|
// write censored data back
|
||||||
a.Metadata = metadata
|
a.Metadata = metadata
|
||||||
|
|
||||||
diffs := deep.Equal(&e, a)
|
pkgtest.AssertPackagesEqual(t, e, a)
|
||||||
if len(diffs) > 0 {
|
|
||||||
t.Errorf("diffs found for %q", a.Name)
|
|
||||||
for _, d := range diffs {
|
|
||||||
t.Errorf("diff: %+v", d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -512,14 +508,13 @@ func TestParseNestedJar(t *testing.T) {
|
|||||||
generateJavaBuildFixture(t, test.fixture)
|
generateJavaBuildFixture(t, test.fixture)
|
||||||
|
|
||||||
fixture, err := os.Open(test.fixture)
|
fixture, err := os.Open(test.fixture)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("failed to open fixture: %+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, _, err := parseJavaArchive(fixture.Name(), fixture)
|
actual, _, err := parseJavaArchive(nil, nil, source.LocationReadCloser{
|
||||||
if err != nil {
|
Location: source.NewLocation(fixture.Name()),
|
||||||
t.Fatalf("failed to parse java archive: %+v", err)
|
ReadCloser: fixture,
|
||||||
}
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
expectedNameVersionPairSet := internal.NewStringSet()
|
expectedNameVersionPairSet := internal.NewStringSet()
|
||||||
|
|
||||||
@ -536,7 +531,8 @@ func TestParseNestedJar(t *testing.T) {
|
|||||||
|
|
||||||
actualNameVersionPairSet := internal.NewStringSet()
|
actualNameVersionPairSet := internal.NewStringSet()
|
||||||
for _, a := range actual {
|
for _, a := range actual {
|
||||||
key := makeKey(a)
|
a := a
|
||||||
|
key := makeKey(&a)
|
||||||
actualNameVersionPairSet.Add(key)
|
actualNameVersionPairSet.Add(key)
|
||||||
if !expectedNameVersionPairSet.Contains(key) {
|
if !expectedNameVersionPairSet.Contains(key) {
|
||||||
t.Errorf("extra package: %s", a)
|
t.Errorf("extra package: %s", a)
|
||||||
@ -554,7 +550,8 @@ func TestParseNestedJar(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range actual {
|
for _, a := range actual {
|
||||||
actualKey := makeKey(a)
|
a := a
|
||||||
|
actualKey := makeKey(&a)
|
||||||
|
|
||||||
metadata := a.Metadata.(pkg.JavaMetadata)
|
metadata := a.Metadata.(pkg.JavaMetadata)
|
||||||
if actualKey == "spring-boot|0.0.1-SNAPSHOT" {
|
if actualKey == "spring-boot|0.0.1-SNAPSHOT" {
|
||||||
@ -942,9 +939,26 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actualPackage := newPackageFromMavenData(test.props, test.project, test.parent, virtualPath)
|
locations := source.NewLocationSet(source.NewLocation(virtualPath))
|
||||||
assert.Equal(t, test.expectedPackage, actualPackage, "new package doesn't match")
|
if test.expectedPackage != nil {
|
||||||
assert.Equal(t, test.expectedParent, *test.parent, "parent doesn't match")
|
test.expectedPackage.Locations = locations
|
||||||
|
if test.expectedPackage.Metadata.(pkg.JavaMetadata).Parent != nil {
|
||||||
|
test.expectedPackage.Metadata.(pkg.JavaMetadata).Parent.Locations = locations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if test.parent != nil {
|
||||||
|
test.parent.Locations = locations
|
||||||
|
}
|
||||||
|
test.expectedParent.Locations = locations
|
||||||
|
|
||||||
|
actualPackage := newPackageFromMavenData(test.props, test.project, test.parent, source.NewLocation(virtualPath))
|
||||||
|
if test.expectedPackage == nil {
|
||||||
|
require.Nil(t, actualPackage)
|
||||||
|
} else {
|
||||||
|
pkgtest.AssertPackagesEqual(t, *test.expectedPackage, *actualPackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgtest.AssertPackagesEqual(t, test.expectedParent, *test.parent)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,31 +4,30 @@ Package java provides a concrete Cataloger implementation for Java archives (jar
|
|||||||
package java
|
package java
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewJavaCataloger returns a new Java archive cataloger object.
|
// NewJavaCataloger returns a new Java archive cataloger object.
|
||||||
func NewJavaCataloger(cfg Config) *common.GenericCataloger {
|
func NewJavaCataloger(cfg Config) *generic.Cataloger {
|
||||||
globParsers := make(map[string]common.ParserFn)
|
c := generic.NewCataloger("java-cataloger").
|
||||||
|
WithParserByGlobs(parseJavaArchive, archiveFormatGlobs...)
|
||||||
// java archive formats
|
|
||||||
for _, pattern := range archiveFormatGlobs {
|
|
||||||
globParsers[pattern] = parseJavaArchive
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.SearchIndexedArchives {
|
if cfg.SearchIndexedArchives {
|
||||||
// java archives wrapped within zip files
|
// java archives wrapped within zip files
|
||||||
for _, pattern := range genericZipGlobs {
|
c.WithParserByGlobs(parseZipWrappedJavaArchive, genericZipGlobs...)
|
||||||
globParsers[pattern] = parseZipWrappedJavaArchive
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SearchUnindexedArchives {
|
if cfg.SearchUnindexedArchives {
|
||||||
// java archives wrapped within tar files
|
// java archives wrapped within tar files
|
||||||
for _, pattern := range genericTarGlobs {
|
c.WithParserByGlobs(parseTarWrappedJavaArchive, genericTarGlobs...)
|
||||||
globParsers[pattern] = parseTarWrappedJavaArchive
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return c
|
||||||
return common.NewGenericCataloger(nil, globParsers, "java-cataloger")
|
}
|
||||||
|
|
||||||
|
// NewJavaPomCataloger returns a cataloger capable of parsing
|
||||||
|
// dependencies from a pom.xml file.
|
||||||
|
// Pom files list dependencies that maybe not be locally installed yet.
|
||||||
|
func NewJavaPomCataloger() *generic.Cataloger {
|
||||||
|
return generic.NewCataloger("java-pom-cataloger").
|
||||||
|
WithParserByGlobs(parserPomXML, pomXMLDirGlob)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
|
||||||
func packageURL(p pkg.Package) string {
|
func packageURL(name, version string, metadata pkg.JavaMetadata) string {
|
||||||
var groupID = p.Name
|
var groupID = name
|
||||||
groupIDs := cpe.GroupIDsFromJavaPackage(p)
|
groupIDs := cpe.GroupIDsFromJavaMetadata(metadata)
|
||||||
if len(groupIDs) > 0 {
|
if len(groupIDs) > 0 {
|
||||||
groupID = groupIDs[0]
|
groupID = groupIDs[0]
|
||||||
}
|
}
|
||||||
@ -17,8 +17,8 @@ func packageURL(p pkg.Package) string {
|
|||||||
pURL := packageurl.NewPackageURL(
|
pURL := packageurl.NewPackageURL(
|
||||||
packageurl.TypeMaven, // TODO: should we filter down by package types here?
|
packageurl.TypeMaven, // TODO: should we filter down by package types here?
|
||||||
groupID,
|
groupID,
|
||||||
p.Name,
|
name,
|
||||||
p.Version,
|
version,
|
||||||
nil, // TODO: there are probably several qualifiers that can be specified here
|
nil, // TODO: there are probably several qualifiers that can be specified here
|
||||||
"")
|
"")
|
||||||
return pURL.ToString()
|
return pURL.ToString()
|
||||||
|
|||||||
@ -41,7 +41,7 @@ func Test_packageURL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.expect, func(t *testing.T) {
|
t.Run(tt.expect, func(t *testing.T) {
|
||||||
assert.Equal(t, tt.expect, packageURL(tt.pkg))
|
assert.Equal(t, tt.expect, packageURL(tt.pkg.Name, tt.pkg.Version, tt.pkg.Metadata.(pkg.JavaMetadata)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,11 +43,6 @@ func parsePomProperties(path string, reader io.Reader) (*pkg.PomProperties, erro
|
|||||||
return nil, fmt.Errorf("unable to parse pom.properties: %w", err)
|
return nil, fmt.Errorf("unable to parse pom.properties: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow for a nil collection, ensure it is empty
|
|
||||||
if props.Extra == nil {
|
|
||||||
props.Extra = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
props.Path = path
|
props.Path = path
|
||||||
|
|
||||||
return &props, nil
|
return &props, nil
|
||||||
|
|||||||
@ -19,7 +19,6 @@ func TestParseJavaPomProperties(t *testing.T) {
|
|||||||
GroupID: "org.anchore",
|
GroupID: "org.anchore",
|
||||||
ArtifactID: "example-java-app-maven",
|
ArtifactID: "example-java-app-maven",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -41,7 +40,6 @@ func TestParseJavaPomProperties(t *testing.T) {
|
|||||||
GroupID: "org.anchore",
|
GroupID: "org.anchore",
|
||||||
ArtifactID: "example-java-app-maven",
|
ArtifactID: "example-java-app-maven",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -50,7 +48,6 @@ func TestParseJavaPomProperties(t *testing.T) {
|
|||||||
GroupID: "org.anchore",
|
GroupID: "org.anchore",
|
||||||
ArtifactID: "example-java:app-maven",
|
ArtifactID: "example-java:app-maven",
|
||||||
Version: "0.1.0:something",
|
Version: "0.1.0:something",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,7 +56,6 @@ func TestParseJavaPomProperties(t *testing.T) {
|
|||||||
GroupID: "org.anchore",
|
GroupID: "org.anchore",
|
||||||
ArtifactID: "example-java=app-maven",
|
ArtifactID: "example-java=app-maven",
|
||||||
Version: "0.1.0=something",
|
Version: "0.1.0=something",
|
||||||
Extra: map[string]string{},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pomXMLGlob = "*pom.xml"
|
const pomXMLGlob = "*pom.xml"
|
||||||
@ -20,15 +22,15 @@ const pomXMLDirGlob = "**/pom.xml"
|
|||||||
|
|
||||||
var propertyMatcher = regexp.MustCompile("[$][{][^}]+[}]")
|
var propertyMatcher = regexp.MustCompile("[$][{][^}]+[}]")
|
||||||
|
|
||||||
func parserPomXML(path string, content io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
func parserPomXML(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
pom, err := decodePomXML(content)
|
pom, err := decodePomXML(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkgs []*pkg.Package
|
var pkgs []pkg.Package
|
||||||
for _, dep := range pom.Dependencies {
|
for _, dep := range pom.Dependencies {
|
||||||
p := newPackageFromPom(pom, dep)
|
p := newPackageFromPom(pom, dep, reader.Location)
|
||||||
if p.Name == "" {
|
if p.Name == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -60,22 +62,28 @@ func newPomProject(path string, p gopom.Project) *pkg.PomProject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPackageFromPom(pom gopom.Project, dep gopom.Dependency) *pkg.Package {
|
func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...source.Location) pkg.Package {
|
||||||
p := &pkg.Package{
|
m := pkg.JavaMetadata{
|
||||||
Name: dep.ArtifactID,
|
PomProperties: &pkg.PomProperties{
|
||||||
Version: resolveProperty(pom, dep.Version),
|
GroupID: resolveProperty(pom, dep.GroupID),
|
||||||
Language: pkg.Java,
|
|
||||||
Type: pkg.JavaPkg, // TODO: should we differentiate between packages from jar/war/zip versus packages from a pom.xml that were not installed yet?
|
|
||||||
MetadataType: pkg.JavaMetadataType,
|
|
||||||
FoundBy: javaPomCataloger,
|
|
||||||
Metadata: pkg.JavaMetadata{
|
|
||||||
PomProperties: &pkg.PomProperties{
|
|
||||||
GroupID: resolveProperty(pom, dep.GroupID),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Metadata = pkg.JavaMetadata{PURL: packageURL(*p)}
|
name := dep.ArtifactID
|
||||||
|
version := resolveProperty(pom, dep.Version)
|
||||||
|
|
||||||
|
p := pkg.Package{
|
||||||
|
Name: name,
|
||||||
|
Version: version,
|
||||||
|
Locations: source.NewLocationSet(locations...),
|
||||||
|
PURL: packageURL(name, version, m),
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg, // TODO: should we differentiate between packages from jar/war/zip versus packages from a pom.xml that were not installed yet?
|
||||||
|
MetadataType: pkg.JavaMetadataType,
|
||||||
|
Metadata: m,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetID()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,36 +8,38 @@ import (
|
|||||||
"github.com/vifraa/gopom"
|
"github.com/vifraa/gopom"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parserPomXML(t *testing.T) {
|
func Test_parserPomXML(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expected []*pkg.Package
|
expected []pkg.Package
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "test-fixtures/pom/pom.xml",
|
input: "test-fixtures/pom/pom.xml",
|
||||||
expected: []*pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "joda-time",
|
Name: "joda-time",
|
||||||
Version: "2.9.2",
|
Version: "2.9.2",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/com.joda/joda-time@2.9.2",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/com.joda/joda-time@2.9.2",
|
PomProperties: &pkg.PomProperties{GroupID: "com.joda"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "junit",
|
Name: "junit",
|
||||||
Version: "4.12",
|
Version: "4.12",
|
||||||
FoundBy: "java-pom-cataloger",
|
PURL: "pkg:maven/junit/junit@4.12",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/junit/junit@4.12",
|
PomProperties: &pkg.PomProperties{GroupID: "junit"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -46,13 +48,10 @@ func Test_parserPomXML(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.input, func(t *testing.T) {
|
t.Run(test.input, func(t *testing.T) {
|
||||||
fixture, err := os.Open(test.input)
|
for i := range test.expected {
|
||||||
assert.NoError(t, err)
|
test.expected[i].Locations.Add(source.NewLocation(test.input))
|
||||||
|
}
|
||||||
actual, relationships, err := parserPomXML(fixture.Name(), fixture)
|
pkgtest.TestFileParser(t, test.input, parserPomXML, test.expected, nil)
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Nil(t, relationships)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,119 +59,119 @@ func Test_parserPomXML(t *testing.T) {
|
|||||||
func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expected []*pkg.Package
|
expected []pkg.Package
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "test-fixtures/pom/commons-text.pom.xml",
|
input: "test-fixtures/pom/commons-text.pom.xml",
|
||||||
expected: []*pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
Name: "commons-lang3",
|
Name: "commons-lang3",
|
||||||
Version: "3.12.0",
|
Version: "3.12.0",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
|
PomProperties: &pkg.PomProperties{GroupID: "org.apache.commons"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "junit-jupiter",
|
Name: "junit-jupiter",
|
||||||
Version: "",
|
Version: "",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.junit.jupiter/junit-jupiter",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.junit.jupiter/junit-jupiter",
|
PomProperties: &pkg.PomProperties{GroupID: "org.junit.jupiter"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "assertj-core",
|
Name: "assertj-core",
|
||||||
Version: "3.23.1",
|
Version: "3.23.1",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.assertj/assertj-core@3.23.1",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.assertj/assertj-core@3.23.1",
|
PomProperties: &pkg.PomProperties{GroupID: "org.assertj"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "commons-io",
|
Name: "commons-io",
|
||||||
Version: "2.11.0",
|
Version: "2.11.0",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/commons-io/commons-io@2.11.0",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/commons-io/commons-io@2.11.0",
|
PomProperties: &pkg.PomProperties{GroupID: "commons-io"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "mockito-inline",
|
Name: "mockito-inline",
|
||||||
Version: "4.8.0",
|
Version: "4.8.0",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.mockito/mockito-inline@4.8.0",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.mockito/mockito-inline@4.8.0",
|
PomProperties: &pkg.PomProperties{GroupID: "org.mockito"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "js",
|
Name: "js",
|
||||||
Version: "22.0.0.2",
|
Version: "22.0.0.2",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.graalvm.js/js@22.0.0.2",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.graalvm.js/js@22.0.0.2",
|
PomProperties: &pkg.PomProperties{GroupID: "org.graalvm.js"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "js-scriptengine",
|
Name: "js-scriptengine",
|
||||||
Version: "22.0.0.2",
|
Version: "22.0.0.2",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.graalvm.js/js-scriptengine@22.0.0.2",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.graalvm.js/js-scriptengine@22.0.0.2",
|
PomProperties: &pkg.PomProperties{GroupID: "org.graalvm.js"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "commons-rng-simple",
|
Name: "commons-rng-simple",
|
||||||
Version: "1.4",
|
Version: "1.4",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.apache.commons/commons-rng-simple@1.4",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.apache.commons/commons-rng-simple@1.4",
|
PomProperties: &pkg.PomProperties{GroupID: "org.apache.commons"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "jmh-core",
|
Name: "jmh-core",
|
||||||
Version: "1.35",
|
Version: "1.35",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.openjdk.jmh/jmh-core@1.35",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.openjdk.jmh/jmh-core@1.35",
|
PomProperties: &pkg.PomProperties{GroupID: "org.openjdk.jmh"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "jmh-generator-annprocess",
|
Name: "jmh-generator-annprocess",
|
||||||
Version: "1.35",
|
Version: "1.35",
|
||||||
FoundBy: javaPomCataloger,
|
PURL: "pkg:maven/org.openjdk.jmh/jmh-generator-annprocess@1.35",
|
||||||
Language: pkg.Java,
|
Language: pkg.Java,
|
||||||
Type: pkg.JavaPkg,
|
Type: pkg.JavaPkg,
|
||||||
MetadataType: pkg.JavaMetadataType,
|
MetadataType: pkg.JavaMetadataType,
|
||||||
Metadata: pkg.JavaMetadata{
|
Metadata: pkg.JavaMetadata{
|
||||||
PURL: "pkg:maven/org.openjdk.jmh/jmh-generator-annprocess@1.35",
|
PomProperties: &pkg.PomProperties{GroupID: "org.openjdk.jmh"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -181,13 +180,10 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.input, func(t *testing.T) {
|
t.Run(test.input, func(t *testing.T) {
|
||||||
fixture, err := os.Open(test.input)
|
for i := range test.expected {
|
||||||
assert.NoError(t, err)
|
test.expected[i].Locations.Add(source.NewLocation(test.input))
|
||||||
|
}
|
||||||
actual, relationships, err := parserPomXML(fixture.Name(), fixture)
|
pkgtest.TestFileParser(t, test.input, parserPomXML, test.expected, nil)
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Nil(t, relationships)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
package java
|
|
||||||
|
|
||||||
import "github.com/anchore/syft/syft/pkg/cataloger/common"
|
|
||||||
|
|
||||||
const javaPomCataloger = "java-pom-cataloger"
|
|
||||||
|
|
||||||
// NewJavaPomCataloger returns a cataloger capable of parsing
|
|
||||||
// dependencies from a pom.xml file.
|
|
||||||
// Pom files list dependencies that maybe not be locally installed yet.
|
|
||||||
func NewJavaPomCataloger() *common.GenericCataloger {
|
|
||||||
globParsers := make(map[string]common.ParserFn)
|
|
||||||
|
|
||||||
// java project files
|
|
||||||
globParsers[pomXMLDirGlob] = parserPomXML
|
|
||||||
|
|
||||||
return common.NewGenericCataloger(nil, globParsers, javaPomCataloger)
|
|
||||||
}
|
|
||||||
@ -2,17 +2,14 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/file"
|
"github.com/anchore/syft/internal/file"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
// integrity check
|
|
||||||
var _ common.ParserFn = parseTarWrappedJavaArchive
|
|
||||||
|
|
||||||
var genericTarGlobs = []string{
|
var genericTarGlobs = []string{
|
||||||
"**/*.tar",
|
"**/*.tar",
|
||||||
// gzipped tar
|
// gzipped tar
|
||||||
@ -45,8 +42,8 @@ var genericTarGlobs = []string{
|
|||||||
// note: for compressed tars this is an extremely expensive operation and can lead to performance degradation. This is
|
// note: for compressed tars this is an extremely expensive operation and can lead to performance degradation. This is
|
||||||
// due to the fact that there is no central directory header (say as in zip), which means that in order to get
|
// due to the fact that there is no central directory header (say as in zip), which means that in order to get
|
||||||
// a file listing within the archive you must decompress the entire archive and seek through all of the entries.
|
// a file listing within the archive you must decompress the entire archive and seek through all of the entries.
|
||||||
func parseTarWrappedJavaArchive(virtualPath string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
func parseTarWrappedJavaArchive(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(virtualPath, reader)
|
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(reader.AccessPath(), reader)
|
||||||
// note: even on error, we should always run cleanup functions
|
// note: even on error, we should always run cleanup functions
|
||||||
defer cleanupFn()
|
defer cleanupFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,14 +51,14 @@ func parseTarWrappedJavaArchive(virtualPath string, reader io.Reader) ([]*pkg.Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// look for java archives within the tar archive
|
// look for java archives within the tar archive
|
||||||
return discoverPkgsFromTar(virtualPath, archivePath, contentPath)
|
return discoverPkgsFromTar(reader.Location, archivePath, contentPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func discoverPkgsFromTar(virtualPath, archivePath, contentPath string) ([]*pkg.Package, []artifact.Relationship, error) {
|
func discoverPkgsFromTar(location source.Location, archivePath, contentPath string) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
openers, err := file.ExtractGlobsFromTarToUniqueTempFile(archivePath, contentPath, archiveFormatGlobs...)
|
openers, err := file.ExtractGlobsFromTarToUniqueTempFile(archivePath, contentPath, archiveFormatGlobs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to extract files from tar: %w", err)
|
return nil, nil, fmt.Errorf("unable to extract files from tar: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return discoverPkgsFromOpeners(virtualPath, openers, nil)
|
return discoverPkgsFromOpeners(location, openers, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
||||||
@ -38,7 +40,10 @@ func Test_parseTarWrappedJavaArchive(t *testing.T) {
|
|||||||
t.Fatalf("failed to open fixture: %+v", err)
|
t.Fatalf("failed to open fixture: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualPkgs, _, err := parseTarWrappedJavaArchive(test.fixture, fixture)
|
actualPkgs, _, err := parseTarWrappedJavaArchive(nil, nil, source.LocationReadCloser{
|
||||||
|
Location: source.NewLocation(test.fixture),
|
||||||
|
ReadCloser: fixture,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var actualNames []string
|
var actualNames []string
|
||||||
|
|||||||
@ -2,17 +2,14 @@ package java
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/file"
|
"github.com/anchore/syft/internal/file"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
// integrity check
|
|
||||||
var _ common.ParserFn = parseZipWrappedJavaArchive
|
|
||||||
|
|
||||||
var genericZipGlobs = []string{
|
var genericZipGlobs = []string{
|
||||||
"**/*.zip",
|
"**/*.zip",
|
||||||
}
|
}
|
||||||
@ -20,8 +17,8 @@ var genericZipGlobs = []string{
|
|||||||
// TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246)
|
// TODO: when the generic archive cataloger is implemented, this should be removed (https://github.com/anchore/syft/issues/246)
|
||||||
|
|
||||||
// parseZipWrappedJavaArchive is a parser function for java archive contents contained within arbitrary zip files.
|
// parseZipWrappedJavaArchive is a parser function for java archive contents contained within arbitrary zip files.
|
||||||
func parseZipWrappedJavaArchive(virtualPath string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
func parseZipWrappedJavaArchive(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(virtualPath, reader)
|
contentPath, archivePath, cleanupFn, err := saveArchiveToTmp(reader.AccessPath(), reader)
|
||||||
// note: even on error, we should always run cleanup functions
|
// note: even on error, we should always run cleanup functions
|
||||||
defer cleanupFn()
|
defer cleanupFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,5 +35,5 @@ func parseZipWrappedJavaArchive(virtualPath string, reader io.Reader) ([]*pkg.Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// look for java archives within the zip archive
|
// look for java archives within the zip archive
|
||||||
return discoverPkgsFromZip(virtualPath, archivePath, contentPath, fileManifest, nil)
|
return discoverPkgsFromZip(reader.Location, archivePath, contentPath, fileManifest, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parseZipWrappedJavaArchive(t *testing.T) {
|
func Test_parseZipWrappedJavaArchive(t *testing.T) {
|
||||||
@ -31,7 +33,10 @@ func Test_parseZipWrappedJavaArchive(t *testing.T) {
|
|||||||
t.Fatalf("failed to open fixture: %+v", err)
|
t.Fatalf("failed to open fixture: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualPkgs, _, err := parseZipWrappedJavaArchive(test.fixture, fixture)
|
actualPkgs, _, err := parseZipWrappedJavaArchive(nil, nil, source.LocationReadCloser{
|
||||||
|
Location: source.NewLocation(test.fixture),
|
||||||
|
ReadCloser: fixture,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var actualNames []string
|
var actualNames []string
|
||||||
|
|||||||
@ -5,11 +5,8 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ urlIdentifier = (*JavaMetadata)(nil)
|
|
||||||
|
|
||||||
var jenkinsPluginPomPropertiesGroupIDs = []string{
|
var jenkinsPluginPomPropertiesGroupIDs = []string{
|
||||||
"io.jenkins.plugins",
|
"io.jenkins.plugins",
|
||||||
"org.jenkins.plugins",
|
"org.jenkins.plugins",
|
||||||
@ -25,7 +22,6 @@ type JavaMetadata struct {
|
|||||||
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty" cyclonedx:"-"`
|
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty" cyclonedx:"-"`
|
||||||
PomProject *PomProject `mapstructure:"PomProject" json:"pomProject,omitempty"`
|
PomProject *PomProject `mapstructure:"PomProject" json:"pomProject,omitempty"`
|
||||||
ArchiveDigests []file.Digest `hash:"ignore" json:"digest,omitempty"`
|
ArchiveDigests []file.Digest `hash:"ignore" json:"digest,omitempty"`
|
||||||
PURL string `hash:"ignore" json:"-"` // pURLs and CPEs are ignored for package IDs
|
|
||||||
Parent *Package `hash:"ignore" json:"-"` // note: the parent cannot be included in the minimal definition of uniqueness since this field is not reproducible in an encode-decode cycle (is lossy).
|
Parent *Package `hash:"ignore" json:"-"` // note: the parent cannot be included in the minimal definition of uniqueness since this field is not reproducible in an encode-decode cycle (is lossy).
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +32,7 @@ type PomProperties struct {
|
|||||||
GroupID string `mapstructure:"groupId" json:"groupId" cyclonedx:"groupID"`
|
GroupID string `mapstructure:"groupId" json:"groupId" cyclonedx:"groupID"`
|
||||||
ArtifactID string `mapstructure:"artifactId" json:"artifactId" cyclonedx:"artifactID"`
|
ArtifactID string `mapstructure:"artifactId" json:"artifactId" cyclonedx:"artifactID"`
|
||||||
Version string `mapstructure:"version" json:"version"`
|
Version string `mapstructure:"version" json:"version"`
|
||||||
Extra map[string]string `mapstructure:",remain" json:"extraFields"`
|
Extra map[string]string `mapstructure:",remain" json:"extraFields,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PomProject represents fields of interest extracted from a Java archive's pom.xml file. See https://maven.apache.org/ref/3.6.3/maven-model/maven.html for more details.
|
// PomProject represents fields of interest extracted from a Java archive's pom.xml file. See https://maven.apache.org/ref/3.6.3/maven-model/maven.html for more details.
|
||||||
@ -72,8 +68,3 @@ type JavaManifest struct {
|
|||||||
Main map[string]string `json:"main,omitempty"`
|
Main map[string]string `json:"main,omitempty"`
|
||||||
NamedSections map[string]map[string]string `json:"namedSections,omitempty"`
|
NamedSections map[string]map[string]string `json:"namedSections,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageURL returns the PURL for the specific Maven package (see https://github.com/package-url/purl-spec)
|
|
||||||
func (m JavaMetadata) PackageURL(_ *linux.Release) string {
|
|
||||||
return m.PURL
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
@ -21,11 +22,11 @@ type Package struct {
|
|||||||
FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
FoundBy string `hash:"ignore" cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
||||||
Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||||
Licenses []string // licenses discovered with the package metadata
|
Licenses []string // licenses discovered with the package metadata
|
||||||
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
Language Language `hash:"ignore" cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
||||||
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
||||||
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
||||||
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec)
|
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec)
|
||||||
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
|
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
|
||||||
Metadata interface{} // additional data found while parsing the package source
|
Metadata interface{} // additional data found while parsing the package source
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,23 +83,43 @@ func IsValid(p *Package) bool {
|
|||||||
return p != nil && p.Name != ""
|
return p != nil && p.Name != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit
|
||||||
|
func Less(i, j Package) bool {
|
||||||
|
if i.Name == j.Name {
|
||||||
|
if i.Version == j.Version {
|
||||||
|
iLocations := i.Locations.ToSlice()
|
||||||
|
jLocations := j.Locations.ToSlice()
|
||||||
|
if i.Type == j.Type {
|
||||||
|
maxLen := len(iLocations)
|
||||||
|
if len(jLocations) > maxLen {
|
||||||
|
maxLen = len(jLocations)
|
||||||
|
}
|
||||||
|
for l := 0; l < maxLen; l++ {
|
||||||
|
if len(iLocations) < l+1 || len(jLocations) < l+1 {
|
||||||
|
if len(iLocations) == len(jLocations) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return len(iLocations) < len(jLocations)
|
||||||
|
}
|
||||||
|
if iLocations[l].RealPath == jLocations[l].RealPath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return iLocations[l].RealPath < jLocations[l].RealPath
|
||||||
|
}
|
||||||
|
// compare remaining metadata as a final fallback
|
||||||
|
// note: we cannot guarantee that IDs (which digests the metadata) are stable enough to sort on
|
||||||
|
// when there are potentially missing elements there is too much reduction in the dimensions to
|
||||||
|
// lean on ID comparison. The best fallback is to look at the string representation of the metadata.
|
||||||
|
return strings.Compare(fmt.Sprintf("%#v", i.Metadata), fmt.Sprintf("%#v", j.Metadata)) < 0
|
||||||
|
}
|
||||||
|
return i.Type < j.Type
|
||||||
|
}
|
||||||
|
return i.Version < j.Version
|
||||||
|
}
|
||||||
|
return i.Name < j.Name
|
||||||
|
}
|
||||||
func Sort(pkgs []Package) {
|
func Sort(pkgs []Package) {
|
||||||
sort.SliceStable(pkgs, func(i, j int) bool {
|
sort.SliceStable(pkgs, func(i, j int) bool {
|
||||||
if pkgs[i].Name == pkgs[j].Name {
|
return Less(pkgs[i], pkgs[j])
|
||||||
if pkgs[i].Version == pkgs[j].Version {
|
|
||||||
iLocations := pkgs[i].Locations.ToSlice()
|
|
||||||
jLocations := pkgs[j].Locations.ToSlice()
|
|
||||||
if pkgs[i].Type == pkgs[j].Type && len(iLocations) > 0 && len(jLocations) > 0 {
|
|
||||||
if iLocations[0].String() == jLocations[0].String() {
|
|
||||||
// compare IDs as a final fallback
|
|
||||||
return pkgs[i].ID() < pkgs[j].ID()
|
|
||||||
}
|
|
||||||
return iLocations[0].String() < jLocations[0].String()
|
|
||||||
}
|
|
||||||
return pkgs[i].Type < pkgs[j].Type
|
|
||||||
}
|
|
||||||
return pkgs[i].Version < pkgs[j].Version
|
|
||||||
}
|
|
||||||
return pkgs[i].Name < pkgs[j].Name
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,12 +183,12 @@ func TestIDUniqueness(t *testing.T) {
|
|||||||
expectedIDComparison: assert.Equal,
|
expectedIDComparison: assert.Equal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "language is reflected",
|
name: "language is NOT reflected",
|
||||||
transform: func(pkg Package) Package {
|
transform: func(pkg Package) Package {
|
||||||
pkg.Language = Rust
|
pkg.Language = Rust
|
||||||
return pkg
|
return pkg
|
||||||
},
|
},
|
||||||
expectedIDComparison: assert.NotEqual,
|
expectedIDComparison: assert.Equal,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "metadata mutation is reflected",
|
name: "metadata mutation is reflected",
|
||||||
|
|||||||
@ -1,14 +1,5 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ urlIdentifier = (*PhpComposerJSONMetadata)(nil)
|
|
||||||
|
|
||||||
// PhpComposerJSONMetadata represents information found from composer v1/v2 "installed.json" files as well as composer.lock files
|
// PhpComposerJSONMetadata represents information found from composer v1/v2 "installed.json" files as well as composer.lock files
|
||||||
type PhpComposerJSONMetadata struct {
|
type PhpComposerJSONMetadata struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -42,29 +33,3 @@ type PhpComposerAuthors struct {
|
|||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Homepage string `json:"homepage,omitempty"`
|
Homepage string `json:"homepage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m PhpComposerJSONMetadata) PackageURL(_ *linux.Release) string {
|
|
||||||
var name, vendor string
|
|
||||||
fields := strings.Split(m.Name, "/")
|
|
||||||
switch len(fields) {
|
|
||||||
case 0:
|
|
||||||
return ""
|
|
||||||
case 1:
|
|
||||||
name = m.Name
|
|
||||||
case 2:
|
|
||||||
vendor = fields[0]
|
|
||||||
name = fields[1]
|
|
||||||
default:
|
|
||||||
vendor = fields[0]
|
|
||||||
name = strings.Join(fields[1:], "-")
|
|
||||||
}
|
|
||||||
|
|
||||||
pURL := packageurl.NewPackageURL(
|
|
||||||
packageurl.TypeComposer,
|
|
||||||
vendor,
|
|
||||||
name,
|
|
||||||
m.Version,
|
|
||||||
nil,
|
|
||||||
"")
|
|
||||||
return pURL.ToString()
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
package pkg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPhpComposerJsonMetadata_pURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
distro *linux.Release
|
|
||||||
metadata PhpComposerJSONMetadata
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "with extractable vendor",
|
|
||||||
metadata: PhpComposerJSONMetadata{
|
|
||||||
Name: "ven/name",
|
|
||||||
Version: "1.0.1",
|
|
||||||
},
|
|
||||||
expected: "pkg:composer/ven/name@1.0.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "name with slashes (invalid)",
|
|
||||||
metadata: PhpComposerJSONMetadata{
|
|
||||||
Name: "ven/name/component",
|
|
||||||
Version: "1.0.1",
|
|
||||||
},
|
|
||||||
expected: "pkg:composer/ven/name-component@1.0.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown vendor",
|
|
||||||
metadata: PhpComposerJSONMetadata{
|
|
||||||
Name: "name",
|
|
||||||
Version: "1.0.1",
|
|
||||||
},
|
|
||||||
expected: "pkg:composer/name@1.0.1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ignores distro",
|
|
||||||
distro: &linux.Release{
|
|
||||||
ID: "rhel",
|
|
||||||
VersionID: "8.4",
|
|
||||||
},
|
|
||||||
metadata: PhpComposerJSONMetadata{
|
|
||||||
Name: "ven/name",
|
|
||||||
Version: "1.0.1",
|
|
||||||
},
|
|
||||||
expected: "pkg:composer/ven/name@1.0.1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
actual := test.metadata.PackageURL(test.distro)
|
|
||||||
if actual != test.expected {
|
|
||||||
dmp := diffmatchpatch.New()
|
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
|
||||||
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -21,44 +21,6 @@ const (
|
|||||||
purlGradlePkgType = "gradle"
|
purlGradlePkgType = "gradle"
|
||||||
)
|
)
|
||||||
|
|
||||||
type urlIdentifier interface {
|
|
||||||
PackageURL(*linux.Release) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func URL(p Package, release *linux.Release) string {
|
|
||||||
if p.Metadata != nil {
|
|
||||||
if i, ok := p.Metadata.(urlIdentifier); ok {
|
|
||||||
return i.PackageURL(release)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the remaining cases are primarily reserved for packages without metadata struct instances
|
|
||||||
|
|
||||||
var purlType = p.Type.PackageURLType()
|
|
||||||
var name = p.Name
|
|
||||||
var namespace = ""
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case purlType == "":
|
|
||||||
purlType = packageurl.TypeGeneric
|
|
||||||
case p.Type == NpmPkg:
|
|
||||||
fields := strings.SplitN(p.Name, "/", 2)
|
|
||||||
if len(fields) > 1 {
|
|
||||||
namespace = fields[0]
|
|
||||||
name = fields[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// generate a purl from the package data
|
|
||||||
return packageurl.NewPackageURL(
|
|
||||||
purlType,
|
|
||||||
namespace,
|
|
||||||
name,
|
|
||||||
p.Version,
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
).ToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
func PURLQualifiers(vars map[string]string, release *linux.Release) (q packageurl.Qualifiers) {
|
func PURLQualifiers(vars map[string]string, release *linux.Release) (q packageurl.Qualifiers) {
|
||||||
keys := make([]string, 0, len(vars))
|
keys := make([]string, 0, len(vars))
|
||||||
for k := range vars {
|
for k := range vars {
|
||||||
|
|||||||
@ -1,100 +0,0 @@
|
|||||||
package pkg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/scylladb/go-set/strset"
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/linux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPackageURL(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pkg Package
|
|
||||||
distro *linux.Release
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "java",
|
|
||||||
pkg: Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: JavaPkg,
|
|
||||||
Metadata: JavaMetadata{
|
|
||||||
PomProperties: &PomProperties{},
|
|
||||||
PURL: "pkg:maven/g.id/a@v", // assembled by the java cataloger
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
expected: "pkg:maven/g.id/a@v",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "jenkins-plugin",
|
|
||||||
pkg: Package{
|
|
||||||
Name: "bad-name",
|
|
||||||
Version: "bad-v0.1.0",
|
|
||||||
Type: JenkinsPluginPkg,
|
|
||||||
Metadata: JavaMetadata{
|
|
||||||
PomProperties: &PomProperties{},
|
|
||||||
PURL: "pkg:maven/g.id/a@v", // assembled by the java cataloger
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
expected: "pkg:maven/g.id/a@v",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var pkgTypes []string
|
|
||||||
var expectedTypes = strset.New()
|
|
||||||
for _, ty := range AllPkgs {
|
|
||||||
expectedTypes.Add(string(ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
// testing microsoft packages is not valid for purl at this time
|
|
||||||
expectedTypes.Remove(string(KbPkg))
|
|
||||||
expectedTypes.Remove(string(PortagePkg))
|
|
||||||
expectedTypes.Remove(string(AlpmPkg))
|
|
||||||
expectedTypes.Remove(string(ApkPkg))
|
|
||||||
expectedTypes.Remove(string(ConanPkg))
|
|
||||||
expectedTypes.Remove(string(DartPubPkg))
|
|
||||||
expectedTypes.Remove(string(DotnetPkg))
|
|
||||||
expectedTypes.Remove(string(DebPkg))
|
|
||||||
expectedTypes.Remove(string(GoModulePkg))
|
|
||||||
expectedTypes.Remove(string(HackagePkg))
|
|
||||||
expectedTypes.Remove(string(BinaryPkg))
|
|
||||||
expectedTypes.Remove(string(PhpComposerPkg))
|
|
||||||
expectedTypes.Remove(string(PythonPkg))
|
|
||||||
expectedTypes.Remove(string(RpmPkg))
|
|
||||||
expectedTypes.Remove(string(GemPkg))
|
|
||||||
expectedTypes.Remove(string(NpmPkg))
|
|
||||||
expectedTypes.Remove(string(RustPkg))
|
|
||||||
expectedTypes.Remove(string(CocoapodsPkg))
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
if test.pkg.Type != "" && !contains(pkgTypes, string(test.pkg.Type)) {
|
|
||||||
pkgTypes = append(pkgTypes, string(test.pkg.Type))
|
|
||||||
}
|
|
||||||
actual := URL(test.pkg, test.distro)
|
|
||||||
if actual != test.expected {
|
|
||||||
dmp := diffmatchpatch.New()
|
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
|
||||||
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
assert.ElementsMatch(t, expectedTypes.List(), pkgTypes, "missing one or more package types to test against (maybe a package type was added?)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(values []string, val string) bool {
|
|
||||||
for _, v := range values {
|
|
||||||
if val == v {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@ -73,6 +73,7 @@ func TestJSONSchema(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateJsonAgainstSchema(t testing.TB, json string) {
|
func validateJsonAgainstSchema(t testing.TB, json string) {
|
||||||
|
t.Helper()
|
||||||
fullSchemaPath := path.Join(repoRoot(t), jsonSchemaPath, fmt.Sprintf("schema-%s.json", internal.JSONSchemaVersion))
|
fullSchemaPath := path.Join(repoRoot(t), jsonSchemaPath, fmt.Sprintf("schema-%s.json", internal.JSONSchemaVersion))
|
||||||
schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", fullSchemaPath))
|
schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", fullSchemaPath))
|
||||||
documentLoader := gojsonschema.NewStringLoader(json)
|
documentLoader := gojsonschema.NewStringLoader(json)
|
||||||
|
|||||||
@ -86,7 +86,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
|||||||
s1 := string(by1)
|
s1 := string(by1)
|
||||||
s2 := string(by2)
|
s2 := string(by2)
|
||||||
if diff := cmp.Diff(s1, s2); diff != "" {
|
if diff := cmp.Diff(s1, s2); diff != "" {
|
||||||
t.Errorf("Encode/Decode mismatch (-want +got):\n%s", diff)
|
t.Errorf("Encode/Decode mismatch (-want +got) [image %q]:\n%s", image, diff)
|
||||||
}
|
}
|
||||||
} else if !assert.True(t, bytes.Equal(by1, by2)) {
|
} else if !assert.True(t, bytes.Equal(by1, by2)) {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user