feat(cataloger): add a terraform provider cataloger (#3378)

* feat(cataloger): add a terraform provider cataloger
* chore: bump schema from 16.0.19 -> 16.0.20
------
Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
This commit is contained in:
Thomas Gosteli 2025-01-21 20:44:54 +01:00 committed by GitHub
parent 1906c179d0
commit c10e904c28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 3131 additions and 14 deletions

View File

@ -146,6 +146,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
- Rust (cargo.lock)
- Swift (cocoapods, swift-package-manager)
- Wordpress plugins
- Terraform providers (.terraform.lock.hcl)
## Documentation

View File

@ -418,6 +418,14 @@ var dirOnlyTestCases = []testCase{
"ocaml-base-compiler": "4.14.0",
},
},
{
name: "find terraform packages",
pkgType: pkg.TerraformPkg,
pkgLanguage: pkg.Go,
pkgInfo: map[string]string{
"registry.terraform.io/hashicorp/aws": "5.72.1",
},
},
}
var commonTestCases = []testCase{

View File

@ -82,6 +82,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.OpamPkg))
definedPkgs.Remove(string(pkg.GithubActionPkg))
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
definedPkgs.Remove(string(pkg.TerraformPkg))
var cases []testCase
cases = append(cases, commonTestCases...)

View File

@ -0,0 +1,25 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.72.1"
constraints = ">= 5.72.0"
hashes = [
"h1:jhd5O5o0CfZCNEwwN0EiDAzb7ApuFrtxJqa6HXW4EKE=",
"zh:0dea6843836e926d33469b48b948744079023816d16a2ff7666bcfb6aa3522d4",
"zh:195fa9513f75800a0d62797ebec75ee73e9b8c28d713fe9b63d3b1d1eec129b3",
"zh:1ed92f3961715bf0e024bcde3c12dfbdc50b00c1f8a43cc00802cfc45a256208",
"zh:2ac687e3a52606466cae4a6813e81d923042488df88d2424e28d3f8530f091bb",
"zh:32e7ca75f9314557daada3c44628fe1f3bf964a4f833bfb4b2295d833fe64b6f",
"zh:374ee0e6b4327cc6ef666908ce5d6450a3a56e90cd2b785e83c2bcfc100021d2",
"zh:5500fd6fdac44f96411fcf9c6d01691159ec35455ed127eb4c3a498e1cc92a64",
"zh:723a2dc4b064c12e7ee62ad4fbfd72fa5e025206ea47b735994ef53f3c373152",
"zh:89d97b87605f1d734f27e642567cbecf785b521af8ea81dac55c77ccde876221",
"zh:951ee1e5731e8d65d521d71b95927e55055b3c4656eef6d46fa580a63328befc",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9b2b362470b64ec227b2da64762ab8bc4111c6b80365fd9d82fc5e1e33f44038",
"zh:aa6e57d0cb974ff0da5dee5d43ad2745cbbc4a2b507d4c799839b9fa96daf688",
"zh:ba0d14c4a6b7aa844a830d47c0bf995b632e37f0795394b5b60c638b62b7fc03",
"zh:c9764065a9c5d324db0b02bd201b9e3a2118e49c4960884acdeea377173302e9",
]
}

7
go.mod
View File

@ -90,6 +90,7 @@ require (
github.com/OneOfOne/xxhash v1.2.8
github.com/adrg/xdg v0.5.3
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51
github.com/hashicorp/hcl/v2 v2.22.0
github.com/magiconair/properties v1.8.9
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
)
@ -104,8 +105,11 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
@ -172,6 +176,7 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
@ -223,6 +228,7 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/zclconf/go-cty v1.13.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
@ -236,6 +242,7 @@ require (
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
google.golang.org/grpc v1.67.3 // indirect

14
go.sum
View File

@ -88,6 +88,8 @@ github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+Ui
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -122,6 +124,10 @@ github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOL
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg=
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI490FF0a7zuvxOxen52ddygCfNVjP0XOCMl+M=
@ -451,6 +457,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
@ -559,6 +567,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -806,6 +816,10 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=

View File

@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "16.0.19"
JSONSchemaVersion = "16.0.20"
)

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at 2024-08-20 11:33:49.349625 -0400 EDT m=+0.383911876
// This file was generated by robots at 2024-11-29 09:22:05.594787 +0100 CET m=+2.414400043
// using data from https://spdx.org/licenses/licenses.json
package spdxlicense

View File

@ -31,6 +31,7 @@ import (
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
"github.com/anchore/syft/syft/pkg/cataloger/swift"
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
"github.com/anchore/syft/syft/pkg/cataloger/terraform"
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
)
@ -152,5 +153,6 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
),
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.19/document",
"$id": "anchore.io/schema/syft/json/16.0.20/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@ -1782,6 +1782,9 @@
{
"$ref": "#/$defs/SwiplpackPackage"
},
{
"$ref": "#/$defs/TerraformLockProviderEntry"
},
{
"$ref": "#/$defs/WordpressPluginEntry"
}
@ -2720,6 +2723,32 @@
"dependencies"
]
},
"TerraformLockProviderEntry": {
"properties": {
"url": {
"type": "string"
},
"constraints": {
"type": "string"
},
"version": {
"type": "string"
},
"hashes": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"url",
"constraints",
"version",
"hashes"
]
},
"WordpressPluginEntry": {
"properties": {
"pluginInstallDirectory": {

View File

@ -372,6 +372,14 @@ func Test_OriginatorSupplier(t *testing.T) {
originator: "",
supplier: "",
},
{
name: "from terraform lock",
input: pkg.Package{
Metadata: pkg.TerraformLockProviderEntry{},
},
originator: "",
supplier: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {

View File

@ -70,6 +70,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from GitHub Actions workflow file or composite action file"
case pkg.WordpressPluginPkg:
answer = "acquired package info from found wordpress plugin PHP source files"
case pkg.TerraformPkg:
answer = "acquired package info from Terraform dependency lock file"
default:
answer = "acquired package info from the following paths"
}

View File

@ -303,6 +303,14 @@ func Test_SourceInfo(t *testing.T) {
"acquired package info from found wordpress plugin PHP source files",
},
},
{
input: pkg.Package{
Type: pkg.TerraformPkg,
},
expected: []string{
"acquired package info from Terraform dependency lock file",
},
},
}
var pkgTypes []pkg.Type
for _, test := range tests {

View File

@ -53,6 +53,7 @@ func AllTypes() []any {
pkg.RustCargoLockEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.SwiplPackEntry{},
pkg.TerraformLockProviderEntry{},
pkg.WordpressPluginEntry{},
pkg.YarnLockEntry{},
}

View File

@ -109,6 +109,7 @@ var jsonTypes = makeJSONTypes(
jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"),
jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"),
)

View File

@ -0,0 +1,11 @@
package terraform
import (
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
func NewLockCataloger() pkg.Cataloger {
return generic.NewCataloger("terraform-lock-cataloger").
WithParserByGlobs(parseTerraformLock, "**/.terraform.lock.hcl")
}

View File

@ -0,0 +1,103 @@
package terraform
import (
"path/filepath"
"testing"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func TestTerraformCataloger(t *testing.T) {
c := NewLockCataloger()
fileLoc := file.NewLocation(".terraform.lock.hcl")
location := fileLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)
awsProviderPkg := pkg.Package{
Name: "registry.terraform.io/hashicorp/aws",
Version: "5.72.1",
FoundBy: "terraform-lock-cataloger",
Locations: file.NewLocationSet(location),
Type: pkg.TerraformPkg,
Language: pkg.Go,
Metadata: pkg.TerraformLockProviderEntry{
URL: "registry.terraform.io/hashicorp/aws",
Version: "5.72.1",
Constraints: "> 5.72.0",
Hashes: []string{
"h1:jhd5O5o0CfZCNEwwN0EiDAzb7ApuFrtxJqa6HXW4EKE=",
"zh:0dea6843836e926d33469b48b948744079023816d16a2ff7666bcfb6aa3522d4",
"zh:195fa9513f75800a0d62797ebec75ee73e9b8c28d713fe9b63d3b1d1eec129b3",
"zh:1ed92f3961715bf0e024bcde3c12dfbdc50b00c1f8a43cc00802cfc45a256208",
"zh:2ac687e3a52606466cae4a6813e81d923042488df88d2424e28d3f8530f091bb",
"zh:32e7ca75f9314557daada3c44628fe1f3bf964a4f833bfb4b2295d833fe64b6f",
"zh:374ee0e6b4327cc6ef666908ce5d6450a3a56e90cd2b785e83c2bcfc100021d2",
"zh:5500fd6fdac44f96411fcf9c6d01691159ec35455ed127eb4c3a498e1cc92a64",
"zh:723a2dc4b064c12e7ee62ad4fbfd72fa5e025206ea47b735994ef53f3c373152",
"zh:89d97b87605f1d734f27e642567cbecf785b521af8ea81dac55c77ccde876221",
"zh:951ee1e5731e8d65d521d71b95927e55055b3c4656eef6d46fa580a63328befc",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9b2b362470b64ec227b2da64762ab8bc4111c6b80365fd9d82fc5e1e33f44038",
"zh:aa6e57d0cb974ff0da5dee5d43ad2745cbbc4a2b507d4c799839b9fa96daf688",
"zh:ba0d14c4a6b7aa844a830d47c0bf995b632e37f0795394b5b60c638b62b7fc03",
"zh:c9764065a9c5d324db0b02bd201b9e3a2118e49c4960884acdeea377173302e9",
},
},
}
awsProviderPkg.SetID()
gcpProviderPkg := pkg.Package{
Name: "registry.terraform.io/hashicorp/google",
Version: "6.8.0",
FoundBy: "terraform-lock-cataloger",
Locations: file.NewLocationSet(location),
Type: pkg.TerraformPkg,
Language: pkg.Go,
Metadata: pkg.TerraformLockProviderEntry{
URL: "registry.terraform.io/hashicorp/google",
Version: "6.8.0",
Constraints: "6.8.0",
Hashes: []string{
"h1:GlCaVPk6eKMg2ZbRY7C5tUeHGNIABT+qFtMl8+XWZHM=",
"zh:1b78f4451f1617092eb6891c9c13eda79671060601c40947feea6794c732157a",
"zh:4c6d7231ce32c6ff2a98218ef363c133d27d423b009354e7fe18459d9feb41d4",
"zh:6ae0112e9c733ab6c72436a334ffe3f197a613bb04f49538462b83b236d37a2d",
"zh:8bd5651838ad674e0a173a453b76c80b94d08ebcb8ea0b6263ce6da0599b42f5",
"zh:94ee7bcd77b0b7c2777113e35282da014e61e813fe46c058a49bf3d616fecdf4",
"zh:c0bf014422c2971985d34ad45ddb6aa737373398f83b325884ea5608ac1264aa",
"zh:c2cbbf0c249c3d1842ad0ad77fb7ef85bd3e92c688618c4087173bc1d69cd098",
"zh:cefa3e06cb353d08b83dafa6135cd78e17540ae735b7c5687833cc1925c3fd8e",
"zh:d20bc0216bf7f054f6318467d3902ced05e9f0bfa500ee55bf43b1b41ef0b854",
"zh:e54ad5959e53b9e9acafc243d6f4039ab5005cec32c7435a122da964888d184c",
"zh:e833c8de147268b3ffc14c60915eccb9347ade5f25b37b3771240a4d68b6aac4",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
},
},
}
gcpProviderPkg.SetID()
tests := []struct {
name string
expected []pkg.Package
}{
{
name: "two-providers",
expected: []pkg.Package{
awsProviderPkg,
gcpProviderPkg,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
WithResolver(fileresolver.NewFromUnindexedDirectory(filepath.Join("test-fixtures", tt.name))).
Expects(tt.expected, nil).
TestCataloger(t, c)
})
}
}

View File

@ -0,0 +1,53 @@
package terraform
import (
"context"
"fmt"
"io"
"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
type terraformLockFile struct {
Providers []pkg.TerraformLockProviderEntry `hcl:"provider,block"`
}
func parseTerraformLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var lockFile terraformLockFile
contents, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to read terraform lock file: %w", err)
}
err = hclsimple.Decode(reader.RealPath, contents, nil, &lockFile)
if err != nil {
return nil, nil, fmt.Errorf("failed to decode terraform lock file: %w", err)
}
pkgs := make([]pkg.Package, 0, len(lockFile.Providers))
for _, provider := range lockFile.Providers {
p := pkg.Package{
Name: provider.URL,
Version: provider.Version,
Locations: file.NewLocationSet(reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Licenses: pkg.NewLicenseSet(), // TODO: license could be found in .terraform/providers/${name}/${version}/${arch}/LICENSE.txt
Language: pkg.Go,
Type: pkg.TerraformPkg,
Metadata: provider,
// TODO: PURL omitted from package creation until the following issue resolved
// https://github.com/package-url/purl-spec/issues/369
}
p.SetID()
pkgs = append(pkgs, p)
}
return pkgs, nil, nil
}

View File

@ -0,0 +1,45 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.72.1"
constraints = "> 5.72.0"
hashes = [
"h1:jhd5O5o0CfZCNEwwN0EiDAzb7ApuFrtxJqa6HXW4EKE=",
"zh:0dea6843836e926d33469b48b948744079023816d16a2ff7666bcfb6aa3522d4",
"zh:195fa9513f75800a0d62797ebec75ee73e9b8c28d713fe9b63d3b1d1eec129b3",
"zh:1ed92f3961715bf0e024bcde3c12dfbdc50b00c1f8a43cc00802cfc45a256208",
"zh:2ac687e3a52606466cae4a6813e81d923042488df88d2424e28d3f8530f091bb",
"zh:32e7ca75f9314557daada3c44628fe1f3bf964a4f833bfb4b2295d833fe64b6f",
"zh:374ee0e6b4327cc6ef666908ce5d6450a3a56e90cd2b785e83c2bcfc100021d2",
"zh:5500fd6fdac44f96411fcf9c6d01691159ec35455ed127eb4c3a498e1cc92a64",
"zh:723a2dc4b064c12e7ee62ad4fbfd72fa5e025206ea47b735994ef53f3c373152",
"zh:89d97b87605f1d734f27e642567cbecf785b521af8ea81dac55c77ccde876221",
"zh:951ee1e5731e8d65d521d71b95927e55055b3c4656eef6d46fa580a63328befc",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9b2b362470b64ec227b2da64762ab8bc4111c6b80365fd9d82fc5e1e33f44038",
"zh:aa6e57d0cb974ff0da5dee5d43ad2745cbbc4a2b507d4c799839b9fa96daf688",
"zh:ba0d14c4a6b7aa844a830d47c0bf995b632e37f0795394b5b60c638b62b7fc03",
"zh:c9764065a9c5d324db0b02bd201b9e3a2118e49c4960884acdeea377173302e9",
]
}
provider "registry.terraform.io/hashicorp/google" {
version = "6.8.0"
constraints = "6.8.0"
hashes = [
"h1:GlCaVPk6eKMg2ZbRY7C5tUeHGNIABT+qFtMl8+XWZHM=",
"zh:1b78f4451f1617092eb6891c9c13eda79671060601c40947feea6794c732157a",
"zh:4c6d7231ce32c6ff2a98218ef363c133d27d423b009354e7fe18459d9feb41d4",
"zh:6ae0112e9c733ab6c72436a334ffe3f197a613bb04f49538462b83b236d37a2d",
"zh:8bd5651838ad674e0a173a453b76c80b94d08ebcb8ea0b6263ce6da0599b42f5",
"zh:94ee7bcd77b0b7c2777113e35282da014e61e813fe46c058a49bf3d616fecdf4",
"zh:c0bf014422c2971985d34ad45ddb6aa737373398f83b325884ea5608ac1264aa",
"zh:c2cbbf0c249c3d1842ad0ad77fb7ef85bd3e92c688618c4087173bc1d69cd098",
"zh:cefa3e06cb353d08b83dafa6135cd78e17540ae735b7c5687833cc1925c3fd8e",
"zh:d20bc0216bf7f054f6318467d3902ced05e9f0bfa500ee55bf43b1b41ef0b854",
"zh:e54ad5959e53b9e9acafc243d6f4039ab5005cec32c7435a122da964888d184c",
"zh:e833c8de147268b3ffc14c60915eccb9347ade5f25b37b3771240a4d68b6aac4",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}

9
syft/pkg/terraform.go Normal file
View File

@ -0,0 +1,9 @@
package pkg
// TerraformLockProviderEntry represents a single provider entry in a Terraform dependency lock file (.terraform.lock.hcl).
type TerraformLockProviderEntry struct {
URL string `hcl:",label" json:"url"`
Constraints string `hcl:"constraints" json:"constraints"`
Version string `hcl:"version" json:"version"`
Hashes []string `hcl:"hashes" json:"hashes"`
}

View File

@ -33,6 +33,7 @@ const (
LinuxKernelModulePkg Type = "linux-kernel-module"
NixPkg Type = "nix"
NpmPkg Type = "npm"
OpamPkg Type = "opam"
PhpComposerPkg Type = "php-composer"
PhpPeclPkg Type = "php-pecl"
PortagePkg Type = "portage"
@ -43,7 +44,7 @@ const (
RustPkg Type = "rust-crate"
SwiftPkg Type = "swift"
SwiplPackPkg Type = "swiplpack"
OpamPkg Type = "opam"
TerraformPkg Type = "terraform"
WordpressPluginPkg Type = "wordpress-plugin"
)
@ -71,6 +72,7 @@ var AllPkgs = []Type{
LinuxKernelModulePkg,
NixPkg,
NpmPkg,
OpamPkg,
PhpComposerPkg,
PhpPeclPkg,
PortagePkg,
@ -81,7 +83,7 @@ var AllPkgs = []Type{
RustPkg,
SwiftPkg,
SwiplPackPkg,
OpamPkg,
TerraformPkg,
WordpressPluginPkg,
}
@ -131,14 +133,16 @@ func (t Type) PackageURLType() string {
return packageurl.TypePyPi
case PortagePkg:
return "portage"
case LuaRocksPkg:
return packageurl.TypeLuaRocks
case NixPkg:
return "nix"
case NpmPkg:
return packageurl.TypeNPM
case OpamPkg:
return "opam"
case Rpkg:
return packageurl.TypeCran
case LuaRocksPkg:
return packageurl.TypeLuaRocks
case RpmPkg:
return packageurl.TypeRPM
case RustPkg:
@ -147,8 +151,8 @@ func (t Type) PackageURLType() string {
return packageurl.TypeSwift
case SwiplPackPkg:
return "swiplpack"
case OpamPkg:
return "opam"
case TerraformPkg:
return "terraform"
case WordpressPluginPkg:
return "wordpress-plugin"
default:
@ -170,7 +174,7 @@ func TypeFromPURL(p string) Type {
return TypeByName(ptype)
}
//nolint:funlen
//nolint:funlen,gocyclo
func TypeByName(name string) Type {
switch name {
case packageurl.TypeDebian:
@ -221,14 +225,16 @@ func TypeByName(name string) Type {
return LinuxKernelModulePkg
case "nix":
return NixPkg
case "opam":
return OpamPkg
case packageurl.TypeCran:
return Rpkg
case packageurl.TypeSwift:
return SwiftPkg
case "swiplpack":
return SwiplPackPkg
case "opam":
return OpamPkg
case "terraform":
return TerraformPkg
case "wordpress-plugin":
return WordpressPluginPkg
default:

View File

@ -128,7 +128,7 @@ func TestTypeFromPURL(t *testing.T) {
}
// testing microsoft packages and jenkins-plugins and custom binary type
// is not valid for purl at this time
// and terraform types is not valid for purl at this time
expectedTypes.Remove(string(KbPkg))
expectedTypes.Remove(string(JenkinsPluginPkg))
expectedTypes.Remove(string(PortagePkg))
@ -136,6 +136,7 @@ func TestTypeFromPURL(t *testing.T) {
expectedTypes.Remove(string(LinuxKernelModulePkg))
expectedTypes.Remove(string(GithubActionPkg), string(GithubActionWorkflowPkg))
expectedTypes.Remove(string(WordpressPluginPkg))
expectedTypes.Remove(string(TerraformPkg))
for _, test := range tests {
t.Run(string(test.expected), func(t *testing.T) {