From 09048dd6810bf3e26ca02cb1073ee00071ee6d07 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Wed, 23 Jul 2025 12:37:41 +0200 Subject: [PATCH 01/11] feat: add first initial vulnerability rule bundle resource --- sysdig/internal/client/v2/sysdig.go | 1 + .../client/v2/vulnerability_rule_bundle.go | 116 +++++++ .../v2/vulnerability_rule_bundle_model.go | 64 ++++ sysdig/provider.go | 1 + ...sysdig_secure_vulnerability_rule_bundle.go | 326 ++++++++++++++++++ ...g_secure_vulnerability_rule_bundle_test.go | 52 +++ 6 files changed, 560 insertions(+) create mode 100644 sysdig/internal/client/v2/vulnerability_rule_bundle.go create mode 100644 sysdig/internal/client/v2/vulnerability_rule_bundle_model.go create mode 100644 sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go create mode 100644 sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go diff --git a/sysdig/internal/client/v2/sysdig.go b/sysdig/internal/client/v2/sysdig.go index 65b092eb..d0fd5bdb 100644 --- a/sysdig/internal/client/v2/sysdig.go +++ b/sysdig/internal/client/v2/sysdig.go @@ -55,6 +55,7 @@ type SysdigSecure interface { PolicyInterface RuleInterface VulnerabilityPolicyClient + VulnerabilityRuleBundleClient } func (sr *SysdigRequest) Request(ctx context.Context, method string, url string, payload io.Reader) (*http.Response, error) { diff --git a/sysdig/internal/client/v2/vulnerability_rule_bundle.go b/sysdig/internal/client/v2/vulnerability_rule_bundle.go new file mode 100644 index 00000000..92c5f6bd --- /dev/null +++ b/sysdig/internal/client/v2/vulnerability_rule_bundle.go @@ -0,0 +1,116 @@ +package v2 + +import ( + "context" + "errors" + "fmt" + "net/http" + "strconv" +) + +const ( + vulnerabilityRuleBundlesPath = "%s/secure/vulnerability/v1/bundles" + vulnerabilityRuleBundlePath = "%s/secure/vulnerability/v1/bundles/%s" +) + +type VulnerabilityRuleBundleClient interface { + CreateVulnerabilityRuleBundle(ctx context.Context, vulnerabilityRuleBundle VulnerabilityRuleBundle) (VulnerabilityRuleBundle, error) + GetVulnerabilityRuleBundleByID(ctx context.Context, vulnerabilityRuleBundleID string) (VulnerabilityRuleBundle, error) + UpdateVulnerabilityRuleBundle(ctx context.Context, vulnerabilityRuleBundle VulnerabilityRuleBundle) (VulnerabilityRuleBundle, error) + DeleteVulnerabilityRuleBundleByID(ctx context.Context, vulnerabilityRuleBundleID string) error +} + +func (c *Client) CreateVulnerabilityRuleBundle(ctx context.Context, vulnerabilityRuleBundle VulnerabilityRuleBundle) (ruleBundle VulnerabilityRuleBundle, err error) { + payload, err := Marshal(vulnerabilityRuleBundle) + if err != nil { + return VulnerabilityRuleBundle{}, err + } + + response, err := c.requester.Request(ctx, http.MethodPost, c.vulnerabilityRuleBundlesURL(), payload) + if err != nil { + return VulnerabilityRuleBundle{}, err + } + defer func() { + if dErr := response.Body.Close(); dErr != nil { + err = fmt.Errorf("unable to close response body: %w", dErr) + } + }() + + if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated { + return VulnerabilityRuleBundle{}, c.ErrorFromResponse(response) + } + + return Unmarshal[VulnerabilityRuleBundle](response.Body) +} + +func (c *Client) GetVulnerabilityRuleBundleByID(ctx context.Context, vulnerabilityRuleBundleID string) (ruleBundle VulnerabilityRuleBundle, err error) { + response, err := c.requester.Request(ctx, http.MethodGet, c.vulnerabilityRuleBundleURL(vulnerabilityRuleBundleID), nil) + if err != nil { + return VulnerabilityRuleBundle{}, err + } + defer func() { + if dErr := response.Body.Close(); dErr != nil { + err = fmt.Errorf("unable to close response body: %w", dErr) + } + }() + + if response.StatusCode != http.StatusOK { + return VulnerabilityRuleBundle{}, c.ErrorFromResponse(response) + } + + return Unmarshal[VulnerabilityRuleBundle](response.Body) +} + +func (c *Client) UpdateVulnerabilityRuleBundle(ctx context.Context, vulnerabilityRuleBundle VulnerabilityRuleBundle) (ruleBundle VulnerabilityRuleBundle, err error) { + if vulnerabilityRuleBundle.ID == nil { + return VulnerabilityRuleBundle{}, errors.New("rule bundle id was null") + } + + payload, err := Marshal(vulnerabilityRuleBundle) + if err != nil { + return VulnerabilityRuleBundle{}, err + } + + idAsStr := strconv.Itoa(int(*vulnerabilityRuleBundle.ID)) + response, err := c.requester.Request(ctx, http.MethodPut, c.vulnerabilityRuleBundleURL(idAsStr), payload) + if err != nil { + return VulnerabilityRuleBundle{}, err + } + defer func() { + if dErr := response.Body.Close(); dErr != nil { + err = fmt.Errorf("unable to close response body: %w", dErr) + } + }() + + if response.StatusCode != http.StatusOK { + return VulnerabilityRuleBundle{}, c.ErrorFromResponse(response) + } + + return Unmarshal[VulnerabilityRuleBundle](response.Body) +} + +func (c *Client) DeleteVulnerabilityRuleBundleByID(ctx context.Context, vulnerabilityRuleBundleID string) (err error) { + response, err := c.requester.Request(ctx, http.MethodDelete, c.vulnerabilityRuleBundleURL(vulnerabilityRuleBundleID), nil) + if err != nil { + return err + } + defer func() { + if dErr := response.Body.Close(); dErr != nil { + err = fmt.Errorf("unable to close response body: %w", dErr) + } + }() + + if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK { + return c.ErrorFromResponse(response) + } + + return err +} + +func (c *Client) vulnerabilityRuleBundlesURL() string { + return fmt.Sprintf(vulnerabilityRuleBundlesPath, c.config.url) +} + +func (c *Client) vulnerabilityRuleBundleURL(vulnerabilityRuleBundleID string) string { + return fmt.Sprintf(vulnerabilityRuleBundlePath, c.config.url, vulnerabilityRuleBundleID) +} diff --git a/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go b/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go new file mode 100644 index 00000000..469fdafd --- /dev/null +++ b/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go @@ -0,0 +1,64 @@ +package v2 + +type VulnerabilityRuleBundle struct { + ID *int `json:"id,omitempty"` + Name string `json:"name"` + Identifier *string `json:"identifier,omitempty"` + Description *string `json:"description,omitempty"` + Rules []VulnerabilityRule `json:"rules"` +} + +type VulnerabilityRule struct { + ID *string `json:"ruleId,omitempty"` + Type VulnerabilityRuleType `json:"ruleType"` + Predicates []VulnerabilityRulePredicate `json:"predicates"` +} + +type VulnerabilityRulePredicate struct { + Type string `json:"type"` + Extra *VulnerabilityRulePredicateExtra `json:"extra,omitempty"` +} + +type VulnerabilityRulePredicateExtra struct { + Level *Level `json:"level,omitempty"` + Age *int `json:"age,omitempty"` + VulnIDS []string `json:"vulnIds,omitempty"` + Value *Value `json:"value,omitempty"` + Packages []Package `json:"packages,omitempty"` + Key *string `json:"key,omitempty"` + User *string `json:"user,omitempty"` + PkgType *string `json:"pkgType,omitempty"` +} + +type Package struct { + Name string `json:"name"` + Version string `json:"version"` +} + +type Level string + +const ( + Critical Level = "critical" + High Level = "high" + Medium Level = "medium" +) + +type VulnerabilityRuleType string + +const ( + VulnerabilityRuleTypeImageConfigCreationDate VulnerabilityRuleType = "imageConfigCreationDate" + VulnerabilityRuleTypeImageConfigDefaultUser VulnerabilityRuleType = "imageConfigDefaultUser" + VulnerabilityRuleTypeImageConfigEnvVariable VulnerabilityRuleType = "imageConfigEnvVariable" + VulnerabilityRuleTypeImageConfigInstructionIsPkgManager VulnerabilityRuleType = "imageConfigInstructionIsPkgManager" + VulnerabilityRuleTypeImageConfigInstructionNotRecommended VulnerabilityRuleType = "imageConfigInstructionNotRecommended" + VulnerabilityRuleTypeImageConfigLabel VulnerabilityRuleType = "imageConfigLabel" + VulnerabilityRuleTypeImageConfigSensitiveInformationAndSecrets VulnerabilityRuleType = "imageConfigSensitiveInformationAndSecrets" + VulnerabilityRuleTypePkgDenyList VulnerabilityRuleType = "pkgDenyList" + VulnerabilityRuleTypeVulnDenyList VulnerabilityRuleType = "vulnDenyList" + VulnerabilityRuleTypeVulnSeverityAndThreats VulnerabilityRuleType = "vulnSeverityAndThreats" +) + +type Value struct { + Integer *int64 + String *string +} diff --git a/sysdig/provider.go b/sysdig/provider.go index 8d0623b4..8ef0ef4e 100644 --- a/sysdig/provider.go +++ b/sysdig/provider.go @@ -200,6 +200,7 @@ func (p *SysdigProvider) Provider() *schema.Provider { "sysdig_secure_team": resourceSysdigSecureTeam(), "sysdig_secure_vulnerability_accept_risk": resourceSysdigSecureVulnerabilityAcceptRisk(), "sysdig_secure_vulnerability_policy": resourceSysdigSecureVulnerabilityPolicy(), + "sysdig_secure_vulnerability_rule_bundle": resourceSysdigSecureVulnerabilityRuleBundle(), "sysdig_secure_zone": resourceSysdigSecureZone(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go new file mode 100644 index 00000000..36b03935 --- /dev/null +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -0,0 +1,326 @@ +package sysdig + +import ( + "context" + "errors" + "fmt" + "iter" + "maps" + "slices" + "strconv" + "time" + + v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "label_must_exist": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func resourceSysdigSecureVulnerabilityRuleBundle() *schema.Resource { + timeout := 5 * time.Minute + + return &schema.Resource{ + CreateContext: resourceSysdigVulnerabilityRuleBundleCreate, + ReadContext: resourceSysdigVulnerabilityRuleBundleRead, + UpdateContext: resourceSysdigVulnerabilityRuleBundleUpdate, + DeleteContext: resourceSysdigVulnerabilityRuleBundleDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(timeout), + Delete: schema.DefaultTimeout(timeout), + Update: schema.DefaultTimeout(timeout), + Read: schema.DefaultTimeout(timeout), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Rule Bundle name", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "Rule Bundle description", + }, + "identifier": { + Type: schema.TypeString, + Computed: true, + Description: "External identifier", + }, + + "rules": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image_config_label": vulnerabilityRuleSchemaImageConfigLabel(), + }, + }, + }, + }, + } +} + +func getSecureVulnerabilityRuleBundleClient(c SysdigClients) (v2.VulnerabilityRuleBundleClient, error) { + return c.sysdigSecureClientV2() +} + +func resourceSysdigVulnerabilityRuleBundleCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client, err := getSecureVulnerabilityRuleBundleClient(meta.(SysdigClients)) + if err != nil { + return diag.FromErr(err) + } + + scanningRuleBundle, err := vulnerabilityRuleBundleFromResourceData(d) + if err != nil { + return diag.FromErr(err) + } + + scanningRuleBundle, err = client.CreateVulnerabilityRuleBundle(ctx, scanningRuleBundle) + if err != nil { + return diag.FromErr(err) + } + + vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + + return nil +} + +func resourceSysdigVulnerabilityRuleBundleUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client, err := getSecureVulnerabilityRuleBundleClient(meta.(SysdigClients)) + if err != nil { + return diag.FromErr(err) + } + + scanningRuleBundle, err := vulnerabilityRuleBundleFromResourceData(d) + if err != nil { + return diag.FromErr(err) + } + + _, err = client.UpdateVulnerabilityRuleBundle(ctx, scanningRuleBundle) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourceSysdigVulnerabilityRuleBundleRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client, err := getSecureVulnerabilityRuleBundleClient(meta.(SysdigClients)) + if err != nil { + return diag.FromErr(err) + } + + scanningRuleBundle, err := client.GetVulnerabilityRuleBundleByID(ctx, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + + return nil +} + +func resourceSysdigVulnerabilityRuleBundleDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client, err := getSecureVulnerabilityRuleBundleClient(meta.(SysdigClients)) + if err != nil { + return diag.FromErr(err) + } + + err = client.DeleteVulnerabilityRuleBundleByID(ctx, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func vulnerabilityRuleBundleToResourceData(scanningRuleBundle *v2.VulnerabilityRuleBundle, d *schema.ResourceData) error { + if scanningRuleBundle.ID == nil { + d.SetId("") // id is nil, let's destroy the resource + return nil + } + + d.SetId(strconv.Itoa(int(*scanningRuleBundle.ID))) + _ = d.Set("name", scanningRuleBundle.Name) + _ = d.Set("description", scanningRuleBundle.Description) + + if scanningRuleBundle.Identifier != nil { + _ = d.Set("identifier", *scanningRuleBundle.Identifier) + } + + ruleData, err := vulnerabilityRulesToData(scanningRuleBundle.Rules) + if err != nil { + return err + } + _ = d.Set("rules", ruleData) + + return nil +} + +func vulnerabilityRulesToData(scanningRuleBundle []v2.VulnerabilityRule) ([]map[string]any, error) { + var rules []map[string]any + + for _, ruleBundle := range scanningRuleBundle { + data, err := vulnerabilityRuleToData(ruleBundle) + if err != nil { + return nil, err + } + rules = append(rules, data) + } + + return rules, nil +} + +func vulnerabilityRuleToData(ruleBundle v2.VulnerabilityRule) (map[string]any, error) { + switch ruleBundle.Type { + case v2.VulnerabilityRuleTypeImageConfigLabel: + return vulnerabilityRuleImageConfigLabelToData(ruleBundle) + default: + return nil, fmt.Errorf("unsupported rule bundle type: %s", ruleBundle.Type) + } +} + +func vulnerabilityRuleImageConfigLabelToData(ruleBundle v2.VulnerabilityRule) (map[string]any, error) { + switch ruleBundle.Predicates[0].Type { + case "imageConfigLabelNotExists": + return map[string]any{ + "image_config_label": []map[string]any{{ + "id": ruleBundle.ID, + "label_must_exist": ruleBundle.Predicates[0].Extra.Key, + }}, + }, nil + } + + return nil, fmt.Errorf("unsupported image config label rule for predicate: %s", ruleBundle.Predicates[0].Type) +} + +func toPtr[T any](any T) *T { + return &any +} + +func vulnerabilityRuleBundleFromResourceData(d *schema.ResourceData) (v2.VulnerabilityRuleBundle, error) { + stringPtr := func(d *schema.ResourceData, key string) *string { + if value, ok := d.GetOk(key); ok && value.(string) != "" { + valueAsString := value.(string) + return &valueAsString + } + return nil + } + + int32PtrFromID := func(d *schema.ResourceData) *int { + id := d.Id() + if id == "" { + return nil + } + + idAsInt, err := strconv.Atoi(id) + if err != nil { + return nil + } + + return &idAsInt + } + + rules, err := vulnerabilityRulesFromSet(d.Get("rules").(*schema.Set)) + if err != nil { + return v2.VulnerabilityRuleBundle{}, err + } + + return v2.VulnerabilityRuleBundle{ + ID: int32PtrFromID(d), + Identifier: stringPtr(d, "identifier"), + Name: d.Get("name").(string), + Description: toPtr(d.Get("description").(string)), + Rules: rules, + }, nil +} + +func vulnerabilityRulesFromSet(list *schema.Set) ([]v2.VulnerabilityRule, error) { + var out []v2.VulnerabilityRule + + for _, ruleRaw := range list.List() { + rule, err := vulnerabilityRuleFromMap(ruleRaw.(map[string]any)) + if err != nil { + return nil, err + } + out = append(out, rule) + } + + return out, nil +} + +func validateRuleMap(ruleMap map[string]any) error { + if len(ruleMap) == 1 { + return nil + } + if len(ruleMap) == 0 { + return errors.New("you must specify one rule") + } + keys := slices.Collect(maps.Keys(ruleMap)) + return fmt.Errorf("you can only specify one rule per rule block, specify more rule blocks if you need more rules, you specified: %s", keys) +} + +func vulnerabilityRuleFromMap(ruleMap map[string]any) (v2.VulnerabilityRule, error) { + err := validateRuleMap(ruleMap) + if err != nil { + return v2.VulnerabilityRule{}, err + } + + next, stop := iter.Pull2(maps.All(ruleMap)) + defer stop() + + ruleType, ruleBody, ok := next() + if !ok { + return v2.VulnerabilityRule{}, errors.New("expected one rule in the iteration, got none") + } + + switch v2.VulnerabilityRuleType(ruleType) { + case "image_config_label": + return vulnerabilityRuleImageConfigLabelFromMap(ruleBody.(*schema.Set).List()[0].(map[string]any)) + default: + return v2.VulnerabilityRule{}, fmt.Errorf("unsupported rule type: %s", ruleType) + } +} + +func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.VulnerabilityRule, error) { + rule := v2.VulnerabilityRule{ + ID: toPtr(ruleBody["id"].(string)), + Type: v2.VulnerabilityRuleTypeImageConfigLabel, + Predicates: []v2.VulnerabilityRulePredicate{}, + } + + if label, ok := ruleBody["label_must_exist"]; ok { + rule.Predicates = append(rule.Predicates, v2.VulnerabilityRulePredicate{ + Type: "imageConfigLabelNotExists", + Extra: &v2.VulnerabilityRulePredicateExtra{ + Key: toPtr(label.(string)), + }, + }) + } + + return rule, nil +} diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go new file mode 100644 index 00000000..2741c50e --- /dev/null +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go @@ -0,0 +1,52 @@ +//go:build tf_acc_sysdig_secure || tf_acc_vulnerability_scanning + +package sysdig_test + +import ( + "fmt" + "os" + "testing" + + "github.com/draios/terraform-provider-sysdig/sysdig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestAccVulnerabilityRuleBundle(t *testing.T) { + random := func() string { return acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + if v := os.Getenv("SYSDIG_SECURE_API_TOKEN"); v == "" { + t.Fatal("SYSDIG_SECURE_API_TOKEN must be set for acceptance tests") + } + }, + ProviderFactories: map[string]func() (*schema.Provider, error){ + "sysdig": func() (*schema.Provider, error) { return sysdig.Provider(), nil }, + }, + Steps: []resource.TestStep{ + { + Config: minimalVulnerabilityRuleBundleConfig(random()), + }, + // { + // ResourceName: "sysdig_secure_vulnerability_rule_bundle.sample", + // ImportState: true, + // ImportStateVerify: true, + // }, + }, + }) +} + +func minimalVulnerabilityRuleBundleConfig(suffix string) string { + return fmt.Sprintf(` +resource "sysdig_secure_vulnerability_rule_bundle" "sample" { + name = "TERRAFORM TEST %s" + rules { + image_config_label { + label_must_exist = "required-label" + } + } +} +`, suffix) +} From 1441d75d17df47600698f6b45684db5418af7f6e Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Wed, 23 Jul 2025 16:47:37 +0200 Subject: [PATCH 02/11] feat: add label_must_not_exist rule bundle --- ...sysdig_secure_vulnerability_rule_bundle.go | 35 +++++++++++++++++-- ...g_secure_vulnerability_rule_bundle_test.go | 5 +++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 36b03935..e26c2997 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -20,6 +20,8 @@ func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { return &schema.Schema{ Type: schema.TypeSet, Optional: true, + MaxItems: 1, + MinItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { @@ -30,6 +32,10 @@ func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { Type: schema.TypeString, Optional: true, }, + "label_must_not_exist": { + Type: schema.TypeString, + Optional: true, + }, }, }, } @@ -104,7 +110,10 @@ func resourceSysdigVulnerabilityRuleBundleCreate(ctx context.Context, d *schema. return diag.FromErr(err) } - vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + err = vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + if err != nil { + return diag.FromErr(err) + } return nil } @@ -139,7 +148,10 @@ func resourceSysdigVulnerabilityRuleBundleRead(ctx context.Context, d *schema.Re return diag.FromErr(err) } - vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + err = vulnerabilityRuleBundleToResourceData(&scanningRuleBundle, d) + if err != nil { + return diag.FromErr(err) + } return nil } @@ -213,6 +225,14 @@ func vulnerabilityRuleImageConfigLabelToData(ruleBundle v2.VulnerabilityRule) (m "label_must_exist": ruleBundle.Predicates[0].Extra.Key, }}, }, nil + case "imageConfigLabelExists": + return map[string]any{ + "image_config_label": []map[string]any{{ + "id": ruleBundle.ID, + "label_must_not_exist": ruleBundle.Predicates[0].Extra.Key, + }}, + }, nil + } return nil, fmt.Errorf("unsupported image config label rule for predicate: %s", ruleBundle.Predicates[0].Type) @@ -313,7 +333,7 @@ func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.Vulne Predicates: []v2.VulnerabilityRulePredicate{}, } - if label, ok := ruleBody["label_must_exist"]; ok { + if label, ok := ruleBody["label_must_exist"]; ok && label.(string) != "" { rule.Predicates = append(rule.Predicates, v2.VulnerabilityRulePredicate{ Type: "imageConfigLabelNotExists", Extra: &v2.VulnerabilityRulePredicateExtra{ @@ -322,5 +342,14 @@ func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.Vulne }) } + if label, ok := ruleBody["label_must_not_exist"]; ok && label.(string) != "" { + rule.Predicates = append(rule.Predicates, v2.VulnerabilityRulePredicate{ + Type: "imageConfigLabelExists", + Extra: &v2.VulnerabilityRulePredicateExtra{ + Key: toPtr(label.(string)), + }, + }) + } + return rule, nil } diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go index 2741c50e..2986bd1c 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go @@ -47,6 +47,11 @@ resource "sysdig_secure_vulnerability_rule_bundle" "sample" { label_must_exist = "required-label" } } + rules { + image_config_label { + label_must_not_exist = "forbidden-label" + } + } } `, suffix) } From 130944382ea1fec98aa102e1706011b9311ee89d Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 09:45:46 +0200 Subject: [PATCH 03/11] feat: add label_must_exist_and_contain_value image label rule --- .../v2/vulnerability_rule_bundle_model.go | 7 +--- ...sysdig_secure_vulnerability_rule_bundle.go | 38 ++++++++++++++++++- ...g_secure_vulnerability_rule_bundle_test.go | 8 ++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go b/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go index 469fdafd..c2f0c693 100644 --- a/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go +++ b/sysdig/internal/client/v2/vulnerability_rule_bundle_model.go @@ -23,7 +23,7 @@ type VulnerabilityRulePredicateExtra struct { Level *Level `json:"level,omitempty"` Age *int `json:"age,omitempty"` VulnIDS []string `json:"vulnIds,omitempty"` - Value *Value `json:"value,omitempty"` + Value *string `json:"value,omitempty"` Packages []Package `json:"packages,omitempty"` Key *string `json:"key,omitempty"` User *string `json:"user,omitempty"` @@ -57,8 +57,3 @@ const ( VulnerabilityRuleTypeVulnDenyList VulnerabilityRuleType = "vulnDenyList" VulnerabilityRuleTypeVulnSeverityAndThreats VulnerabilityRuleType = "vulnSeverityAndThreats" ) - -type Value struct { - Integer *int64 - String *string -} diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index e26c2997..80b40586 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -36,6 +36,22 @@ func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { Type: schema.TypeString, Optional: true, }, + "label_must_exist_and_contain_value": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "required_label": { + Type: schema.TypeString, + Required: true, + }, + "required_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, }, }, } @@ -232,7 +248,16 @@ func vulnerabilityRuleImageConfigLabelToData(ruleBundle v2.VulnerabilityRule) (m "label_must_not_exist": ruleBundle.Predicates[0].Extra.Key, }}, }, nil - + case "imageConfigLabelNotContains": + return map[string]any{ + "image_config_label": []map[string]any{{ + "id": ruleBundle.ID, + "label_must_exist_and_contain_value": []map[string]any{{ + "required_label": ruleBundle.Predicates[0].Extra.Key, + "required_value": ruleBundle.Predicates[0].Extra.Value, + }}, + }}, + }, nil } return nil, fmt.Errorf("unsupported image config label rule for predicate: %s", ruleBundle.Predicates[0].Type) @@ -351,5 +376,16 @@ func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.Vulne }) } + if label, ok := ruleBody["label_must_exist_and_contain_value"]; ok && len(label.([]any)) > 0 { + contents := label.([]any)[0].(map[string]any) + rule.Predicates = append(rule.Predicates, v2.VulnerabilityRulePredicate{ + Type: "imageConfigLabelNotContains", + Extra: &v2.VulnerabilityRulePredicateExtra{ + Key: toPtr(contents["required_label"].(string)), + Value: toPtr(contents["required_value"].(string)), + }, + }) + } + return rule, nil } diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go index 2986bd1c..f62af9d2 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go @@ -52,6 +52,14 @@ resource "sysdig_secure_vulnerability_rule_bundle" "sample" { label_must_not_exist = "forbidden-label" } } + rules { + image_config_label { + label_must_exist_and_contain_value { + required_label = "required-label" + required_value = "required-value" + } + } + } } `, suffix) } From 7ce33291068ac02046de9ffbce8d64739c3e90af Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 09:48:25 +0200 Subject: [PATCH 04/11] refactor: rename fields --- ...ce_sysdig_secure_vulnerability_rule_bundle.go | 16 ++++++++-------- ...sdig_secure_vulnerability_rule_bundle_test.go | 15 +++++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 80b40586..3e483bd3 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -93,12 +93,12 @@ func resourceSysdigSecureVulnerabilityRuleBundle() *schema.Resource { Description: "External identifier", }, - "rules": { + "rule": { Type: schema.TypeSet, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "image_config_label": vulnerabilityRuleSchemaImageConfigLabel(), + "image_label": vulnerabilityRuleSchemaImageConfigLabel(), }, }, }, @@ -204,7 +204,7 @@ func vulnerabilityRuleBundleToResourceData(scanningRuleBundle *v2.VulnerabilityR if err != nil { return err } - _ = d.Set("rules", ruleData) + _ = d.Set("rule", ruleData) return nil } @@ -236,21 +236,21 @@ func vulnerabilityRuleImageConfigLabelToData(ruleBundle v2.VulnerabilityRule) (m switch ruleBundle.Predicates[0].Type { case "imageConfigLabelNotExists": return map[string]any{ - "image_config_label": []map[string]any{{ + "image_label": []map[string]any{{ "id": ruleBundle.ID, "label_must_exist": ruleBundle.Predicates[0].Extra.Key, }}, }, nil case "imageConfigLabelExists": return map[string]any{ - "image_config_label": []map[string]any{{ + "image_label": []map[string]any{{ "id": ruleBundle.ID, "label_must_not_exist": ruleBundle.Predicates[0].Extra.Key, }}, }, nil case "imageConfigLabelNotContains": return map[string]any{ - "image_config_label": []map[string]any{{ + "image_label": []map[string]any{{ "id": ruleBundle.ID, "label_must_exist_and_contain_value": []map[string]any{{ "required_label": ruleBundle.Predicates[0].Extra.Key, @@ -290,7 +290,7 @@ func vulnerabilityRuleBundleFromResourceData(d *schema.ResourceData) (v2.Vulnera return &idAsInt } - rules, err := vulnerabilityRulesFromSet(d.Get("rules").(*schema.Set)) + rules, err := vulnerabilityRulesFromSet(d.Get("rule").(*schema.Set)) if err != nil { return v2.VulnerabilityRuleBundle{}, err } @@ -344,7 +344,7 @@ func vulnerabilityRuleFromMap(ruleMap map[string]any) (v2.VulnerabilityRule, err } switch v2.VulnerabilityRuleType(ruleType) { - case "image_config_label": + case "image_label": return vulnerabilityRuleImageConfigLabelFromMap(ruleBody.(*schema.Set).List()[0].(map[string]any)) default: return v2.VulnerabilityRule{}, fmt.Errorf("unsupported rule type: %s", ruleType) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go index f62af9d2..d57b8dd0 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go @@ -42,18 +42,21 @@ func minimalVulnerabilityRuleBundleConfig(suffix string) string { return fmt.Sprintf(` resource "sysdig_secure_vulnerability_rule_bundle" "sample" { name = "TERRAFORM TEST %s" - rules { - image_config_label { + + rule { + image_label { label_must_exist = "required-label" } } - rules { - image_config_label { + + rule { + image_label { label_must_not_exist = "forbidden-label" } } - rules { - image_config_label { + + rule { + image_label { label_must_exist_and_contain_value { required_label = "required-label" required_value = "required-value" From b482d1514145d8441416b5e7aa7e0f55c31b07c5 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 10:27:43 +0200 Subject: [PATCH 05/11] refactor: replace iter with a simpler for loop --- ..._sysdig_secure_vulnerability_rule_bundle.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 3e483bd3..792474ee 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "iter" "maps" "slices" "strconv" @@ -335,20 +334,15 @@ func vulnerabilityRuleFromMap(ruleMap map[string]any) (v2.VulnerabilityRule, err return v2.VulnerabilityRule{}, err } - next, stop := iter.Pull2(maps.All(ruleMap)) - defer stop() - - ruleType, ruleBody, ok := next() - if !ok { - return v2.VulnerabilityRule{}, errors.New("expected one rule in the iteration, got none") - } + for ruleType, ruleBody := range ruleMap { + switch v2.VulnerabilityRuleType(ruleType) { + case "image_label": + return vulnerabilityRuleImageConfigLabelFromMap(ruleBody.(*schema.Set).List()[0].(map[string]any)) + } - switch v2.VulnerabilityRuleType(ruleType) { - case "image_label": - return vulnerabilityRuleImageConfigLabelFromMap(ruleBody.(*schema.Set).List()[0].(map[string]any)) - default: return v2.VulnerabilityRule{}, fmt.Errorf("unsupported rule type: %s", ruleType) } + panic("unreachable") } func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.VulnerabilityRule, error) { From ba273c85600f87c133afbed2f243266eb66a34ae Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 10:29:54 +0200 Subject: [PATCH 06/11] feat: make the list of rules keep order --- .../resource_sysdig_secure_vulnerability_rule_bundle.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 792474ee..28471558 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -93,7 +93,7 @@ func resourceSysdigSecureVulnerabilityRuleBundle() *schema.Resource { }, "rule": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -289,7 +289,7 @@ func vulnerabilityRuleBundleFromResourceData(d *schema.ResourceData) (v2.Vulnera return &idAsInt } - rules, err := vulnerabilityRulesFromSet(d.Get("rule").(*schema.Set)) + rules, err := vulnerabilityRulesFromList(d.Get("rule").([]any)) if err != nil { return v2.VulnerabilityRuleBundle{}, err } @@ -303,10 +303,10 @@ func vulnerabilityRuleBundleFromResourceData(d *schema.ResourceData) (v2.Vulnera }, nil } -func vulnerabilityRulesFromSet(list *schema.Set) ([]v2.VulnerabilityRule, error) { +func vulnerabilityRulesFromList(list []any) ([]v2.VulnerabilityRule, error) { var out []v2.VulnerabilityRule - for _, ruleRaw := range list.List() { + for _, ruleRaw := range list { rule, err := vulnerabilityRuleFromMap(ruleRaw.(map[string]any)) if err != nil { return nil, err From 5e804d6aaf997154657760aab2a4ffa8b7dfb2d8 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 11:09:26 +0200 Subject: [PATCH 07/11] ci: add more verifications to validate the resource --- ...sysdig_secure_vulnerability_rule_bundle.go | 14 +++- ...g_secure_vulnerability_rule_bundle_test.go | 71 +++++++++++++++++-- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 28471558..521a43bc 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -20,7 +20,6 @@ func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { Type: schema.TypeSet, Optional: true, MaxItems: 1, - MinItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { @@ -93,8 +92,10 @@ func resourceSysdigSecureVulnerabilityRuleBundle() *schema.Resource { }, "rule": { - Type: schema.TypeList, - Required: true, + Type: schema.TypeList, + Required: true, + MinItems: 1, + Description: "Rules for this bundle", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "image_label": vulnerabilityRuleSchemaImageConfigLabel(), @@ -307,6 +308,9 @@ func vulnerabilityRulesFromList(list []any) ([]v2.VulnerabilityRule, error) { var out []v2.VulnerabilityRule for _, ruleRaw := range list { + if ruleRaw == nil { + return nil, errors.New("empty rule detected, you need to specify one") + } rule, err := vulnerabilityRuleFromMap(ruleRaw.(map[string]any)) if err != nil { return nil, err @@ -381,5 +385,9 @@ func vulnerabilityRuleImageConfigLabelFromMap(ruleBody map[string]any) (v2.Vulne }) } + if len(rule.Predicates) == 0 { + return v2.VulnerabilityRule{}, errors.New("no predicate has been specified for image label rule") + } + return rule, nil } diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go index d57b8dd0..9f200ced 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle_test.go @@ -5,6 +5,7 @@ package sysdig_test import ( "fmt" "os" + "regexp" "testing" "github.com/draios/terraform-provider-sysdig/sysdig" @@ -26,22 +27,82 @@ func TestAccVulnerabilityRuleBundle(t *testing.T) { "sysdig": func() (*schema.Provider, error) { return sysdig.Provider(), nil }, }, Steps: []resource.TestStep{ + { + Config: incorrectVulnerabilityRuleBundleConfig(random()), + ExpectError: regexp.MustCompile("empty rule detected, you need to specify one"), + }, + { + Config: incorrectVulnerabilityRuleBundleConfig2(random()), + ExpectError: regexp.MustCompile(`No more than 1 "image_label" blocks are allowed`), + }, + { + Config: incorrectVulnerabilityRuleBundleConfig3(random()), + ExpectError: regexp.MustCompile(`no predicate has been specified for image label rule`), + }, { Config: minimalVulnerabilityRuleBundleConfig(random()), }, - // { - // ResourceName: "sysdig_secure_vulnerability_rule_bundle.sample", - // ImportState: true, - // ImportStateVerify: true, - // }, + { + Config: fullVulnerabilityRuleBundleConfig(random()), + }, + { + ResourceName: "sysdig_secure_vulnerability_rule_bundle.sample", + ImportState: true, + ImportStateVerify: true, + }, }, }) } +func incorrectVulnerabilityRuleBundleConfig(suffix string) string { + return fmt.Sprintf(` +resource "sysdig_secure_vulnerability_rule_bundle" "sample" { + name = "TERRAFORM TEST %s" + rule {} +} +`, suffix) +} + +func incorrectVulnerabilityRuleBundleConfig2(suffix string) string { + return fmt.Sprintf(` +resource "sysdig_secure_vulnerability_rule_bundle" "sample" { + name = "TERRAFORM TEST %s" + rule { + image_label {} + image_label {} + } +} +`, suffix) +} + +func incorrectVulnerabilityRuleBundleConfig3(suffix string) string { + return fmt.Sprintf(` +resource "sysdig_secure_vulnerability_rule_bundle" "sample" { + name = "TERRAFORM TEST %s" + rule { + image_label {} + } +} +`, suffix) +} + func minimalVulnerabilityRuleBundleConfig(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" + } + } +} +`, suffix) +} + +func fullVulnerabilityRuleBundleConfig(suffix string) string { + return fmt.Sprintf(` +resource "sysdig_secure_vulnerability_rule_bundle" "sample" { + name = "TERRAFORM TEST %s" rule { image_label { From 8e03ac4a31bd01843b5e21f26b61963ab23fc948 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 11:13:53 +0200 Subject: [PATCH 08/11] ci: add a sysdig_secure_vulnerability_rule_bundle reference to sysdig_secure_vulnerability_policy test --- ...rce_sysdig_secure_vulnerability_policy_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_policy_test.go b/sysdig/resource_sysdig_secure_vulnerability_policy_test.go index 0ed667e5..2fbad888 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_policy_test.go +++ b/sysdig/resource_sysdig_secure_vulnerability_policy_test.go @@ -32,7 +32,7 @@ func TestAccVulnerabilityPolicy(t *testing.T) { { Config: vulnerabilityPolicyConfig(random()), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("sysdig_secure_vulnerability_policy.sample", "bundles.#", "1"), + resource.TestCheckResourceAttr("sysdig_secure_vulnerability_policy.sample", "bundles.#", "2"), resource.TestCheckResourceAttr("sysdig_secure_vulnerability_policy.sample", "bundles.0", "1"), resource.TestCheckResourceAttr("sysdig_secure_vulnerability_policy.sample", "stages.#", "3"), ), @@ -57,11 +57,20 @@ resource "sysdig_secure_vulnerability_policy" "sample" { func vulnerabilityPolicyConfig(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" + } + } +} + resource "sysdig_secure_vulnerability_policy" "sample" { name = "TERRAFORM TEST %s" description = "Acceptance test for bundles as ordered list %s" - bundles = [ "1" ] + bundles = [ "1", sysdig_secure_vulnerability_rule_bundle.sample.id ] stages { name = "pipeline" @@ -82,5 +91,5 @@ resource "sysdig_secure_vulnerability_policy" "sample" { } } } -`, suffix, suffix) +`, suffix, suffix, suffix) } From ef3071a600d6e7636203cd11206d61f6b818673a Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 11:20:51 +0200 Subject: [PATCH 09/11] chore: add description to the fields in resource --- ...sysdig_secure_vulnerability_rule_bundle.go | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go index 521a43bc..00cefc26 100644 --- a/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go +++ b/sysdig/resource_sysdig_secure_vulnerability_rule_bundle.go @@ -17,35 +17,42 @@ import ( func vulnerabilityRuleSchemaImageConfigLabel() *schema.Schema { return &schema.Schema{ - Type: schema.TypeSet, - Optional: true, - MaxItems: 1, + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Description: "Defines label-based matching rules for image configuration.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Description: "Internal identifier for the label rule block.", }, "label_must_exist": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Description: "A label key that must exist in the image configuration for the rule to match.", }, "label_must_not_exist": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Description: "A label key that must not exist in the image configuration for the rule to match.", }, "label_must_exist_and_contain_value": { - Type: schema.TypeList, - Optional: true, + Type: schema.TypeList, + Optional: true, + Description: "List of label-value pairs that must exist in the image configuration for the rule to match.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "required_label": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Description: "Label key that must exist in the image configuration.", }, "required_value": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Description: "Expected value for the given label key.", }, }, }, From 6ff64167e133045b9cb16d5aa004e8b90d9c87d2 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Thu, 24 Jul 2025 11:48:34 +0200 Subject: [PATCH 10/11] docs: add documentation for secure_vulnerability_rule_bundle --- .../r/secure_vulnerability_rule_bundle.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 website/docs/r/secure_vulnerability_rule_bundle.md diff --git a/website/docs/r/secure_vulnerability_rule_bundle.md b/website/docs/r/secure_vulnerability_rule_bundle.md new file mode 100644 index 00000000..901690ea --- /dev/null +++ b/website/docs/r/secure_vulnerability_rule_bundle.md @@ -0,0 +1,78 @@ +--- +subcategory: "Sysdig Secure" +layout: "sysdig" +page_title: "Sysdig: sysdig_secure_vulnerability_rule_bundle" +description: |- + Creates a Sysdig Secure Vulnerability Rule Bundle. +--- + +# Resource: sysdig_secure_vulnerability_rule_bundle + +Creates a Sysdig Secure Vulnerability Rule Bundle to define custom rules for vulnerability management, supporting various types of rules. + +-> **Note:** Sysdig Terraform Provider is under rapid development at this point. If you experience any issue or discrepancy while using it, please make sure you have the latest version. If the issue persists, or you have a Feature Request to support an additional set of resources, please open a [new issue](https://github.com/sysdiglabs/terraform-provider-sysdig/issues/new) in the GitHub repository. + +## Example Usage + +```terraform +resource "sysdig_secure_vulnerability_rule_bundle" "example" { + name = "Example Rule Bundle" + + rule { + image_label { + label_must_exist = "required-label" + } + } + + rule { + image_label { + label_must_not_exist = "forbidden-label" + } + } + + rule { + image_label { + label_must_exist_and_contain_value { + required_label = "required-label" + required_value = "required-value" + } + } + } +} +``` + +## Argument Reference + +* `name` - (Required) The name of the vulnerability rule bundle. + +* `description` - (Optional) A description for the rule bundle. + +* `rule` - (Required) List of rule definitions. Each rule supports multiple types (e.g., `image_label`). Each type may have different required attributes: + +### Rule Types + +#### image_label + +Defines label-based matching rules for image configuration. Only one of the following attributes must be specified: + +* `label_must_exist` - (Optional) Label key that must exist in the image configuration. +* `label_must_not_exist` - (Optional) Label key that must not exist in the image configuration. +* `label_must_exist_and_contain_value` - (Optional) List of required label-value pairs, each containing: + + * `required_label` - (Required) Label key required in the image configuration. + * `required_value` - (Required) Value that the label must contain. + +## Attributes Reference + +The following attributes are exported: + +* `identifier` - External identifier computed after creation. Not to be used with the `secure_vulnerability_policy.bundles` field, use `id` for that. + +## Import + +Vulnerability rule bundles can be imported using their bundle ID, for example: + +```shell +$ terraform import sysdig_secure_vulnerability_rule_bundle.example bundle_123456 +``` + From 131539dea69493bb627bd4c064c29fb1fed347a1 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Mon, 28 Jul 2025 17:52:39 +0200 Subject: [PATCH 11/11] fix(docs): add required line by tfproviderdocs lint --- website/docs/r/secure_vulnerability_rule_bundle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/secure_vulnerability_rule_bundle.md b/website/docs/r/secure_vulnerability_rule_bundle.md index 901690ea..fd2e2b25 100644 --- a/website/docs/r/secure_vulnerability_rule_bundle.md +++ b/website/docs/r/secure_vulnerability_rule_bundle.md @@ -64,7 +64,7 @@ Defines label-based matching rules for image configuration. Only one of the foll ## Attributes Reference -The following attributes are exported: +In addition to all arguments above, the following attributes are exported: * `identifier` - External identifier computed after creation. Not to be used with the `secure_vulnerability_policy.bundles` field, use `id` for that.