Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(misconf): resolve tf module from OpenTofu compatible registry #6743

Merged
merged 1 commit into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions pkg/iac/scanners/terraform/parser/module_retrieval.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import (
"fmt"
"io/fs"

resolvers2 "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers"
"github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers"
)

type ModuleResolver interface {
Resolve(context.Context, fs.FS, resolvers2.Options) (filesystem fs.FS, prefix string, downloadPath string, applies bool, err error)
Resolve(context.Context, fs.FS, resolvers.Options) (filesystem fs.FS, prefix string, downloadPath string, applies bool, err error)
}

var defaultResolvers = []ModuleResolver{
resolvers2.Cache,
resolvers2.Local,
resolvers2.Remote,
resolvers2.Registry,
resolvers.Cache,
resolvers.Local,
resolvers.Remote,
resolvers.Registry,
}

func resolveModule(ctx context.Context, current fs.FS, opt resolvers2.Options) (filesystem fs.FS, sourcePrefix, downloadPath string, err error) {
func resolveModule(ctx context.Context, current fs.FS, opt resolvers.Options) (filesystem fs.FS, sourcePrefix, downloadPath string, err error) {
opt.Debug("Resolving module '%s' with source: '%s'...", opt.Name, opt.Source)
for _, resolver := range defaultResolvers {
if filesystem, prefix, path, applies, err := resolver.Resolve(ctx, current, opt); err != nil {
Expand Down
22 changes: 20 additions & 2 deletions pkg/iac/scanners/terraform/parser/resolvers/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,29 @@ func (r *registryResolver) Resolve(ctx context.Context, target fs.FS, opt Option
return nil, "", "", true, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusNoContent {

// OpenTofu may return 200 with body
switch resp.StatusCode {
case http.StatusOK:
// https://opentofu.org/docs/internals/module-registry-protocol/#sample-response-1
var downloadResponse struct {
Location string `json:"location"`
}
if err := json.NewDecoder(resp.Body).Decode(&downloadResponse); err != nil {
return nil, "", "", true, fmt.Errorf("failed to decode download response: %w", err)
}

opt.Source = downloadResponse.Location
case http.StatusNoContent:
opt.Source = resp.Header.Get("X-Terraform-Get")
default:
return nil, "", "", true, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

opt.Source = resp.Header.Get("X-Terraform-Get")
if opt.Source == "" {
return nil, "", "", true, fmt.Errorf("no source was found for the registry at %s", hostname)
}

opt.Debug("Module '%s' resolved via registry to new source: '%s'", opt.Name, opt.Source)
opt.RelativePath = relativePath
filesystem, prefix, downloadPath, _, err = Remote.Resolve(ctx, target, opt)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package resolvers_test

import (
"context"
"io/fs"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"

"github.com/aquasecurity/trivy/pkg/iac/scanners/terraform/parser/resolvers"
)

func TestResolveModuleFromOpenTofuRegistry(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}

fsys, _, path, _, err := resolvers.Registry.Resolve(context.Background(), nil, resolvers.Options{
Source: "registry.opentofu.org/terraform-aws-modules/s3-bucket/aws",
RelativePath: "test",
Name: "bucket",
Version: "4.1.2",
AllowDownloads: true,
SkipCache: true,
})
require.NoError(t, err)

_, err = fs.Stat(fsys, filepath.Join(path, "main.tf"))
require.NoError(t, err)
}