Merge pull request #264 from safedep/feat/terraform-support

feat: Add automatic resolution of terraform lockfile name to custom parser type
This commit is contained in:
Abhisek Datta 2024-10-21 21:37:16 +05:30 committed by GitHub
commit 298ddbe199
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 3 deletions

View File

@ -85,12 +85,12 @@ type dependencyGraphParser func(lockfilePath string, config *ParserConfig) (*mod
// Maintain a map of lockfileAs to dependencyGraphParser // Maintain a map of lockfileAs to dependencyGraphParser
var dependencyGraphParsers map[string]dependencyGraphParser = map[string]dependencyGraphParser{ var dependencyGraphParsers map[string]dependencyGraphParser = map[string]dependencyGraphParser{
customParserTerraform: parseTerraformLockfile,
"package-lock.json": parseNpmPackageLockAsGraph, "package-lock.json": parseNpmPackageLockAsGraph,
customParserCycloneDXSBOM: parseSbomCycloneDxAsGraph, customParserCycloneDXSBOM: parseSbomCycloneDxAsGraph,
customParserTypeJavaArchive: parseJavaArchiveAsGraph, customParserTypeJavaArchive: parseJavaArchiveAsGraph,
customParserTypeJavaWebAppArchive: parseJavaArchiveAsGraph, customParserTypeJavaWebAppArchive: parseJavaArchiveAsGraph,
customParserGitHubActions: parseGithubActionWorkflowAsGraph, customParserGitHubActions: parseGithubActionWorkflowAsGraph,
customParserTerraform: parseTerraformLockfile,
} }
// Maintain a map of extension to lockfileAs // Maintain a map of extension to lockfileAs
@ -100,6 +100,13 @@ var lockfileAsMapByExtension map[string]string = map[string]string{
"war": customParserTypeJavaWebAppArchive, "war": customParserTypeJavaWebAppArchive,
} }
// Maintain a map of standard filenames to a custom parser. This has
// higher precendence that lockfile package. Graph parsers discover
// reference to this map to resolve the lockfileAs from base filename
var lockfileAsMapByPath map[string]string = map[string]string{
".terraform.lock.hcl": customParserTerraform,
}
func FindLockFileAsByExtension(extension string) (string, error) { func FindLockFileAsByExtension(extension string) (string, error) {
if lockfileAs, ok := lockfileAsMapByExtension[extension]; ok { if lockfileAs, ok := lockfileAsMapByExtension[extension]; ok {
return lockfileAs, nil return lockfileAs, nil
@ -185,6 +192,9 @@ func findGraphParser(lockfilePath, lockfileAs string) (dependencyGraphParser, st
parseAs := lockfileAs parseAs := lockfileAs
if lockfileAs == "" { if lockfileAs == "" {
parseAs = filepath.Base(lockfilePath) parseAs = filepath.Base(lockfilePath)
if _, ok := lockfileAsMapByPath[parseAs]; ok {
parseAs = lockfileAsMapByPath[parseAs]
}
} }
if _, ok := dependencyGraphParsers[parseAs]; ok { if _, ok := dependencyGraphParsers[parseAs]; ok {

View File

@ -2,13 +2,14 @@ package parser
import ( import (
"fmt" "fmt"
"github.com/hashicorp/hcl/v2/hclparse" "github.com/hashicorp/hcl/v2/hclparse"
"github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/safedep/vet/pkg/common/logger"
"github.com/safedep/vet/pkg/models" "github.com/safedep/vet/pkg/models"
) )
func parseTerraformLockfile(path string, config *ParserConfig) (*models.PackageManifest, error) { func parseTerraformLockfile(path string, config *ParserConfig) (*models.PackageManifest, error) {
// Parse the file using the HCL parser
parser := hclparse.NewParser() parser := hclparse.NewParser()
hclFile, diags := parser.ParseHCLFile(path) hclFile, diags := parser.ParseHCLFile(path)
if diags.HasErrors() { if diags.HasErrors() {
@ -19,12 +20,18 @@ func parseTerraformLockfile(path string, config *ParserConfig) (*models.PackageM
if !ok { if !ok {
return nil, fmt.Errorf("failed to assert body as hclsyntax.Body") return nil, fmt.Errorf("failed to assert body as hclsyntax.Body")
} }
manifest := models.NewPackageManifestFromLocal(path, models.EcosystemTerraform)
manifest := models.NewPackageManifestFromLocal(path, models.EcosystemTerraform)
for _, block := range body.Blocks { for _, block := range body.Blocks {
if block.Type != "provider" { if block.Type != "provider" {
continue continue
} }
if len(block.Labels) == 0 {
logger.Warnf("Terraform parser: provider block has no labels")
continue
}
providerName := block.Labels[0] // The provider name is the first label providerName := block.Labels[0] // The provider name is the first label
providerVersion := "0.0.0" providerVersion := "0.0.0"
if versionAttr, exists := block.Body.Attributes["version"]; exists { if versionAttr, exists := block.Body.Attributes["version"]; exists {
@ -32,13 +39,16 @@ func parseTerraformLockfile(path string, config *ParserConfig) (*models.PackageM
if diags.HasErrors() { if diags.HasErrors() {
return nil, fmt.Errorf("failed to extract version: %v", diags) return nil, fmt.Errorf("failed to extract version: %v", diags)
} }
providerVersion = versionVal.AsString() providerVersion = versionVal.AsString()
} }
pkgdetails := models.NewPackageDetail(models.EcosystemTerraform, providerName, providerVersion) pkgdetails := models.NewPackageDetail(models.EcosystemTerraform, providerName, providerVersion)
packageModel := models.Package{ packageModel := models.Package{
PackageDetails: pkgdetails, PackageDetails: pkgdetails,
Depth: 0, Depth: 0,
} }
manifest.AddPackage(&packageModel) manifest.AddPackage(&packageModel)
} }