• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

redhat-openshift-ecosystem / openshift-preflight / 22185704777

19 Feb 2026 02:24PM UTC coverage: 83.333% (-0.7%) from 84.053%
22185704777

push

github

acornett21
engine: support filter/allowlist for untar

Signed-off-by: Caleb Xu <caxu@redhat.com>

145 of 168 new or added lines in 2 files covered. (86.31%)

127 existing lines in 23 files now uncovered.

5185 of 6222 relevant lines covered (83.33%)

164.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.69
/internal/bundle/bundle.go
1
package bundle
2

3
import (
4
        "context"
5
        "fmt"
6
        "io"
7
        "os"
8
        "path/filepath"
9
        "strings"
10

11
        "github.com/redhat-openshift-ecosystem/openshift-preflight/internal/log"
12

13
        "github.com/blang/semver"
14
        "github.com/go-logr/logr"
15
        "github.com/operator-framework/api/pkg/manifests"
16
        "github.com/operator-framework/api/pkg/validation"
17

18
        rbacv1 "k8s.io/api/rbac/v1"
19
        "sigs.k8s.io/yaml"
20
)
21

22
// This table signifies what the NEXT release of OpenShift will
23
// deprecate, not what it matches up to.
24
var ocpToKubeVersion = map[string]string{
25
        "4.9":  "1.22",
26
        "4.10": "1.23",
27
        "4.11": "1.24",
28
        "4.12": "1.25",
29
        "4.13": "1.26",
30
        "4.14": "1.27",
31
        "4.15": "1.28",
32
        "4.16": "1.29",
33
        "4.17": "1.30",
34
        "4.18": "1.31",
35
        "4.19": "1.32",
36
        "4.20": "1.33",
37
        "4.21": "1.34",
38
        "4.22": "1.35",
39
}
40

41
const latestReleasedVersion = "4.21"
42

43
var BundleFiles = []string{
44
        "/manifests/*",
45
        "/metadata/annotations.yaml",
46
}
47

48
func Validate(ctx context.Context, imagePath string) (*Report, error) {
4✔
49
        logger := logr.FromContextOrDiscard(ctx)
4✔
50
        logger.V(log.TRC).Info("reading annotations file from the bundle")
4✔
51
        logger.V(log.DBG).Info("image extraction directory", "directory", imagePath)
4✔
52

4✔
53
        bundle, err := manifests.GetBundleFromDir(imagePath)
4✔
54
        if err != nil {
5✔
55
                return nil, fmt.Errorf("could not load bundle from path: %s: %v", imagePath, err)
1✔
56
        }
1✔
57
        validators := validation.DefaultBundleValidators.WithValidators(
3✔
58
                validation.AlphaDeprecatedAPIsValidator,
3✔
59
                validation.OperatorHubV2Validator,
3✔
60
                validation.StandardCapabilitiesValidator,
3✔
61
                validation.StandardCategoriesValidator,
3✔
62
        )
3✔
63

3✔
64
        objs := bundle.ObjectsToValidate()
3✔
65

3✔
66
        // retrieve the operator metadata from bundle image
3✔
67
        annotationsFileName := filepath.Join(imagePath, "metadata", "annotations.yaml")
3✔
68
        annotationsFile, err := os.Open(annotationsFileName)
3✔
69
        if err != nil {
4✔
70
                return nil, fmt.Errorf("could not open annotations.yaml: %v", err)
1✔
71
        }
1✔
72
        annotations, err := LoadAnnotations(ctx, annotationsFile)
2✔
73
        if err != nil {
2✔
UNCOV
74
                return nil, fmt.Errorf("unable to get annotations.yaml from the bundle: %v", err)
×
UNCOV
75
        }
×
76

77
        optionalValues := make(map[string]string)
2✔
78
        if annotations.OpenshiftVersions != "" {
3✔
79
                // Check that the label range contains >= 4.9
1✔
80
                targetVersion, err := targetVersion(annotations.OpenshiftVersions)
1✔
81
                if err != nil {
1✔
UNCOV
82
                        // Could not parse the version, which probably means the annotation is invalid
×
UNCOV
83
                        return nil, fmt.Errorf("%v", err)
×
UNCOV
84
                }
×
85
                if k8sVer, found := ocpToKubeVersion[targetVersion]; found {
2✔
86
                        logger.V(log.DBG).Info("running with additional checks enabled because of the OpenShift version detected", "version", targetVersion)
1✔
87
                        optionalValues = make(map[string]string)
1✔
88
                        optionalValues["k8s-version"] = k8sVer
1✔
89
                }
1✔
90
        }
91
        objs = append(objs, optionalValues)
2✔
92

2✔
93
        results := validators.Validate(objs...)
2✔
94
        passed := true
2✔
95
        for _, v := range results {
10✔
96
                if v.HasError() {
9✔
97
                        passed = false
1✔
98
                        break
1✔
99
                }
100
        }
101

102
        return &Report{Results: results, Passed: passed}, nil
2✔
103
}
104

105
func targetVersion(ocpLabelIndex string) (string, error) {
20✔
106
        beginsEqual := strings.HasPrefix(ocpLabelIndex, "=")
20✔
107
        // It means that the OCP label is =OCP version
20✔
108
        if beginsEqual {
23✔
109
                version := cleanStringToGetTheVersionToParse(strings.Split(ocpLabelIndex, "=")[1])
3✔
110
                verParsed, err := semver.ParseTolerant(version)
3✔
111
                if err != nil {
4✔
112
                        return "", fmt.Errorf("unable to parse the value (%s) on (%s): %v", version, ocpLabelIndex, err)
1✔
113
                }
1✔
114

115
                return fmt.Sprintf("%d.%d", verParsed.Major, verParsed.Minor), nil
2✔
116
        }
117

118
        indexRange := cleanStringToGetTheVersionToParse(ocpLabelIndex)
17✔
119
        if len(indexRange) > 1 {
34✔
120
                // Bare version, so send back latest released
17✔
121
                if !strings.Contains(indexRange, "-") {
29✔
122
                        verParsed, err := semver.ParseTolerant(indexRange)
12✔
123
                        if err != nil {
13✔
124
                                // The passed version is not valid. We don't care what it is,
1✔
125
                                // just that it's valid.
1✔
126
                                return "", fmt.Errorf("unable to parse the version: %v", err)
1✔
127
                        }
1✔
128

129
                        // If the specified version is greater than latestReleased, we will accept that
130
                        latestReleasedParsed, _ := semver.ParseTolerant(latestReleasedVersion)
11✔
131
                        if verParsed.GT(latestReleasedParsed) {
12✔
132
                                return fmt.Sprintf("%d.%d", verParsed.Major, verParsed.Minor), nil
1✔
133
                        }
1✔
134

135
                        return latestReleasedVersion, nil
10✔
136
                }
137

138
                versions := strings.Split(indexRange, "-")
5✔
139
                // This is a normal range of 1.0-2.0
5✔
140
                if len(versions) > 1 && versions[1] != "" {
9✔
141
                        version := versions[1]
4✔
142
                        verParsed, err := semver.ParseTolerant(version)
4✔
143
                        if err != nil {
5✔
144
                                return "", fmt.Errorf("unable to parse the version: %v", err)
1✔
145
                        }
1✔
146
                        return fmt.Sprintf("%d.%d", verParsed.Major, verParsed.Minor), nil
3✔
147
                }
148

149
                // This is an open-ended range: v1-. This is not valid.
150
                // So, we just fall through to the default return.
151
                return "", fmt.Errorf("unable to parse the version: malformed range: %s", indexRange)
1✔
152
        }
UNCOV
153
        return "", fmt.Errorf("unable to parse the version: unknown error")
×
154
}
155

156
// cleanStringToGetTheVersionToParse will remove the expected characters for
157
// we are able to parse the version informed.
158
func cleanStringToGetTheVersionToParse(value string) string {
20✔
159
        doubleQuote := "\""
20✔
160
        singleQuote := "'"
20✔
161
        value = strings.ReplaceAll(value, singleQuote, "")
20✔
162
        value = strings.ReplaceAll(value, doubleQuote, "")
20✔
163
        value = strings.ReplaceAll(value, "v", "")
20✔
164
        return value
20✔
165
}
20✔
166

167
// LoadAnnotations reads an operator bundle's annotations.yaml from r.
168
func LoadAnnotations(ctx context.Context, r io.Reader) (*Annotations, error) {
5✔
169
        annFile, err := io.ReadAll(r)
5✔
170
        if err != nil {
6✔
171
                return nil, fmt.Errorf("fail to read metadata/annotation.yaml file in bundle: %v", err)
1✔
172
        }
1✔
173

174
        if len(annFile) == 0 {
5✔
175
                return nil, fmt.Errorf("annotations file was empty")
1✔
176
        }
1✔
177

178
        var annotationsFile AnnotationsFile
3✔
179
        if err := yaml.Unmarshal(annFile, &annotationsFile); err != nil {
4✔
180
                return nil, fmt.Errorf("unable to load the annotations file: %v", err)
1✔
181
        }
1✔
182

183
        return &annotationsFile.Annotations, nil
2✔
184
}
185

186
// GetSecurityContextConstraints returns an string array of SCC resource names requested by the operator as specified
187
// in the csv
188
func GetSecurityContextConstraints(ctx context.Context, bundlePath string) ([]string, error) {
×
189
        bundle, err := manifests.GetBundleFromDir(bundlePath)
×
190
        if err != nil {
×
191
                return nil, fmt.Errorf("could not get bundle from dir: %s: %v", bundlePath, err)
×
192
        }
×
UNCOV
193
        for _, cp := range bundle.CSV.Spec.InstallStrategy.StrategySpec.ClusterPermissions {
×
UNCOV
194
                for _, rule := range cp.Rules {
×
195
                        if hasSCCApiGroup(rule) && hasSCCResource(rule) {
×
UNCOV
196
                                return rule.ResourceNames, nil
×
UNCOV
197
                        }
×
198
                }
199
        }
200
        return nil, nil
×
201
}
202

203
// hasSCCApiGroup returns a bool indicating if security.openshift.io is in the list of apigroups referenced in a policy
204
// rule
UNCOV
205
func hasSCCApiGroup(rule rbacv1.PolicyRule) bool {
×
206
        for _, apiGroup := range rule.APIGroups {
×
UNCOV
207
                if apiGroup == "security.openshift.io" {
×
UNCOV
208
                        return true
×
UNCOV
209
                }
×
210
        }
211
        return false
×
212
}
213

214
// hasSCCResource returns a bool indicating if any securitycontextconstraints resources are referenced in a policy rule
UNCOV
215
func hasSCCResource(rule rbacv1.PolicyRule) bool {
×
216
        for _, resource := range rule.Resources {
×
UNCOV
217
                if resource == "securitycontextconstraints" {
×
UNCOV
218
                        return true
×
UNCOV
219
                }
×
220
        }
UNCOV
221
        return false
×
222
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc