Skip to content

Commit 6fab88d

Browse files
authored
fix(helm): properly handle multiple archived dependencies (#7782)
Signed-off-by: nikpivkin <[email protected]>
1 parent c70b6fa commit 6fab88d

File tree

8 files changed

+89
-16
lines changed

8 files changed

+89
-16
lines changed

pkg/iac/scanners/helm/parser/parser.go

+31-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io/fs"
9+
"path"
910
"path/filepath"
1011
"regexp"
1112
"sort"
@@ -46,7 +47,7 @@ type ChartFile struct {
4647
ManifestContent string
4748
}
4849

49-
func New(path string, opts ...Option) (*Parser, error) {
50+
func New(src string, opts ...Option) (*Parser, error) {
5051

5152
client := action.NewInstall(&action.Configuration{})
5253
client.DryRun = true // don't do anything
@@ -55,7 +56,7 @@ func New(path string, opts ...Option) (*Parser, error) {
5556

5657
p := &Parser{
5758
helmClient: client,
58-
ChartSource: path,
59+
ChartSource: src,
5960
logger: log.WithPrefix("helm parser"),
6061
}
6162

@@ -79,10 +80,10 @@ func New(path string, opts ...Option) (*Parser, error) {
7980
return p, nil
8081
}
8182

82-
func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error {
83-
p.workingFS = target
83+
func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, target string) error {
84+
p.workingFS = fsys
8485

85-
if err := fs.WalkDir(p.workingFS, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error {
86+
if err := fs.WalkDir(p.workingFS, filepath.ToSlash(target), func(filePath string, entry fs.DirEntry, err error) error {
8687
select {
8788
case <-ctx.Done():
8889
return ctx.Err()
@@ -95,16 +96,20 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error {
9596
return nil
9697
}
9798

98-
if detection.IsArchive(path) {
99-
tarFS, err := p.addTarToFS(path)
99+
if _, err := fs.Stat(p.workingFS, filePath); err != nil {
100+
return nil
101+
}
102+
103+
if detection.IsArchive(filePath) && !isDependencyChartArchive(p.workingFS, filePath) {
104+
tarFS, err := p.addTarToFS(filePath)
100105
if errors.Is(err, errSkipFS) {
101106
// an unpacked Chart already exists
102107
return nil
103108
} else if err != nil {
104-
return fmt.Errorf("failed to add tar %q to FS: %w", path, err)
109+
return fmt.Errorf("failed to add tar %q to FS: %w", filePath, err)
105110
}
106111

107-
targetPath := filepath.Dir(path)
112+
targetPath := filepath.Dir(filePath)
108113
if targetPath == "" {
109114
targetPath = "."
110115
}
@@ -114,7 +119,7 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error {
114119
}
115120
return nil
116121
} else {
117-
return p.addPaths(path)
122+
return p.addPaths(filePath)
118123
}
119124
}); err != nil {
120125
return fmt.Errorf("walk dir error: %w", err)
@@ -123,19 +128,29 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error {
123128
return nil
124129
}
125130

131+
func isDependencyChartArchive(fsys fs.FS, archivePath string) bool {
132+
parent := path.Dir(archivePath)
133+
if path.Base(parent) != "charts" {
134+
return false
135+
}
136+
137+
_, err := fs.Stat(fsys, path.Join(parent, "..", "Chart.yaml"))
138+
return err == nil
139+
}
140+
126141
func (p *Parser) addPaths(paths ...string) error {
127-
for _, path := range paths {
128-
if _, err := fs.Stat(p.workingFS, path); err != nil {
142+
for _, filePath := range paths {
143+
if _, err := fs.Stat(p.workingFS, filePath); err != nil {
129144
return err
130145
}
131146

132-
if strings.HasSuffix(path, "Chart.yaml") && p.rootPath == "" {
133-
if err := p.extractChartName(path); err != nil {
147+
if strings.HasSuffix(filePath, "Chart.yaml") && p.rootPath == "" {
148+
if err := p.extractChartName(filePath); err != nil {
134149
return err
135150
}
136-
p.rootPath = filepath.Dir(path)
151+
p.rootPath = filepath.Dir(filePath)
137152
}
138-
p.filepaths = append(p.filepaths, path)
153+
p.filepaths = append(p.filepaths, filePath)
139154
}
140155
return nil
141156
}

pkg/iac/scanners/helm/parser/parser_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,34 @@ func TestParseFS(t *testing.T) {
4848
}
4949
assert.Equal(t, expectedFiles, p.filepaths)
5050
})
51+
52+
t.Run("chart with multiple archived deps", func(t *testing.T) {
53+
p, err := New(".")
54+
require.NoError(t, err)
55+
56+
fsys := os.DirFS(filepath.Join("testdata", "multiple-archived-deps"))
57+
require.NoError(t, p.ParseFS(context.TODO(), fsys, "."))
58+
59+
expectedFiles := []string{
60+
"Chart.yaml",
61+
"charts/common-2.26.0.tgz",
62+
"charts/opentelemetry-collector-0.108.0.tgz",
63+
}
64+
assert.Equal(t, expectedFiles, p.filepaths)
65+
})
66+
67+
t.Run("archives are not dependencies", func(t *testing.T) {
68+
p, err := New(".")
69+
require.NoError(t, err)
70+
71+
fsys := os.DirFS(filepath.Join("testdata", "non-deps-archives"))
72+
require.NoError(t, p.ParseFS(context.TODO(), fsys, "."))
73+
74+
expectedFiles := []string{
75+
"Chart.yaml",
76+
"backup_charts/wordpress-operator/Chart.yaml",
77+
"backup_charts/mysql-operator/Chart.yaml",
78+
}
79+
assert.Subset(t, p.filepaths, expectedFiles)
80+
})
5181
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v2
2+
appVersion: "1.1"
3+
description: Test Chart
4+
name: y-chart
5+
version: 1.0.0
6+
kubeVersion: ">=1.21"
7+
8+
dependencies:
9+
- name: common
10+
repository: https://charts.bitnami.com/bitnami
11+
version: 2.26.0
12+
- name: opentelemetry-collector
13+
version: 0.108.0
14+
repository: https://open-telemetry.github.io/opentelemetry-helm-charts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v2
2+
appVersion: "1.1"
3+
description: Test Chart
4+
name: y-chart
5+
version: 1.0.0
6+
kubeVersion: ">=1.21"
7+
8+
dependencies:
9+
- name: mysql-operator
10+
repository: https://mysql.github.io/mysql-operator/
11+
version: 2.2.2
12+
- name: wordpress-operator
13+
version: 0.12.4
14+
repository: https://helm-charts.bitpoke.io

0 commit comments

Comments
 (0)