mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 17:03:17 +01:00
Merge pull request #375 from anchore/package-verification-fields-dpkg
Update package verification fields for DPKG
This commit is contained in:
commit
66ebe49a04
@ -6,5 +6,5 @@ const (
|
|||||||
|
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON presenter
|
// JSONSchemaVersion is the current schema version output by the JSON presenter
|
||||||
// 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 = "1.0.5"
|
JSONSchemaVersion = "1.1.0"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -75,7 +75,7 @@
|
|||||||
"version": "[not provided]"
|
"version": "[not provided]"
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.5.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"path": "/somefile-1.txt",
|
"path": "/somefile-1.txt",
|
||||||
"layerID": "sha256:6c376352c0537f4483e4033e332d7a4ab9433db68c54c297a834d36719aeb6c9"
|
"layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [
|
"licenses": [
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"locations": [
|
"locations": [
|
||||||
{
|
{
|
||||||
"path": "/somefile-2.txt",
|
"path": "/somefile-2.txt",
|
||||||
"layerID": "sha256:fc8218a8142ee4952bb8d9b96b3e9838322e9e6eae6477136bcad8fd768949b7"
|
"layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [],
|
"licenses": [],
|
||||||
@ -67,7 +67,7 @@
|
|||||||
"type": "image",
|
"type": "image",
|
||||||
"target": {
|
"target": {
|
||||||
"userInput": "user-image-input",
|
"userInput": "user-image-input",
|
||||||
"imageID": "sha256:1f9cb9dc477f7482856f88ed40c38e260db0526d7a0dad5a0be566bfedde929b",
|
"imageID": "sha256:5900c94a5bc1e083aa24ad1a223bf6eb9910dc8a6b01cb979ec306cb91709ea1",
|
||||||
"manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
"manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -77,17 +77,17 @@
|
|||||||
"layers": [
|
"layers": [
|
||||||
{
|
{
|
||||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
"digest": "sha256:6c376352c0537f4483e4033e332d7a4ab9433db68c54c297a834d36719aeb6c9",
|
"digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59",
|
||||||
"size": 22
|
"size": 22
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||||
"digest": "sha256:fc8218a8142ee4952bb8d9b96b3e9838322e9e6eae6477136bcad8fd768949b7",
|
"digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec",
|
||||||
"size": 16
|
"size": 16
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxNTg2LCJkaWdlc3QiOiJzaGEyNTY6MWY5Y2I5ZGM0NzdmNzQ4Mjg1NmY4OGVkNDBjMzhlMjYwZGIwNTI2ZDdhMGRhZDVhMGJlNTY2YmZlZGRlOTI5YiJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMDQ4LCJkaWdlc3QiOiJzaGEyNTY6NmMzNzYzNTJjMDUzN2Y0NDgzZTQwMzNlMzMyZDdhNGFiOTQzM2RiNjhjNTRjMjk3YTgzNGQzNjcxOWFlYjZjOSJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYzgyMThhODE0MmVlNDk1MmJiOGQ5Yjk2YjNlOTgzODMyMmU5ZTZlYWU2NDc3MTM2YmNhZDhmZDc2ODk0OWI3In1dfQ==",
|
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NjUsImRpZ2VzdCI6InNoYTI1Njo1OTAwYzk0YTViYzFlMDgzYWEyNGFkMWEyMjNiZjZlYjk5MTBkYzhhNmIwMWNiOTc5ZWMzMDZjYjkxNzA5ZWExIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19",
|
||||||
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpudWxsLCJJbWFnZSI6InNoYTI1NjoyOWQ1YjFjOTkyNjg0MzgwYjQ3NTEyMjliMmNjN2E4MzdkOTBmOWQ1OTJhYmIxZjAyZGYzZGRkMGQ3OWFjMDkxIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCIjKG5vcCkgQUREIGZpbGU6ZGYzYjc0NGY1NGE5YjE2YjliOWFlZDQwZTNlOThkOWNhMmI0OWY1YTc3ZDlmYThhOTc2OTBkN2JhZjU4ODgyMCBpbiAvc29tZWZpbGUtMi50eHQgIl0sIkltYWdlIjoic2hhMjU2OjI5ZDViMWM5OTI2ODQzODBiNDc1MTIyOWIyY2M3YTgzN2Q5MGY5ZDU5MmFiYjFmMDJkZjNkZGQwZDc5YWMwOTEiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDQtMDFUMTI6NDg6MzIuMjYzNjAzMVoiLCJkb2NrZXJfdmVyc2lvbiI6IjIwLjEwLjIiLCJoaXN0b3J5IjpbeyJjcmVhdGVkIjoiMjAyMS0wNC0wMVQxMjo0ODozMi4wODY3MTY2WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSBBREQgZmlsZTphYzMyZGEyM2Q1MWU4MDFmMDJmOTI0MTIzZWQzMDk5MGViM2YwZmVjMWI5ZWQ0ZjBiMDZjMjRlODhiOWMzNjk1IGluIC9zb21lZmlsZS0xLnR4dCAifSx7ImNyZWF0ZWQiOiIyMDIxLTA0LTAxVDEyOjQ4OjMyLjI2MzYwMzFaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBmaWxlOmRmM2I3NDRmNTRhOWIxNmI5YjlhZWQ0MGUzZTk4ZDljYTJiNDlmNWE3N2Q5ZmE4YTk3NjkwZDdiYWY1ODg4MjAgaW4gL3NvbWVmaWxlLTIudHh0ICJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OjZjMzc2MzUyYzA1MzdmNDQ4M2U0MDMzZTMzMmQ3YTRhYjk0MzNkYjY4YzU0YzI5N2E4MzRkMzY3MTlhZWI2YzkiLCJzaGEyNTY6ZmM4MjE4YTgxNDJlZTQ5NTJiYjhkOWI5NmIzZTk4MzgzMjJlOWU2ZWFlNjQ3NzEzNmJjYWQ4ZmQ3Njg5NDliNyJdfX0=",
|
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjEtMDQtMDZUMTk6MTM6NTIuNTI0Mzc4WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjQ1ODIwNjFaIiwiY3JlYXRlZF9ieSI6IkFERCBmaWxlLTEudHh0IC9zb21lZmlsZS0xLnR4dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifSx7ImNyZWF0ZWQiOiIyMDIxLTA0LTA2VDE5OjEzOjUyLjUyNDM3OFoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMi50eHQgL3NvbWVmaWxlLTIudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OmZiNmJlZWNiNzViMzlmNGJiODEzZGJmMTc3ZTUwMWVkZDVkZGIzZTY5YmI0NWNlZGViNzhjNjc2ZWUxYjdhNTkiLCJzaGEyNTY6MzE5YjU4OGNlNjQyNTNhODdiNTMzYzhlZDAxY2YwMDI1ZTBlYWM5OGU3YjUxNmUxMjUzMjk1N2UxMjQ0ZmRlYyJdfX0=",
|
||||||
"repoDigests": [],
|
"repoDigests": [],
|
||||||
"scope": "Squashed"
|
"scope": "Squashed"
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@
|
|||||||
"version": "[not provided]"
|
"version": "[not provided]"
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.5.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -205,7 +205,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.0",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.5.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.1.0.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,7 @@ func build() *jsonschema.Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set the "anyOf" field for Package.Metadata to be a conjunction of several types
|
// set the "anyOf" field for Package.Metadata to be a conjunction of several types
|
||||||
documentSchema.Definitions["Document"].Properties.Set("artifacts.metadata", map[string][]map[string]string{
|
documentSchema.Definitions["Package"].Properties.Set("metadata", map[string][]map[string]string{
|
||||||
"anyOf": metadataTypes,
|
"anyOf": metadataTypes,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
891
schema/json/schema-1.1.0.json
Normal file
891
schema/json/schema-1.1.0.json
Normal file
@ -0,0 +1,891 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Document",
|
||||||
|
"definitions": {
|
||||||
|
"ApkFileRecord": {
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ownerUid": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ownerGid": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"ApkMetadata": {
|
||||||
|
"required": [
|
||||||
|
"package",
|
||||||
|
"originPackage",
|
||||||
|
"maintainer",
|
||||||
|
"version",
|
||||||
|
"license",
|
||||||
|
"architecture",
|
||||||
|
"url",
|
||||||
|
"description",
|
||||||
|
"size",
|
||||||
|
"installedSize",
|
||||||
|
"pullDependencies",
|
||||||
|
"pullChecksum",
|
||||||
|
"gitCommitOfApkPort",
|
||||||
|
"files"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"package": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"originPackage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"maintainer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"architecture": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"installedSize": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pullDependencies": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pullChecksum": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"gitCommitOfApkPort": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/ApkFileRecord"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"CargoPackageMetadata": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"source",
|
||||||
|
"checksum",
|
||||||
|
"dependencies"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"checksum": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Descriptor": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Digest": {
|
||||||
|
"required": [
|
||||||
|
"algorithm",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"algorithm": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Distribution": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"idLike"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"idLike": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Document": {
|
||||||
|
"required": [
|
||||||
|
"artifacts",
|
||||||
|
"artifactRelationships",
|
||||||
|
"source",
|
||||||
|
"distro",
|
||||||
|
"descriptor",
|
||||||
|
"schema"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"fileMetadata": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/FileMetadata"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Secrets"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"artifacts": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Package"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"artifactRelationships": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Relationship"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Source"
|
||||||
|
},
|
||||||
|
"distro": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Distribution"
|
||||||
|
},
|
||||||
|
"descriptor": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Descriptor"
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Schema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DpkgFileRecord": {
|
||||||
|
"required": [
|
||||||
|
"path",
|
||||||
|
"isConfigFile"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Digest"
|
||||||
|
},
|
||||||
|
"isConfigFile": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"DpkgMetadata": {
|
||||||
|
"required": [
|
||||||
|
"package",
|
||||||
|
"source",
|
||||||
|
"version",
|
||||||
|
"sourceVersion",
|
||||||
|
"architecture",
|
||||||
|
"maintainer",
|
||||||
|
"installedSize",
|
||||||
|
"files"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"package": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sourceVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"architecture": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"maintainer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"installedSize": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/DpkgFileRecord"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"FileMetadata": {
|
||||||
|
"required": [
|
||||||
|
"location",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"location": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Location"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/FileMetadataEntry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"FileMetadataEntry": {
|
||||||
|
"required": [
|
||||||
|
"mode",
|
||||||
|
"type",
|
||||||
|
"userID",
|
||||||
|
"groupID"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"mode": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"linkDestination": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"userID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"groupID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"digests": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/Digest"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"GemMetadata": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"authors": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"homepage": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"JavaManifest": {
|
||||||
|
"properties": {
|
||||||
|
"main": {
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"namedSections": {
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"JavaMetadata": {
|
||||||
|
"required": [
|
||||||
|
"virtualPath"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"virtualPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/JavaManifest"
|
||||||
|
},
|
||||||
|
"pomProperties": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/PomProperties"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Location": {
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"layerID": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"NpmPackageJSONMetadata": {
|
||||||
|
"required": [
|
||||||
|
"author",
|
||||||
|
"licenses",
|
||||||
|
"homepage",
|
||||||
|
"description",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"homepage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Package": {
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"type",
|
||||||
|
"foundBy",
|
||||||
|
"locations",
|
||||||
|
"licenses",
|
||||||
|
"language",
|
||||||
|
"cpes",
|
||||||
|
"purl",
|
||||||
|
"metadataType",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"foundBy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"locations": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Location"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cpes": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"purl": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadataType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/ApkMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/CargoPackageMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/DpkgMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/GemMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/JavaMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/NpmPackageJSONMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PythonPackageMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/RpmdbMetadata"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"PomProperties": {
|
||||||
|
"required": [
|
||||||
|
"path",
|
||||||
|
"name",
|
||||||
|
"groupId",
|
||||||
|
"artifactId",
|
||||||
|
"version",
|
||||||
|
"extraFields"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"groupId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"artifactId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"extraFields": {
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"PythonFileDigest": {
|
||||||
|
"required": [
|
||||||
|
"algorithm",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"algorithm": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"PythonFileRecord": {
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/PythonFileDigest"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"PythonPackageMetadata": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"license",
|
||||||
|
"author",
|
||||||
|
"authorEmail",
|
||||||
|
"platform",
|
||||||
|
"sitePackagesRootPath"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authorEmail": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/PythonFileRecord"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"sitePackagesRootPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"topLevelPackages": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Relationship": {
|
||||||
|
"required": [
|
||||||
|
"parent",
|
||||||
|
"child",
|
||||||
|
"type",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"parent": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"child": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"RpmdbFileRecord": {
|
||||||
|
"required": [
|
||||||
|
"path",
|
||||||
|
"mode",
|
||||||
|
"size",
|
||||||
|
"sha256"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"sha256": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"RpmdbMetadata": {
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"epoch",
|
||||||
|
"architecture",
|
||||||
|
"release",
|
||||||
|
"sourceRpm",
|
||||||
|
"size",
|
||||||
|
"license",
|
||||||
|
"vendor",
|
||||||
|
"files"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"epoch": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"architecture": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sourceRpm": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vendor": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/RpmdbFileRecord"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Schema": {
|
||||||
|
"required": [
|
||||||
|
"version",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"SearchResult": {
|
||||||
|
"required": [
|
||||||
|
"classification",
|
||||||
|
"lineNumber",
|
||||||
|
"lineOffset",
|
||||||
|
"seekPosition",
|
||||||
|
"length"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"classification": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lineNumber": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"lineOffset": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"seekPosition": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"length": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Secrets": {
|
||||||
|
"required": [
|
||||||
|
"location",
|
||||||
|
"secrets"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"location": {
|
||||||
|
"$ref": "#/definitions/Location"
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"items": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"$ref": "#/definitions/SearchResult"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Source": {
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"target"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true,
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,13 +8,16 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
md5sumsExt = ".md5sums"
|
md5sumsExt = ".md5sums"
|
||||||
|
conffilesExt = ".conffiles"
|
||||||
docsPath = "/usr/share/doc"
|
docsPath = "/usr/share/doc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -56,34 +59,23 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, error)
|
|||||||
p.FoundBy = c.Name()
|
p.FoundBy = c.Name()
|
||||||
p.Locations = []source.Location{dbLocation}
|
p.Locations = []source.Location{dbLocation}
|
||||||
|
|
||||||
metadata := p.Metadata.(pkg.DpkgMetadata)
|
// the current entry only has what may have been listed in the status file, however, there are additional
|
||||||
|
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
|
||||||
|
// together.
|
||||||
|
mergeFileListing(resolver, dbLocation, p)
|
||||||
|
|
||||||
md5Reader, md5Location, err := fetchMd5Contents(resolver, dbLocation, p)
|
// fetch additional data from the copyright file to derive the license information
|
||||||
if err != nil {
|
addLicenses(resolver, dbLocation, p)
|
||||||
return nil, fmt.Errorf("unable to find dpkg md5 contents: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if md5Reader != nil {
|
results = append(results, pkgs...)
|
||||||
// attach the file list
|
|
||||||
metadata.Files = parseDpkgMD5Info(md5Reader)
|
|
||||||
|
|
||||||
// keep a record of the file where this was discovered
|
|
||||||
if md5Location != nil {
|
|
||||||
p.Locations = append(p.Locations, *md5Location)
|
|
||||||
}
|
}
|
||||||
} else {
|
return results, nil
|
||||||
// ensure the file list is an empty collection (not nil)
|
|
||||||
metadata.Files = make([]pkg.DpkgFileRecord, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// persist alterations
|
func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
||||||
p.Metadata = metadata
|
|
||||||
|
|
||||||
// get license information from the copyright file
|
// get license information from the copyright file
|
||||||
copyrightReader, copyrightLocation, err := fetchCopyrightContents(resolver, dbLocation, p)
|
copyrightReader, copyrightLocation := fetchCopyrightContents(resolver, dbLocation, p)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to find dpkg copyright contents: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if copyrightReader != nil {
|
if copyrightReader != nil {
|
||||||
// attach the licenses
|
// attach the licenses
|
||||||
@ -96,53 +88,137 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, pkgs...)
|
func mergeFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
||||||
|
metadata := p.Metadata.(pkg.DpkgMetadata)
|
||||||
|
|
||||||
|
// get file listing (package files + additional config files)
|
||||||
|
files, infoLocations := getAdditionalFileListing(resolver, dbLocation, p)
|
||||||
|
loopNewFiles:
|
||||||
|
for _, newFile := range files {
|
||||||
|
for _, existingFile := range metadata.Files {
|
||||||
|
if existingFile.Path == newFile.Path {
|
||||||
|
// skip adding this file since it already exists
|
||||||
|
continue loopNewFiles
|
||||||
}
|
}
|
||||||
return results, nil
|
}
|
||||||
|
metadata.Files = append(metadata.Files, newFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.Reader, *source.Location, error) {
|
// sort files by path
|
||||||
|
sort.SliceStable(metadata.Files, func(i, j int) bool {
|
||||||
|
return metadata.Files[i].Path < metadata.Files[j].Path
|
||||||
|
})
|
||||||
|
|
||||||
|
// persist alterations
|
||||||
|
p.Metadata = metadata
|
||||||
|
|
||||||
|
// persist location information from each new source of information
|
||||||
|
p.Locations = append(p.Locations, infoLocations...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []source.Location) {
|
||||||
|
// ensure the default value for a collection is never nil since this may be shown as JSON
|
||||||
|
var files = make([]pkg.DpkgFileRecord, 0)
|
||||||
|
var locations []source.Location
|
||||||
|
|
||||||
|
md5Reader, md5Location := fetchMd5Contents(resolver, dbLocation, p)
|
||||||
|
|
||||||
|
if md5Reader != nil {
|
||||||
|
// attach the file list
|
||||||
|
files = append(files, parseDpkgMD5Info(md5Reader)...)
|
||||||
|
|
||||||
|
// keep a record of the file where this was discovered
|
||||||
|
if md5Location != nil {
|
||||||
|
locations = append(locations, *md5Location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conffilesReader, conffilesLocation := fetchConffileContents(resolver, dbLocation, p)
|
||||||
|
|
||||||
|
if conffilesReader != nil {
|
||||||
|
// attach the file list
|
||||||
|
files = append(files, parseDpkgConffileInfo(md5Reader)...)
|
||||||
|
|
||||||
|
// keep a record of the file where this was discovered
|
||||||
|
if conffilesLocation != nil {
|
||||||
|
locations = append(locations, *conffilesLocation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, locations
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchMd5Contents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
||||||
|
var md5Reader io.ReadCloser
|
||||||
|
var err error
|
||||||
|
|
||||||
parentPath := filepath.Dir(dbLocation.RealPath)
|
parentPath := filepath.Dir(dbLocation.RealPath)
|
||||||
|
|
||||||
// look for /var/lib/dpkg/info/NAME:ARCH.md5sums
|
// look for /var/lib/dpkg/info/NAME:ARCH.md5sums
|
||||||
name := md5Key(p)
|
name := md5Key(p)
|
||||||
md5SumLocation := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+md5sumsExt))
|
location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+md5sumsExt))
|
||||||
|
|
||||||
if md5SumLocation == nil {
|
if location == nil {
|
||||||
// the most specific key did not work, fallback to just the name
|
// the most specific key did not work, fallback to just the name
|
||||||
// look for /var/lib/dpkg/info/NAME.md5sums
|
// look for /var/lib/dpkg/info/NAME.md5sums
|
||||||
md5SumLocation = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+md5sumsExt))
|
location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+md5sumsExt))
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is unexpected, but not a show-stopper
|
// this is unexpected, but not a show-stopper
|
||||||
if md5SumLocation == nil {
|
if location != nil {
|
||||||
return nil, nil, nil
|
md5Reader, err = resolver.FileContentsByLocation(*location)
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := resolver.FileContentsByLocation(*md5SumLocation)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to fetch deb md5 contents (%+v): %w", p, err)
|
log.Warnf("failed to fetch deb md5 contents (package=%s): %+v", p.Name, err)
|
||||||
}
|
}
|
||||||
return reader, md5SumLocation, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.Reader, *source.Location, error) {
|
return md5Reader, location
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchConffileContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
||||||
|
var reader io.ReadCloser
|
||||||
|
var err error
|
||||||
|
|
||||||
|
parentPath := filepath.Dir(dbLocation.RealPath)
|
||||||
|
|
||||||
|
// look for /var/lib/dpkg/info/NAME:ARCH.conffiles
|
||||||
|
name := md5Key(p)
|
||||||
|
location := resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", name+conffilesExt))
|
||||||
|
|
||||||
|
if location == nil {
|
||||||
|
// the most specific key did not work, fallback to just the name
|
||||||
|
// look for /var/lib/dpkg/info/NAME.conffiles
|
||||||
|
location = resolver.RelativeFileByPath(dbLocation, path.Join(parentPath, "info", p.Name+conffilesExt))
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is unexpected, but not a show-stopper
|
||||||
|
if location != nil {
|
||||||
|
reader, err = resolver.FileContentsByLocation(*location)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to fetch deb conffiles contents (package=%s): %+v", p.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader, location
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchCopyrightContents(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) (io.ReadCloser, *source.Location) {
|
||||||
// look for /usr/share/docs/NAME/copyright files
|
// look for /usr/share/docs/NAME/copyright files
|
||||||
name := p.Name
|
name := p.Name
|
||||||
copyrightPath := path.Join(docsPath, name, "copyright")
|
copyrightPath := path.Join(docsPath, name, "copyright")
|
||||||
copyrightLocation := resolver.RelativeFileByPath(dbLocation, copyrightPath)
|
location := resolver.RelativeFileByPath(dbLocation, copyrightPath)
|
||||||
|
|
||||||
// we may not have a copyright file for each package, ignore missing files
|
// we may not have a copyright file for each package, ignore missing files
|
||||||
if copyrightLocation == nil {
|
if location == nil {
|
||||||
return nil, nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := resolver.FileContentsByLocation(*copyrightLocation)
|
reader, err := resolver.FileContentsByLocation(*location)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to fetch deb copyright contents (%+v): %w", p, err)
|
log.Warnf("failed to fetch deb copyright contents (package=%s): %w", p.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return reader, copyrightLocation, nil
|
return reader, location
|
||||||
}
|
}
|
||||||
|
|
||||||
func md5Key(p *pkg.Package) string {
|
func md5Key(p *pkg.Package) string {
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
@ -19,7 +21,12 @@ func TestDpkgCataloger(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "go-case",
|
name: "go-case",
|
||||||
sources: map[string][]string{
|
sources: map[string][]string{
|
||||||
"libpam-runtime": {"/var/lib/dpkg/status", "/var/lib/dpkg/info/libpam-runtime.md5sums", "/usr/share/doc/libpam-runtime/copyright"},
|
"libpam-runtime": {
|
||||||
|
"/var/lib/dpkg/status",
|
||||||
|
"/var/lib/dpkg/info/libpam-runtime.md5sums",
|
||||||
|
"/var/lib/dpkg/info/libpam-runtime.conffiles",
|
||||||
|
"/usr/share/doc/libpam-runtime/copyright",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
expected: []pkg.Package{
|
expected: []pkg.Package{
|
||||||
{
|
{
|
||||||
@ -37,10 +44,38 @@ func TestDpkgCataloger(t *testing.T) {
|
|||||||
Maintainer: "Steve Langasek <vorlon@debian.org>",
|
Maintainer: "Steve Langasek <vorlon@debian.org>",
|
||||||
InstalledSize: 1016,
|
InstalledSize: 1016,
|
||||||
Files: []pkg.DpkgFileRecord{
|
Files: []pkg.DpkgFileRecord{
|
||||||
{Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", MD5: "55f905631797551d4d936a34c7e73474"},
|
{
|
||||||
{Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", MD5: "cede84bda30d2380217f97753c8ccf3a"},
|
Path: "/etc/pam.conf",
|
||||||
{Path: "/usr/share/doc/zlib1g/changelog.gz", MD5: "f3c9dafa6da7992c47328b4464f6d122"},
|
Digest: &file.Digest{
|
||||||
{Path: "/usr/share/doc/zlib1g/copyright", MD5: "a4fae96070439a5209a62ae5b8017ab2"},
|
Algorithm: "md5",
|
||||||
|
Value: "87fc76f18e98ee7d3848f6b81b3391e5",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/pam.d/other",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "31aa7f2181889ffb00b87df4126d1701",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "55f905631797551d4d936a34c7e73474",
|
||||||
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "cede84bda30d2380217f97753c8ccf3a",
|
||||||
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/changelog.gz", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "f3c9dafa6da7992c47328b4464f6d122",
|
||||||
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/copyright", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "a4fae96070439a5209a62ae5b8017ab2",
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,12 +5,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseDpkgMD5Info(reader io.Reader) []pkg.DpkgFileRecord {
|
func parseDpkgMD5Info(reader io.Reader) (findings []pkg.DpkgFileRecord) {
|
||||||
// we must preallocate to ensure the resulting struct does not have null
|
|
||||||
var findings = make([]pkg.DpkgFileRecord, 0)
|
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@ -23,9 +22,53 @@ func parseDpkgMD5Info(reader io.Reader) []pkg.DpkgFileRecord {
|
|||||||
}
|
}
|
||||||
findings = append(findings, pkg.DpkgFileRecord{
|
findings = append(findings, pkg.DpkgFileRecord{
|
||||||
Path: path,
|
Path: path,
|
||||||
MD5: strings.TrimSpace(fields[0]),
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: strings.TrimSpace(fields[0]),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return findings
|
return findings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseDpkgConffileInfo(reader io.Reader) (findings []pkg.DpkgFileRecord) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.Trim(scanner.Text(), " \n")
|
||||||
|
fields := strings.SplitN(line, " ", 2)
|
||||||
|
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var path string
|
||||||
|
if len(fields) >= 1 {
|
||||||
|
path = strings.TrimSpace(fields[0])
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
path = "/" + path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var digest *file.Digest
|
||||||
|
if len(fields) >= 2 {
|
||||||
|
digest = &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: strings.TrimSpace(fields[1]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path != "" {
|
||||||
|
record := pkg.DpkgFileRecord{
|
||||||
|
Path: path,
|
||||||
|
IsConfigFile: true,
|
||||||
|
}
|
||||||
|
if digest != nil {
|
||||||
|
record.Digest = digest
|
||||||
|
}
|
||||||
|
findings = append(findings, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findings
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
@ -17,10 +19,22 @@ func TestMD5SumInfoParsing(t *testing.T) {
|
|||||||
{
|
{
|
||||||
fixture: "test-fixtures/info/zlib1g.md5sums",
|
fixture: "test-fixtures/info/zlib1g.md5sums",
|
||||||
expected: []pkg.DpkgFileRecord{
|
expected: []pkg.DpkgFileRecord{
|
||||||
{Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", MD5: "55f905631797551d4d936a34c7e73474"},
|
{Path: "/lib/x86_64-linux-gnu/libz.so.1.2.11", Digest: &file.Digest{
|
||||||
{Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", MD5: "cede84bda30d2380217f97753c8ccf3a"},
|
Algorithm: "md5",
|
||||||
{Path: "/usr/share/doc/zlib1g/changelog.gz", MD5: "f3c9dafa6da7992c47328b4464f6d122"},
|
Value: "55f905631797551d4d936a34c7e73474",
|
||||||
{Path: "/usr/share/doc/zlib1g/copyright", MD5: "a4fae96070439a5209a62ae5b8017ab2"},
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/changelog.Debian.gz", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "cede84bda30d2380217f97753c8ccf3a",
|
||||||
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/changelog.gz", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "f3c9dafa6da7992c47328b4464f6d122",
|
||||||
|
}},
|
||||||
|
{Path: "/usr/share/doc/zlib1g/copyright", Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "a4fae96070439a5209a62ae5b8017ab2",
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -55,3 +69,52 @@ func TestMD5SumInfoParsing(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConffileInfoParsing(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
fixture string
|
||||||
|
expected []pkg.DpkgFileRecord
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
fixture: "test-fixtures/info/util-linux.conffiles",
|
||||||
|
expected: []pkg.DpkgFileRecord{
|
||||||
|
{Path: "/etc/default/hwclock", IsConfigFile: true},
|
||||||
|
{Path: "/etc/init.d/hwclock.sh", IsConfigFile: true},
|
||||||
|
{Path: "/etc/pam.d/runuser", IsConfigFile: true},
|
||||||
|
{Path: "/etc/pam.d/runuser-l", IsConfigFile: true},
|
||||||
|
{Path: "/etc/pam.d/su", IsConfigFile: true},
|
||||||
|
{Path: "/etc/pam.d/su-l", IsConfigFile: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.fixture, func(t *testing.T) {
|
||||||
|
file, err := os.Open(test.fixture)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unable to read: ", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := file.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("closing file failed:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
actual := parseDpkgConffileInfo(file)
|
||||||
|
|
||||||
|
if len(actual) != len(test.expected) {
|
||||||
|
for _, a := range actual {
|
||||||
|
t.Logf(" %+v", a)
|
||||||
|
}
|
||||||
|
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(test.expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
diffs := deep.Equal(actual, test.expected)
|
||||||
|
for _, d := range diffs {
|
||||||
|
t.Errorf("diff: %+v", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -15,7 +15,10 @@ import (
|
|||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errEndOfPackages = fmt.Errorf("no more packages to read")
|
var (
|
||||||
|
errEndOfPackages = fmt.Errorf("no more packages to read")
|
||||||
|
sourceRegexp = regexp.MustCompile(`(?P<name>\S+)( \((?P<version>.*)\))?`)
|
||||||
|
)
|
||||||
|
|
||||||
// parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed.
|
// parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed.
|
||||||
func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
|
func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
|
||||||
@ -48,20 +51,52 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader.
|
// parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader.
|
||||||
// nolint:funlen
|
func parseDpkgStatusEntry(reader *bufio.Reader) (pkg.DpkgMetadata, error) {
|
||||||
func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err error) {
|
|
||||||
dpkgFields := make(map[string]interface{})
|
|
||||||
var retErr error
|
var retErr error
|
||||||
|
dpkgFields, err := extractAllFields(reader)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, errEndOfPackages) {
|
||||||
|
return pkg.DpkgMetadata{}, err
|
||||||
|
}
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := pkg.DpkgMetadata{
|
||||||
|
// ensure the default value for a collection is never nil since this may be shown as JSON
|
||||||
|
Files: make([]pkg.DpkgFileRecord, 0),
|
||||||
|
}
|
||||||
|
err = mapstructure.Decode(dpkgFields, &entry)
|
||||||
|
if err != nil {
|
||||||
|
return pkg.DpkgMetadata{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, version := extractSourceVersion(entry.Source)
|
||||||
|
if version != "" {
|
||||||
|
entry.SourceVersion = version
|
||||||
|
entry.Source = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// there may be an optional conffiles section that we should persist as files
|
||||||
|
if conffilesSection, exists := dpkgFields["Conffiles"]; exists && conffilesSection != nil {
|
||||||
|
if sectionStr, ok := conffilesSection.(string); ok {
|
||||||
|
entry.Files = parseDpkgConffileInfo(strings.NewReader(sectionStr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry, retErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) {
|
||||||
|
dpkgFields := make(map[string]interface{})
|
||||||
var key string
|
var key string
|
||||||
|
|
||||||
for {
|
for {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
retErr = errEndOfPackages
|
return dpkgFields, errEndOfPackages
|
||||||
break
|
|
||||||
}
|
}
|
||||||
return pkg.DpkgMetadata{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
line = strings.TrimRight(line, "\n")
|
line = strings.TrimRight(line, "\n")
|
||||||
@ -79,12 +114,12 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err
|
|||||||
case strings.HasPrefix(line, " "):
|
case strings.HasPrefix(line, " "):
|
||||||
// a field-body continuation
|
// a field-body continuation
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return pkg.DpkgMetadata{}, fmt.Errorf("no match for continuation: line: '%s'", line)
|
return nil, fmt.Errorf("no match for continuation: line: '%s'", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
val, ok := dpkgFields[key]
|
val, ok := dpkgFields[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return pkg.DpkgMetadata{}, fmt.Errorf("no previous key exists, expecting: %s", key)
|
return nil, fmt.Errorf("no previous key exists, expecting: %s", key)
|
||||||
}
|
}
|
||||||
// concatenate onto previous value
|
// concatenate onto previous value
|
||||||
val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line))
|
val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line))
|
||||||
@ -94,36 +129,18 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err err
|
|||||||
var val interface{}
|
var val interface{}
|
||||||
key, val, err = handleNewKeyValue(line)
|
key, val, err = handleNewKeyValue(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pkg.DpkgMetadata{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := dpkgFields[key]; ok {
|
if _, ok := dpkgFields[key]; ok {
|
||||||
return pkg.DpkgMetadata{}, fmt.Errorf("duplicate key discovered: %s", key)
|
return nil, fmt.Errorf("duplicate key discovered: %s", key)
|
||||||
}
|
}
|
||||||
dpkgFields[key] = val
|
dpkgFields[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return dpkgFields, nil
|
||||||
err = mapstructure.Decode(dpkgFields, &entry)
|
|
||||||
if err != nil {
|
|
||||||
return pkg.DpkgMetadata{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
name, version := extractSourceVersion(entry.Source)
|
|
||||||
if version != "" {
|
|
||||||
entry.SourceVersion = version
|
|
||||||
entry.Source = name
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry, retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// match examples:
|
|
||||||
// "a-thing (1.2.3)" name="a-thing" version="1.2.3"
|
|
||||||
// "a-thing" name="a-thing" version=""
|
|
||||||
// "" name="" version=""
|
|
||||||
var sourceRegexp = regexp.MustCompile(`(?P<name>\S+)( \((?P<version>.*)\))?`)
|
|
||||||
|
|
||||||
// If the source entry string is of the form "<name> (<version>)" then parse and return the components, if
|
// If the source entry string is of the form "<name> (<version>)" then parse and return the components, if
|
||||||
// of the "<name>" form, then return name and nil
|
// of the "<name>" form, then return name and nil
|
||||||
func extractSourceVersion(source string) (string, string) {
|
func extractSourceVersion(source string) (string, string) {
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
@ -30,6 +32,40 @@ func TestSinglePackage(t *testing.T) {
|
|||||||
Architecture: "amd64",
|
Architecture: "amd64",
|
||||||
InstalledSize: 4064,
|
InstalledSize: 4064,
|
||||||
Maintainer: "APT Development Team <deity@lists.debian.org>",
|
Maintainer: "APT Development Team <deity@lists.debian.org>",
|
||||||
|
Files: []pkg.DpkgFileRecord{
|
||||||
|
{
|
||||||
|
Path: "/etc/apt/apt.conf.d/01autoremove",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "76120d358bc9037bb6358e737b3050b5",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/cron.daily/apt-compat",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "49e9b2cfa17849700d4db735d04244f3",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/kernel/postinst.d/apt-auto-removal",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "4ad976a68f045517cf4696cec7b8aa3a",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/logrotate.d/apt",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "179f2ed4f85cbaca12fa3d69c2a4a1c3",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -74,6 +110,7 @@ func TestMultiplePackages(t *testing.T) {
|
|||||||
Architecture: "all",
|
Architecture: "all",
|
||||||
InstalledSize: 3036,
|
InstalledSize: 3036,
|
||||||
Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>",
|
Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>",
|
||||||
|
Files: []pkg.DpkgFileRecord{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Package: "util-linux",
|
Package: "util-linux",
|
||||||
@ -81,6 +118,56 @@ func TestMultiplePackages(t *testing.T) {
|
|||||||
Architecture: "amd64",
|
Architecture: "amd64",
|
||||||
InstalledSize: 4327,
|
InstalledSize: 4327,
|
||||||
Maintainer: "LaMont Jones <lamont@debian.org>",
|
Maintainer: "LaMont Jones <lamont@debian.org>",
|
||||||
|
Files: []pkg.DpkgFileRecord{
|
||||||
|
{
|
||||||
|
Path: "/etc/default/hwclock",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "3916544450533eca69131f894db0ca12",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/init.d/hwclock.sh",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "1ca5c0743fa797ffa364db95bb8d8d8e",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/pam.d/runuser",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "b8b44b045259525e0fae9e38fdb2aeeb",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/pam.d/runuser-l",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "2106ea05877e8913f34b2c77fa02be45",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/pam.d/su",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "ce6dcfda3b190a27a455bb38a45ff34a",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/etc/pam.d/su-l",
|
||||||
|
Digest: &file.Digest{
|
||||||
|
Algorithm: "md5",
|
||||||
|
Value: "756fef5687fecc0d986e5951427b0c4f",
|
||||||
|
},
|
||||||
|
IsConfigFile: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
/etc/pam.conf
|
||||||
|
/etc/pam.d/other
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
/etc/default/hwclock
|
||||||
|
/etc/init.d/hwclock.sh
|
||||||
|
/etc/pam.d/runuser
|
||||||
|
/etc/pam.d/runuser-l
|
||||||
|
/etc/pam.d/su
|
||||||
|
/etc/pam.d/su-l
|
||||||
@ -3,6 +3,8 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/distro"
|
"github.com/anchore/syft/syft/distro"
|
||||||
"github.com/package-url/packageurl-go"
|
"github.com/package-url/packageurl-go"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
@ -28,7 +30,8 @@ type DpkgMetadata struct {
|
|||||||
// DpkgFileRecord represents a single file attributed to a debian package.
|
// DpkgFileRecord represents a single file attributed to a debian package.
|
||||||
type DpkgFileRecord struct {
|
type DpkgFileRecord struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
MD5 string `json:"md5"`
|
Digest *file.Digest `json:"digest,omitempty"`
|
||||||
|
IsConfigFile bool `json:"isConfigFile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user