Skip to content
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
19 changes: 10 additions & 9 deletions sysdig/internal/client/v2/vulnerability_rule_bundle_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ type VulnerabilityRulePredicate struct {

type VulnerabilityRulePredicateExtra struct {
// Common fields for different predicate types
Level *Level `json:"level,omitempty"`
Age *int `json:"age,omitempty"`
Days *int `json:"days,omitempty"`
PkgType *string `json:"pkgType,omitempty"`
VulnIDS []string `json:"vulnIds,omitempty"`
Packages []Package `json:"packages,omitempty"`
Key *string `json:"key,omitempty"`
User *string `json:"user,omitempty"`
Value any `json:"value,omitempty"` // For image labels or CVSS score
Level *Level `json:"level,omitempty"`
Age *int `json:"age,omitempty"`
Days *int `json:"days,omitempty"`
PkgType *string `json:"pkgType,omitempty"`
VulnIDS []string `json:"vulnIds,omitempty"`
Packages []Package `json:"packages,omitempty"`
Key *string `json:"key,omitempty"`
User *string `json:"user,omitempty"`
Value any `json:"value,omitempty"` // For image labels or CVSS score
RequiredLabels []string `json:"requiredLabels,omitempty"`

// Disclosure Date Range
StartDate *string `json:"startDate,omitempty"`
Expand Down
78 changes: 78 additions & 0 deletions sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema {
},
},
},
"label_with_value_and_required_labels": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "A label key-value pair that must exist along with additional required labels.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"target_label": {
Type: schema.TypeString,
Required: true,
Description: "Label key that must exist with the specified value.",
},
"target_value": {
Type: schema.TypeString,
Required: true,
Description: "Expected value for the target label. Can be empty.",
},
"required_labels": {
Type: schema.TypeList,
Required: true,
Description: "Additional label keys that must also exist.",
Elem: &schema.Schema{Type: schema.TypeString},
},
Comment thread
tembleking marked this conversation as resolved.
},
},
},
},
},
}
Expand Down Expand Up @@ -574,6 +600,17 @@ func vulnerabilityRuleImageConfigLabelToData(ruleBundle v2.VulnerabilityRule) (m
}},
}},
}, nil
case "imageConfigLabelWithValueAndLabelsExist":
return map[string]any{
"image_label": []map[string]any{{
"id": ruleBundle.ID,
"label_with_value_and_required_labels": []map[string]any{{
"target_label": ruleBundle.Predicates[0].Extra.Key,
"target_value": ruleBundle.Predicates[0].Extra.Value,
"required_labels": ruleBundle.Predicates[0].Extra.RequiredLabels,
}},
}},
}, nil
}

return nil, fmt.Errorf("unsupported image config label rule for predicate: %s", ruleBundle.Predicates[0].Type)
Expand Down Expand Up @@ -729,7 +766,31 @@ func validateSeveritiesAndThreatsRule(ruleBody map[string]any) error {
return nil
}

func validateImageLabelRule(ruleBody map[string]any) error {
count := 0
if val, ok := ruleBody["label_must_exist"]; ok && val.(string) != "" {
count++
}
if val, ok := ruleBody["label_must_not_exist"]; ok && val.(string) != "" {
count++
}
if val, ok := ruleBody["label_must_exist_and_contain_value"]; ok && len(val.([]any)) > 0 {
count++
}
if val, ok := ruleBody["label_with_value_and_required_labels"]; ok && len(val.([]any)) > 0 {
count++
}
if count > 1 {
return errors.New("only one image label predicate can be set per image_label block")
}
return nil
}

func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.VulnerabilityRule, error) {
if err := validateImageLabelRule(ruleBody); err != nil {
return v2.VulnerabilityRule{}, err
}

rule := v2.VulnerabilityRule{
ID: new(ruleBody["id"].(string)),
Type: v2.VulnerabilityRuleTypeImageConfigLabel,
Expand Down Expand Up @@ -765,6 +826,23 @@ func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.Vulne
})
}

if label, ok := ruleBody["label_with_value_and_required_labels"]; ok && len(label.([]any)) > 0 {
contents := label.([]any)[0].(map[string]any)
rawLabels := contents["required_labels"].([]any)
requiredLabels := make([]string, len(rawLabels))
for i, v := range rawLabels {
requiredLabels[i] = v.(string)
}
rule.Predicates = append(rule.Predicates, v2.VulnerabilityRulePredicate{
Type: "imageConfigLabelWithValueAndLabelsExist",
Extra: &v2.VulnerabilityRulePredicateExtra{
Key: new(contents["target_label"].(string)),
Value: new(contents["target_value"].(string)),
RequiredLabels: requiredLabels,
},
})
}

Comment thread
tembleking marked this conversation as resolved.
if len(rule.Predicates) == 0 {
return v2.VulnerabilityRule{}, errors.New("no predicate has been specified for image label rule")
}
Expand Down
60 changes: 60 additions & 0 deletions sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func TestAccVulnerabilityRuleBundle(t *testing.T) {
Config: errorVulnerabilityRuleBundleConfig_ExploitConflict(random()),
ExpectError: regexp.MustCompile("`public_exploit_available` and `public_exploit_available_since_days` are mutually exclusive"),
},
{
Config: errorVulnerabilityRuleBundleConfig_ImageLabelConflict(random()),
ExpectError: regexp.MustCompile("only one image label predicate can be set"),
},
{
Config: minimalVulnerabilityRuleBundleConfig_ImageLabel(random()),
Check: resource.ComposeTestCheckFunc(
Expand Down Expand Up @@ -94,6 +98,16 @@ func TestAccVulnerabilityRuleBundle(t *testing.T) {
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_must_exist_and_contain_value.0.required_value", "required-value"),
),
},
{
Config: singleRuleConfig_label_with_value_and_required_labels(random()),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_with_value_and_required_labels.0.target_label", "Vendor"),
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_with_value_and_required_labels.0.target_value", "Acme"),
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_with_value_and_required_labels.0.required_labels.#", "2"),
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_with_value_and_required_labels.0.required_labels.0", "Team"),
resource.TestCheckResourceAttr("sysdig_secure_vulnerability_rule_bundle.sample", "rule.0.image_label.0.label_with_value_and_required_labels.0.required_labels.1", "Org"),
),
},
Comment thread
tembleking marked this conversation as resolved.
{
Config: fullVulnerabilityRuleBundleConfig_Severities(random()),
Check: resource.ComposeTestCheckFunc(
Expand Down Expand Up @@ -259,6 +273,24 @@ resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
`, suffix)
}

func singleRuleConfig_label_with_value_and_required_labels(suffix string) string {
return fmt.Sprintf(`
resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
name = "TERRAFORM TEST %s"
description = "rule with label_with_value_and_required_labels"
rule {
image_label {
label_with_value_and_required_labels {
target_label = "Vendor"
target_value = "Acme"
required_labels = ["Team", "Org"]
}
}
}
}
`, suffix)
}

func fullVulnerabilityRuleBundleConfig_Severities(suffix string) string {
return fmt.Sprintf(`
resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
Expand Down Expand Up @@ -400,6 +432,24 @@ resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
`, suffix)
}

func errorVulnerabilityRuleBundleConfig_ImageLabelConflict(suffix string) string {
return fmt.Sprintf(`
resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
name = "TERRAFORM TEST %s"
rule {
image_label {
label_must_exist = "required-label"
label_with_value_and_required_labels {
target_label = "Vendor"
target_value = "Acme"
required_labels = ["Team", "Org"]
}
}
}
}
`, suffix)
}

func variantVulnerabilityRuleBundleConfig_DisclosureDate(suffix string) string {
return fmt.Sprintf(`
resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
Expand Down Expand Up @@ -469,6 +519,16 @@ resource "sysdig_secure_vulnerability_rule_bundle" "sample" {
}
}

rule {
image_label {
label_with_value_and_required_labels {
target_label = "Vendor"
target_value = "Acme"
required_labels = ["Team", "Org"]
}
}
}

rule {
severities_and_threats {
severity_at_least = "high"
Expand Down
15 changes: 15 additions & 0 deletions website/docs/r/secure_vulnerability_rule_bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ resource "sysdig_secure_vulnerability_rule_bundle" "example_image_label" {
}
}
}

# Rule to ensure a label exists with a value AND other labels also exist
rule {
image_label {
label_with_value_and_required_labels {
target_label = "Vendor"
target_value = "Acme"
required_labels = ["Team", "Org"]
}
}
}
}
```

Expand Down Expand Up @@ -114,6 +125,10 @@ Defines rules based on image labels to evaluate image configuration. Only one of
* `label_must_exist_and_contain_value` - (Optional) A block specifying a label key and value that must exist in the image configuration.
* `required_label` - (Required) The label key that must exist.
* `required_value` - (Required) The expected value for the given label key.
* `label_with_value_and_required_labels` - (Optional) A block specifying a label key-value pair that must exist along with additional required labels.
* `target_label` - (Required) The label key that must exist with the specified value.
* `target_value` - (Required) The expected value for the target label. Can be empty.
* `required_labels` - (Required) A list of additional label keys that must also exist.

#### `severities_and_threats`

Expand Down
Loading