From f2d44421feec071fa38ae12fd41d114bce50188e Mon Sep 17 00:00:00 2001 From: Aditya Kumar Date: Wed, 20 Nov 2024 21:05:13 +0530 Subject: [PATCH 1/2] added cs providers --- provider/provider.go | 4 + provider/resource_custom_signature_alert.go | 640 ++++++++++++++++++ provider/resource_custom_signature_allow.go | 472 +++++++++++++ provider/resource_custom_signature_block.go | 481 +++++++++++++ provider/resource_custom_signature_testing.go | 630 +++++++++++++++++ provider/util.go | 42 ++ 6 files changed, 2269 insertions(+) create mode 100644 provider/resource_custom_signature_alert.go create mode 100644 provider/resource_custom_signature_allow.go create mode 100644 provider/resource_custom_signature_block.go create mode 100644 provider/resource_custom_signature_testing.go diff --git a/provider/provider.go b/provider/provider.go index 613934f..b5da832 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -35,6 +35,10 @@ func Provider() *schema.Provider { // "traceable_api_exclusion_rule": resourceApiExclusionRule(), "traceable_label_creation_rule": resourceLabelCreationRule(), // "traceable_agent_token": resourceAgentToken(), + "traceable_custom_signature_allow": resourceCustomSignatureAllowRule(), + "traceable_custom_signature_block": resourceCustomSignatureBlockRule(), + "traceable_custom_signature_alert": resourceCustomSignatureAlertRule(), + "traceable_custom_signature_testing": resourceCustomSignatureTestingRule(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/provider/resource_custom_signature_alert.go b/provider/resource_custom_signature_alert.go new file mode 100644 index 0000000..e2e62be --- /dev/null +++ b/provider/resource_custom_signature_alert.go @@ -0,0 +1,640 @@ +package provider + +import ( + "encoding/json" + "fmt" + "strings" + "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCustomSignatureAlertRule() *schema.Resource { + return &schema.Resource{ + Create: resourceCustomSignatureAlertCreate, + Read: resourceCustomSignatureAlertRead, + Update: resourceCustomSignatureAlertUpdate, + Delete: resourceCustomSignatureAlertDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Name of the custom signature allow rule", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "Description of the custom signature allow rule", + Optional: true, + }, + "environments": { + Type: schema.TypeSet, + Description: "Environment of the custom signature allow rule", + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "req_res_conditions": { + Type: schema.TypeList, + Description: "Request/Response conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_key": { + Type: schema.TypeString, + Required: true, + }, + "match_category": { + Type: schema.TypeString, + Required: true, + }, + "match_operator": { + Type: schema.TypeString, + Required: true, + }, + "match_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "attribute_based_conditions": { + Type: schema.TypeList, + Description: "Attribute based conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "operator": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "inject_request_headers": { + Type: schema.TypeList, + Description: "Inject Data in Request header?", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_key": { + Type: schema.TypeString, + Required: true, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "custom_sec_rule": { + Type: schema.TypeString, + Description: "Custom sec rule", + Optional: true, + StateFunc: func(val interface{}) string { + return strings.TrimSpace(escapeString(val.(string))) + }, + }, + "block_expiry_duration":{ + Type: schema.TypeString, + Description: "Time to allow the rule (Time is seconds)", + Optional: true, + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + _, err := ConvertDurationToSeconds(v) + if err != nil { + errs = append(errs, fmt.Errorf("%q must be a valid duration in seconds or ISO 8601 format: %s", key, err)) + } + return + }, + StateFunc: func(val interface{}) string { + v := val.(string) + converted, _ := ConvertDurationToSeconds(v) + return converted + }, + }, + "disabled": { + Type: schema.TypeBool, + Description: "Flag to enable or disable the rule", + Optional: true, + Default: false, + }, + "alert_severity": { + Type: schema.TypeString, + Description: "LOW/MEDIUM/HIGH/CRITICAL", + Required: true, + }, + }, + } +} + +func resourceCustomSignatureAlertCreate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() +// disabled := d.Get("disabled").(bool) + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + attribute_based_conditions := d.Get("attribute_based_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + alert_severity := d.Get("alert_severity").(string) + inject_request_headers := d.Get("inject_request_headers").([]interface{}) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + block_expiry_duration := d.Get("block_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + finalAttributeBasedConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + templateAttributeBasedConditions:=` { + clauseType: ATTRIBUTE_KEY_VALUE_EXPRESSION + attributeKeyValueExpression: { + keyCondition: { operator: %s, value: "%s" } + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + for _,att_based_cond := range attribute_based_conditions{ + att_based_cond_data := att_based_cond.(map[string]interface{}) + finalAttributeBasedConditionsQuery+=fmt.Sprintf(templateAttributeBasedConditions,att_based_cond_data["operator"],att_based_cond_data["value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule=="" && finalAttributeBasedConditionsQuery==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + + finalAgentEffectQuery:="" + agenEffectQueryTemplate:=`{ + agentModificationType: HEADER_INJECTION + headerInjection: { + key: "%s" + value: "%s" + headerCategory: REQUEST + } + }` + + for _,req_header := range inject_request_headers{ + req_header_key:=req_header.(map[string]interface{})["header_key"] + req_header_value:=req_header.(map[string]interface{})["header_value"] + finalAgentEffectQuery+=fmt.Sprintf(agenEffectQueryTemplate,req_header_key,req_header_value) + } + + if finalAgentEffectQuery!=""{ + finalAgentEffectQuery=fmt.Sprintf(`{ + ruleEffectType: AGENT_EFFECT + agentEffect: { + agentModifications: [ + %s + ] + } + }`,finalAgentEffectQuery) + } + expiryDurationString:="" + if block_expiry_duration!=""{ + expiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + createCustomSignatureRule( + create: { + name: "%s" + description: "%s" + ruleEffect: { + eventType: NORMAL_DETECTION, + effects: [ + %s + ] , + eventSeverity: %s + } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,name,description,finalAgentEffectQuery,alert_severity,finalReqResConditionsQuery,customSecRuleQuery,finalAttributeBasedConditionsQuery,envQuery,expiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id := response["data"].(map[string]interface{})["createCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureAlertRead(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + log.Printf("Id from read %s", id) + readQuery:=`{ + customSignatureRules { + results { + id + name + description + disabled + internal + blockingExpirationDuration + blockingExpirationTime + ruleSource + ruleEffect { + eventType + eventSeverity + effects { + ruleEffectType + agentEffect { + agentModifications { + agentModificationType + headerInjection { + key + value + headerCategory + __typename + } + __typename + } + __typename + } + __typename + } + __typename + } + ruleDefinition { + labels { + key + value + __typename + } + clauseGroup { + clauseOperator + clauses { + clauseType + matchExpression { + matchKey + matchOperator + matchValue + matchCategory + __typename + } + keyValueExpression { + keyValueTag + matchKey + matchValue + keyMatchOperator + valueMatchOperator + matchCategory + __typename + } + attributeKeyValueExpression { + keyCondition { + operator + value + __typename + } + valueCondition { + operator + value + __typename + } + __typename + } + customSecRule { + inputSecRuleString + __typename + } + __typename + } + __typename + } + __typename + } + ruleScope { + environmentScope { + environmentIds + __typename + } + __typename + } + __typename + } + __typename + } + }` + responseStr, err := executeQuery(readQuery, meta) + if err != nil { + return err + } + var response map[string]interface{} + if err := json.Unmarshal([]byte(responseStr), &response); err != nil { + return err + } + ruleDetails:=getRuleDetailsFromRulesListUsingIdName(response,"customSignatureRules" ,id) + if len(ruleDetails)==0{ + d.SetId("") + return nil + } + d.Set("name",ruleDetails["name"].(string)) + d.Set("disabled",ruleDetails["disabled"].(bool)) + + reqResConditions := []map[string]interface{}{} + injectedHeaders := []map[string]interface{}{} + attributeBasedConditions := []map[string]interface{}{} + if ruleEffect, ok := ruleDetails["ruleEffect"].(map[string]interface{}); ok { + d.Set("alert_severity",ruleEffect["eventSeverity"].(string)) + if effects,ok := ruleEffect["effects"].([]interface{}); ok { + for _,effect := range effects { + effectMap := effect.(map[string]interface{}) + if effectMap["ruleEffectType"] == "AGENT_EFFECT" { + agentEffect := effectMap["agentEffect"].(map[string]interface{}) + agentModifications := agentEffect["agentModifications"].([]interface{}) + for _,agentModification := range agentModifications{ + agentModificationMap := agentModification.(map[string]interface{}) + injectedHeader := map[string]interface{}{ + "header_key" : agentModificationMap["headerInjection"].(map[string]interface{})["key"].(string), + "header_value" : agentModificationMap["headerInjection"].(map[string]interface{})["value"].(string), + } + injectedHeaders = append(injectedHeaders,injectedHeader) + } + d.Set("inject_request_headers",injectedHeaders) + } + } + } + } + + if ruleDefinition, ok := ruleDetails["ruleDefinition"].(map[string]interface{}); ok { + if clauseGroup, ok := ruleDefinition["clauseGroup"].(map[string]interface{}); ok { + if clauses, ok := clauseGroup["clauses"].([]interface{}); ok { + for _, clause := range clauses { + if clauseMap, ok := clause.(map[string]interface{}); ok { + if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "MATCH_EXPRESSION" { + if matchExpression, ok := clauseMap["matchExpression"].(map[string]interface{}); ok { + reqResCondition := map[string]interface{}{ + "match_key": matchExpression["matchKey"], + "match_category": matchExpression["matchCategory"], + "match_operator": matchExpression["matchOperator"], + "match_value": matchExpression["matchValue"], + } + reqResConditions = append(reqResConditions, reqResCondition) + } + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "CUSTOM_SEC_RULE"{ + d.Set("custom_sec_rule",strings.TrimSpace(escapeString(clauseMap["customSecRule"].(map[string]interface{})["inputSecRuleString"].(string)))) + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "ATTRIBUTE_KEY_VALUE_EXPRESSION"{ + if attributeKeyValueExpression, ok := clauseMap["attributeKeyValueExpression"].(map[string]interface{}); ok { + attributeBasedCondition := map[string]interface{}{ + "operator": attributeKeyValueExpression["keyCondition"].(map[string]interface{})["operator"], + "value": attributeKeyValueExpression["keyCondition"].(map[string]interface{})["value"], + } + attributeBasedConditions = append(attributeBasedConditions, attributeBasedCondition) + } + } + } + } + d.Set("attribute_based_conditions",attributeBasedConditions) + } + } + } + environments := []string{} + + // Extract environment IDs from ruleScope.environmentScope + if ruleScope, ok := ruleDetails["ruleScope"].(map[string]interface{}); ok { + if environmentScope, ok := ruleScope["environmentScope"].(map[string]interface{}); ok { + if environmentIds, ok := environmentScope["environmentIds"].([]interface{}); ok { + for _, envID := range environmentIds { + if envStr, ok := envID.(string); ok { + environments = append(environments, envStr) + } + } + } + } + } + + d.Set("environments",environments) + if blockingExpirationDuration, ok := ruleDetails["blockingExpirationDuration"].(string); ok { + blockingExpirationDuration,_ := ConvertDurationToSeconds(blockingExpirationDuration) + d.Set("block_expiry_duration",blockingExpirationDuration) + } + + return nil +} + +func resourceCustomSignatureAlertUpdate(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + name := d.Get("name").(string) + description:=d.Get("description").(string) + disabled:=d.Get("disabled").(bool) + environments := d.Get("environments").(*schema.Set).List() + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + attribute_based_conditions := d.Get("attribute_based_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + alert_severity := d.Get("alert_severity").(string) + inject_request_headers := d.Get("inject_request_headers").([]interface{}) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + block_expiry_duration := d.Get("block_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + finalAttributeBasedConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + templateAttributeBasedConditions:=` { + clauseType: ATTRIBUTE_KEY_VALUE_EXPRESSION + attributeKeyValueExpression: { + keyCondition: { operator: %s, value: "%s" } + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + for _,att_based_cond := range attribute_based_conditions{ + att_based_cond_data := att_based_cond.(map[string]interface{}) + finalAttributeBasedConditionsQuery+=fmt.Sprintf(templateAttributeBasedConditions,att_based_cond_data["operator"],att_based_cond_data["value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule=="" && finalAttributeBasedConditionsQuery==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + + finalAgentEffectQuery:="" + agenEffectQueryTemplate:=`{ + agentModificationType: HEADER_INJECTION + headerInjection: { + key: "%s" + value: "%s" + headerCategory: REQUEST + } + }` + + for _,req_header := range inject_request_headers{ + req_header_key:=req_header.(map[string]interface{})["header_key"] + req_header_value:=req_header.(map[string]interface{})["header_value"] + finalAgentEffectQuery+=fmt.Sprintf(agenEffectQueryTemplate,req_header_key,req_header_value) + } + + if finalAgentEffectQuery!=""{ + finalAgentEffectQuery=fmt.Sprintf(`{ + ruleEffectType: AGENT_EFFECT + agentEffect: { + agentModifications: [ + %s + ] + } + }`,finalAgentEffectQuery) + } + expiryDurationString:="" + if block_expiry_duration!=""{ + expiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + updateCustomSignatureRule( + update: { + id: "%s" + disabled: %t + name: "%s" + description: "%s" + ruleEffect: { + eventType: NORMAL_DETECTION, + effects: [ + %s + ] , + eventSeverity: %s + } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,id,disabled,name,description,finalAgentEffectQuery,alert_severity,finalReqResConditionsQuery,customSecRuleQuery,finalAttributeBasedConditionsQuery,envQuery,expiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id = response["data"].(map[string]interface{})["updateCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureAlertDelete(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + query := fmt.Sprintf(`mutation { + deleteCustomSignatureRule(delete: {id: "%s"}) { + success + __typename + } + }`, id) + _, err := executeQuery(query, meta) + if err != nil { + return err + } + d.SetId("") + return nil +} \ No newline at end of file diff --git a/provider/resource_custom_signature_allow.go b/provider/resource_custom_signature_allow.go new file mode 100644 index 0000000..d49c0ed --- /dev/null +++ b/provider/resource_custom_signature_allow.go @@ -0,0 +1,472 @@ +package provider + +import ( + "encoding/json" + "fmt" + "strings" + "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCustomSignatureAllowRule() *schema.Resource { + return &schema.Resource{ + Create: resourceCustomSignatureAllowCreate, + Read: resourceCustomSignatureAllowRead, + Update: resourceCustomSignatureAllowUpdate, + Delete: resourceCustomSignatureAllowDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Name of the custom signature allow rule", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "Description of the custom signature allow rule", + Optional: true, + }, + "environments": { + Type: schema.TypeSet, + Description: "Environment of the custom signature allow rule", + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "req_res_conditions": { + Type: schema.TypeList, + Description: "Request/Response conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_key": { + Type: schema.TypeString, + Required: true, + }, + "match_category": { + Type: schema.TypeString, + Required: true, + }, + "match_operator": { + Type: schema.TypeString, + Required: true, + }, + "match_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "custom_sec_rule": { + Type: schema.TypeString, + Description: "Custom sec rule", + Optional: true, + StateFunc: func(val interface{}) string { + + // Process the string using the escape function + return strings.TrimSpace(escapeString(val.(string))) + }, + }, + "allow_expiry_duration":{ + Type: schema.TypeString, + Description: "Time to allow the rule (Time is seconds)", + Optional: true, + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + _, err := ConvertDurationToSeconds(v) + if err != nil { + errs = append(errs, fmt.Errorf("%q must be a valid duration in seconds or ISO 8601 format: %s", key, err)) + } + return + }, + StateFunc: func(val interface{}) string { + v := val.(string) + converted, _ := ConvertDurationToSeconds(v) + return converted + }, + }, + "disabled": { + Type: schema.TypeBool, + Description: "Flag to enable or disable the rule", + Optional: true, + Default: false, + }, + }, + } +} + +func resourceCustomSignatureAllowCreate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() +// disabled := d.Get("disabled").(bool) + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + allow_expiry_duration := d.Get("allow_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + exipiryDurationString:="" + if allow_expiry_duration!=""{ + exipiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,allow_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + createCustomSignatureRule( + create: { + name: "%s" + description: "%s" + ruleEffect: { eventType: ALLOW, effects: [] } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,name,description,finalReqResConditionsQuery,customSecRuleQuery,envQuery,exipiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id := response["data"].(map[string]interface{})["createCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureAllowRead(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + log.Printf("Id from read %s", id) + readQuery:=`{ + customSignatureRules { + results { + id + name + description + disabled + internal + blockingExpirationDuration + blockingExpirationTime + ruleSource + ruleEffect { + eventType + eventSeverity + effects { + ruleEffectType + agentEffect { + agentModifications { + agentModificationType + headerInjection { + key + value + headerCategory + __typename + } + __typename + } + __typename + } + __typename + } + __typename + } + ruleDefinition { + labels { + key + value + __typename + } + clauseGroup { + clauseOperator + clauses { + clauseType + matchExpression { + matchKey + matchOperator + matchValue + matchCategory + __typename + } + keyValueExpression { + keyValueTag + matchKey + matchValue + keyMatchOperator + valueMatchOperator + matchCategory + __typename + } + attributeKeyValueExpression { + keyCondition { + operator + value + __typename + } + valueCondition { + operator + value + __typename + } + __typename + } + customSecRule { + inputSecRuleString + __typename + } + __typename + } + __typename + } + __typename + } + ruleScope { + environmentScope { + environmentIds + __typename + } + __typename + } + __typename + } + __typename + } + }` + responseStr, err := executeQuery(readQuery, meta) + if err != nil { + return err + } + var response map[string]interface{} + if err := json.Unmarshal([]byte(responseStr), &response); err != nil { + return err + } + ruleDetails:=getRuleDetailsFromRulesListUsingIdName(response,"customSignatureRules" ,id) + if len(ruleDetails)==0{ + d.SetId("") + return nil + } + d.Set("name",ruleDetails["name"].(string)) + d.Set("disabled",ruleDetails["disabled"].(bool)) + reqResConditions := []map[string]interface{}{} + + if ruleDefinition, ok := ruleDetails["ruleDefinition"].(map[string]interface{}); ok { + if clauseGroup, ok := ruleDefinition["clauseGroup"].(map[string]interface{}); ok { + if clauses, ok := clauseGroup["clauses"].([]interface{}); ok { + for _, clause := range clauses { + if clauseMap, ok := clause.(map[string]interface{}); ok { + if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "MATCH_EXPRESSION" { + if matchExpression, ok := clauseMap["matchExpression"].(map[string]interface{}); ok { + reqResCondition := map[string]interface{}{ + "match_key": matchExpression["matchKey"], + "match_category": matchExpression["matchCategory"], + "match_operator": matchExpression["matchOperator"], + "match_value": matchExpression["matchValue"], + } + reqResConditions = append(reqResConditions, reqResCondition) + } + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "CUSTOM_SEC_RULE"{ + d.Set("custom_sec_rule",strings.TrimSpace(escapeString(clauseMap["customSecRule"].(map[string]interface{})["inputSecRuleString"].(string)))) + } + } + } + d.Set("req_res_conditions",reqResConditions) + } + } + } + environments := []string{} + + // Extract environment IDs from ruleScope.environmentScope + if ruleScope, ok := ruleDetails["ruleScope"].(map[string]interface{}); ok { + if environmentScope, ok := ruleScope["environmentScope"].(map[string]interface{}); ok { + if environmentIds, ok := environmentScope["environmentIds"].([]interface{}); ok { + for _, envID := range environmentIds { + if envStr, ok := envID.(string); ok { + environments = append(environments, envStr) + } + } + } + } + } + + d.Set("environments",environments) + if blockingExpirationDuration, ok := ruleDetails["blockingExpirationDuration"].(string); ok { + blockingExpirationDuration,_ := ConvertDurationToSeconds(blockingExpirationDuration) + d.Set("allow_expiry_duration",blockingExpirationDuration) + } + + return nil +} + +func resourceCustomSignatureAllowUpdate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + id := d.Id() + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + allow_expiry_duration := d.Get("allow_expiry_duration").(string) + + disabled := d.Get("disabled").(bool) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + exipiryDurationString:="" + if allow_expiry_duration!=""{ + exipiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,allow_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + updateCustomSignatureRule( + update: { + name: "%s" + description: "%s" + ruleEffect: { eventType: ALLOW, effects: [] } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + ] + } + } + %s + %s + id: "%s" + disabled: %t + } + ) { + id + __typename + } + } + `,name,description,finalReqResConditionsQuery,customSecRuleQuery,envQuery,exipiryDurationString,id,disabled) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + updatedId := response["data"].(map[string]interface{})["updateCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(updatedId) + return nil +} + +func resourceCustomSignatureAllowDelete(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + query := fmt.Sprintf(`mutation { + deleteCustomSignatureRule(delete: {id: "%s"}) { + success + __typename + } + }`, id) + _, err := executeQuery(query, meta) + if err != nil { + return err + } + d.SetId("") + return nil +} \ No newline at end of file diff --git a/provider/resource_custom_signature_block.go b/provider/resource_custom_signature_block.go new file mode 100644 index 0000000..074d1aa --- /dev/null +++ b/provider/resource_custom_signature_block.go @@ -0,0 +1,481 @@ +package provider + +import ( + "encoding/json" + "fmt" + "strings" + "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCustomSignatureBlockRule() *schema.Resource { + return &schema.Resource{ + Create: resourceCustomSignatureBlockCreate, + Read: resourceCustomSignatureBlockRead, + Update: resourceCustomSignatureBlockUpdate, + Delete: resourceCustomSignatureBlockDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Name of the custom signature allow rule", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "Description of the custom signature allow rule", + Optional: true, + }, + "environments": { + Type: schema.TypeSet, + Description: "Environment of the custom signature allow rule", + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "req_res_conditions": { + Type: schema.TypeList, + Description: "Request/Response conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_key": { + Type: schema.TypeString, + Required: true, + }, + "match_category": { + Type: schema.TypeString, + Required: true, + }, + "match_operator": { + Type: schema.TypeString, + Required: true, + }, + "match_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "custom_sec_rule": { + Type: schema.TypeString, + Description: "Custom sec rule", + Optional: true, + StateFunc: func(val interface{}) string { + + // Process the string using the escape function + return strings.TrimSpace(escapeString(val.(string))) + }, + }, + "block_expiry_duration":{ + Type: schema.TypeString, + Description: "Time to allow the rule (Time is seconds)", + Optional: true, + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + _, err := ConvertDurationToSeconds(v) + if err != nil { + errs = append(errs, fmt.Errorf("%q must be a valid duration in seconds or ISO 8601 format: %s", key, err)) + } + return + }, + StateFunc: func(val interface{}) string { + v := val.(string) + converted, _ := ConvertDurationToSeconds(v) + return converted + }, + }, + "disabled": { + Type: schema.TypeBool, + Description: "Flag to enable or disable the rule", + Optional: true, + Default: false, + }, + "alert_severity": { + Type: schema.TypeString, + Description: "LOW/MEDIUM/HIGH/CRITICAL", + Required: true, + }, + }, + } +} + +func resourceCustomSignatureBlockCreate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() +// disabled := d.Get("disabled").(bool) + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + alert_severity := d.Get("alert_severity").(string) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + block_expiry_duration := d.Get("block_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + exipiryDurationString:="" + if block_expiry_duration!=""{ + exipiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + createCustomSignatureRule( + create: { + name: "%s" + description: "%s" + ruleEffect: { eventType: DETECTION_AND_BLOCKING, effects: [] ,eventSeverity: %s} + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,name,description,alert_severity,finalReqResConditionsQuery,customSecRuleQuery,envQuery,exipiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id := response["data"].(map[string]interface{})["createCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureBlockRead(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + log.Printf("Id from read %s", id) + readQuery:=`{ + customSignatureRules { + results { + id + name + description + disabled + internal + blockingExpirationDuration + blockingExpirationTime + ruleSource + ruleEffect { + eventType + eventSeverity + effects { + ruleEffectType + agentEffect { + agentModifications { + agentModificationType + headerInjection { + key + value + headerCategory + __typename + } + __typename + } + __typename + } + __typename + } + __typename + } + ruleDefinition { + labels { + key + value + __typename + } + clauseGroup { + clauseOperator + clauses { + clauseType + matchExpression { + matchKey + matchOperator + matchValue + matchCategory + __typename + } + keyValueExpression { + keyValueTag + matchKey + matchValue + keyMatchOperator + valueMatchOperator + matchCategory + __typename + } + attributeKeyValueExpression { + keyCondition { + operator + value + __typename + } + valueCondition { + operator + value + __typename + } + __typename + } + customSecRule { + inputSecRuleString + __typename + } + __typename + } + __typename + } + __typename + } + ruleScope { + environmentScope { + environmentIds + __typename + } + __typename + } + __typename + } + __typename + } + }` + responseStr, err := executeQuery(readQuery, meta) + if err != nil { + return err + } + var response map[string]interface{} + if err := json.Unmarshal([]byte(responseStr), &response); err != nil { + return err + } + ruleDetails:=getRuleDetailsFromRulesListUsingIdName(response,"customSignatureRules" ,id) + if len(ruleDetails)==0{ + d.SetId("") + return nil + } + d.Set("name",ruleDetails["name"].(string)) + d.Set("disabled",ruleDetails["disabled"].(bool)) + reqResConditions := []map[string]interface{}{} + if ruleEffect, ok := ruleDetails["ruleEffect"].(map[string]interface{}); ok { + d.Set("alert_severity",ruleEffect["eventSeverity"].(string)) + } + if ruleDefinition, ok := ruleDetails["ruleDefinition"].(map[string]interface{}); ok { + if clauseGroup, ok := ruleDefinition["clauseGroup"].(map[string]interface{}); ok { + if clauses, ok := clauseGroup["clauses"].([]interface{}); ok { + for _, clause := range clauses { + if clauseMap, ok := clause.(map[string]interface{}); ok { + if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "MATCH_EXPRESSION" { + if matchExpression, ok := clauseMap["matchExpression"].(map[string]interface{}); ok { + reqResCondition := map[string]interface{}{ + "match_key": matchExpression["matchKey"], + "match_category": matchExpression["matchCategory"], + "match_operator": matchExpression["matchOperator"], + "match_value": matchExpression["matchValue"], + } + reqResConditions = append(reqResConditions, reqResCondition) + } + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "CUSTOM_SEC_RULE"{ + d.Set("custom_sec_rule",strings.TrimSpace(escapeString(clauseMap["customSecRule"].(map[string]interface{})["inputSecRuleString"].(string)))) + } + } + } + d.Set("req_res_conditions",reqResConditions) + } + } + } + environments := []string{} + + // Extract environment IDs from ruleScope.environmentScope + if ruleScope, ok := ruleDetails["ruleScope"].(map[string]interface{}); ok { + if environmentScope, ok := ruleScope["environmentScope"].(map[string]interface{}); ok { + if environmentIds, ok := environmentScope["environmentIds"].([]interface{}); ok { + for _, envID := range environmentIds { + if envStr, ok := envID.(string); ok { + environments = append(environments, envStr) + } + } + } + } + } + + d.Set("environments",environments) + if blockingExpirationDuration, ok := ruleDetails["blockingExpirationDuration"].(string); ok { + blockingExpirationDuration,_ := ConvertDurationToSeconds(blockingExpirationDuration) + d.Set("block_expiry_duration",blockingExpirationDuration) + } + + return nil +} + +func resourceCustomSignatureBlockUpdate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + id := d.Id() + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + alert_severity := d.Get("alert_severity").(string) + block_expiry_duration := d.Get("block_expiry_duration").(string) + + disabled := d.Get("disabled").(bool) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + exipiryDurationString:="" + if block_expiry_duration!=""{ + exipiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + updateCustomSignatureRule( + update: { + name: "%s" + description: "%s" + ruleEffect: { eventType: DETECTION_AND_BLOCKING, effects: [] , eventSeverity: %s} + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + ] + } + } + %s + %s + id: "%s" + disabled: %t + } + ) { + id + __typename + } + } + `,name,description,alert_severity,finalReqResConditionsQuery,customSecRuleQuery,envQuery,exipiryDurationString,id,disabled) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + updatedId := response["data"].(map[string]interface{})["updateCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(updatedId) + return nil +} + +func resourceCustomSignatureBlockDelete(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + query := fmt.Sprintf(`mutation { + deleteCustomSignatureRule(delete: {id: "%s"}) { + success + __typename + } + }`, id) + _, err := executeQuery(query, meta) + if err != nil { + return err + } + d.SetId("") + return nil +} \ No newline at end of file diff --git a/provider/resource_custom_signature_testing.go b/provider/resource_custom_signature_testing.go new file mode 100644 index 0000000..e0257dd --- /dev/null +++ b/provider/resource_custom_signature_testing.go @@ -0,0 +1,630 @@ +package provider + +import ( + "encoding/json" + "fmt" + "strings" + "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCustomSignatureTestingRule() *schema.Resource { + return &schema.Resource{ + Create: resourceCustomSignatureTestingCreate, + Read: resourceCustomSignatureTestingRead, + Update: resourceCustomSignatureTestingUpdate, + Delete: resourceCustomSignatureTestingDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Name of the custom signature allow rule", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "Description of the custom signature allow rule", + Optional: true, + }, + "environments": { + Type: schema.TypeSet, + Description: "Environment of the custom signature allow rule", + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "req_res_conditions": { + Type: schema.TypeList, + Description: "Request/Response conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match_key": { + Type: schema.TypeString, + Required: true, + }, + "match_category": { + Type: schema.TypeString, + Required: true, + }, + "match_operator": { + Type: schema.TypeString, + Required: true, + }, + "match_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "attribute_based_conditions": { + Type: schema.TypeList, + Description: "Attribute based conditions for the rule", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "operator": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "inject_request_headers": { + Type: schema.TypeList, + Description: "Inject Data in Request header?", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "header_key": { + Type: schema.TypeString, + Required: true, + }, + "header_value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "custom_sec_rule": { + Type: schema.TypeString, + Description: "Custom sec rule", + Optional: true, + StateFunc: func(val interface{}) string { + return strings.TrimSpace(escapeString(val.(string))) + }, + }, + "block_expiry_duration":{ + Type: schema.TypeString, + Description: "Time to allow the rule (Time is seconds)", + Optional: true, + ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { + v := val.(string) + _, err := ConvertDurationToSeconds(v) + if err != nil { + errs = append(errs, fmt.Errorf("%q must be a valid duration in seconds or ISO 8601 format: %s", key, err)) + } + return + }, + StateFunc: func(val interface{}) string { + v := val.(string) + converted, _ := ConvertDurationToSeconds(v) + return converted + }, + }, + "disabled": { + Type: schema.TypeBool, + Description: "Flag to enable or disable the rule", + Optional: true, + Default: false, + }, + }, + } +} + +func resourceCustomSignatureTestingCreate(d *schema.ResourceData, meta interface{}) error { + name := d.Get("name").(string) + description:=d.Get("description").(string) + environments := d.Get("environments").(*schema.Set).List() +// disabled := d.Get("disabled").(bool) + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + attribute_based_conditions := d.Get("attribute_based_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + inject_request_headers := d.Get("inject_request_headers").([]interface{}) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + block_expiry_duration := d.Get("block_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + finalAttributeBasedConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + templateAttributeBasedConditions:=` { + clauseType: ATTRIBUTE_KEY_VALUE_EXPRESSION + attributeKeyValueExpression: { + keyCondition: { operator: %s, value: "%s" } + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + for _,att_based_cond := range attribute_based_conditions{ + att_based_cond_data := att_based_cond.(map[string]interface{}) + finalAttributeBasedConditionsQuery+=fmt.Sprintf(templateAttributeBasedConditions,att_based_cond_data["operator"],att_based_cond_data["value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule=="" && finalAttributeBasedConditionsQuery==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + + finalAgentEffectQuery:="" + agenEffectQueryTemplate:=`{ + agentModificationType: HEADER_INJECTION + headerInjection: { + key: "%s" + value: "%s" + headerCategory: REQUEST + } + }` + + for _,req_header := range inject_request_headers{ + req_header_key:=req_header.(map[string]interface{})["header_key"] + req_header_value:=req_header.(map[string]interface{})["header_value"] + finalAgentEffectQuery+=fmt.Sprintf(agenEffectQueryTemplate,req_header_key,req_header_value) + } + + if finalAgentEffectQuery!=""{ + finalAgentEffectQuery=fmt.Sprintf(`{ + ruleEffectType: AGENT_EFFECT + agentEffect: { + agentModifications: [ + %s + ] + } + }`,finalAgentEffectQuery) + } + expiryDurationString:="" + if block_expiry_duration!=""{ + expiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + createCustomSignatureRule( + create: { + name: "%s" + description: "%s" + ruleEffect: { + eventType: NORMAL_DETECTION, + effects: [ + %s + ] + } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,name,description,finalAgentEffectQuery,finalReqResConditionsQuery,customSecRuleQuery,finalAttributeBasedConditionsQuery,envQuery,expiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id := response["data"].(map[string]interface{})["createCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureTestingRead(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + log.Printf("Id from read %s", id) + readQuery:=`{ + customSignatureRules { + results { + id + name + description + disabled + internal + blockingExpirationDuration + blockingExpirationTime + ruleSource + ruleEffect { + eventType + eventSeverity + effects { + ruleEffectType + agentEffect { + agentModifications { + agentModificationType + headerInjection { + key + value + headerCategory + __typename + } + __typename + } + __typename + } + __typename + } + __typename + } + ruleDefinition { + labels { + key + value + __typename + } + clauseGroup { + clauseOperator + clauses { + clauseType + matchExpression { + matchKey + matchOperator + matchValue + matchCategory + __typename + } + keyValueExpression { + keyValueTag + matchKey + matchValue + keyMatchOperator + valueMatchOperator + matchCategory + __typename + } + attributeKeyValueExpression { + keyCondition { + operator + value + __typename + } + valueCondition { + operator + value + __typename + } + __typename + } + customSecRule { + inputSecRuleString + __typename + } + __typename + } + __typename + } + __typename + } + ruleScope { + environmentScope { + environmentIds + __typename + } + __typename + } + __typename + } + __typename + } + }` + responseStr, err := executeQuery(readQuery, meta) + if err != nil { + return err + } + var response map[string]interface{} + if err := json.Unmarshal([]byte(responseStr), &response); err != nil { + return err + } + ruleDetails:=getRuleDetailsFromRulesListUsingIdName(response,"customSignatureRules" ,id) + if len(ruleDetails)==0{ + d.SetId("") + return nil + } + d.Set("name",ruleDetails["name"].(string)) + d.Set("disabled",ruleDetails["disabled"].(bool)) + + reqResConditions := []map[string]interface{}{} + injectedHeaders := []map[string]interface{}{} + attributeBasedConditions := []map[string]interface{}{} + if ruleEffect, ok := ruleDetails["ruleEffect"].(map[string]interface{}); ok { + if effects,ok := ruleEffect["effects"].([]interface{}); ok { + for _,effect := range effects { + effectMap := effect.(map[string]interface{}) + if effectMap["ruleEffectType"].(string) == "AGENT_EFFECT" { + agentEffect := effectMap["agentEffect"].(map[string]interface{}) + agentModifications := agentEffect["agentModifications"].([]interface{}) + for _,agentModification := range agentModifications{ + agentModificationMap := agentModification.(map[string]interface{}) + injectedHeader := map[string]interface{}{ + "header_key" : agentModificationMap["headerInjection"].(map[string]interface{})["key"].(string), + "header_value" : agentModificationMap["headerInjection"].(map[string]interface{})["value"].(string), + } + injectedHeaders = append(injectedHeaders,injectedHeader) + } + d.Set("inject_request_headers",injectedHeaders) + } + } + } + } + + if ruleDefinition, ok := ruleDetails["ruleDefinition"].(map[string]interface{}); ok { + if clauseGroup, ok := ruleDefinition["clauseGroup"].(map[string]interface{}); ok { + if clauses, ok := clauseGroup["clauses"].([]interface{}); ok { + for _, clause := range clauses { + if clauseMap, ok := clause.(map[string]interface{}); ok { + if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "MATCH_EXPRESSION" { + if matchExpression, ok := clauseMap["matchExpression"].(map[string]interface{}); ok { + reqResCondition := map[string]interface{}{ + "match_key": matchExpression["matchKey"], + "match_category": matchExpression["matchCategory"], + "match_operator": matchExpression["matchOperator"], + "match_value": matchExpression["matchValue"], + } + reqResConditions = append(reqResConditions, reqResCondition) + } + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "CUSTOM_SEC_RULE"{ + d.Set("custom_sec_rule",strings.TrimSpace(escapeString(clauseMap["customSecRule"].(map[string]interface{})["inputSecRuleString"].(string)))) + }else if clauseType, exists := clauseMap["clauseType"].(string); exists && clauseType == "ATTRIBUTE_KEY_VALUE_EXPRESSION"{ + if attributeKeyValueExpression, ok := clauseMap["attributeKeyValueExpression"].(map[string]interface{}); ok { + attributeBasedCondition := map[string]interface{}{ + "operator": attributeKeyValueExpression["keyCondition"].(map[string]interface{})["operator"], + "value": attributeKeyValueExpression["keyCondition"].(map[string]interface{})["value"], + } + attributeBasedConditions = append(attributeBasedConditions, attributeBasedCondition) + } + } + } + } + d.Set("attribute_based_conditions",attributeBasedConditions) + } + } + } + environments := []string{} + + // Extract environment IDs from ruleScope.environmentScope + if ruleScope, ok := ruleDetails["ruleScope"].(map[string]interface{}); ok { + if environmentScope, ok := ruleScope["environmentScope"].(map[string]interface{}); ok { + if environmentIds, ok := environmentScope["environmentIds"].([]interface{}); ok { + for _, envID := range environmentIds { + if envStr, ok := envID.(string); ok { + environments = append(environments, envStr) + } + } + } + } + } + + d.Set("environments",environments) + if blockingExpirationDuration, ok := ruleDetails["blockingExpirationDuration"].(string); ok { + blockingExpirationDuration,_ := ConvertDurationToSeconds(blockingExpirationDuration) + d.Set("block_expiry_duration",blockingExpirationDuration) + } + + return nil +} + +func resourceCustomSignatureTestingUpdate(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + name := d.Get("name").(string) + description:=d.Get("description").(string) + disabled:=d.Get("disabled").(bool) + environments := d.Get("environments").(*schema.Set).List() + req_res_conditions := d.Get("req_res_conditions").([]interface{}) + attribute_based_conditions := d.Get("attribute_based_conditions").([]interface{}) + custom_sec_rule := d.Get("custom_sec_rule").(string) + inject_request_headers := d.Get("inject_request_headers").([]interface{}) + custom_sec_rule=strings.TrimSpace(escapeString(custom_sec_rule)) + + block_expiry_duration := d.Get("block_expiry_duration").(string) + var envList []string + for _, env := range environments { + envList = append(envList, fmt.Sprintf(`"%s"`, env.(string))) + } + envQuery:="" + if len(environments)!=0{ + envQuery=fmt.Sprintf(`ruleScope: { + environmentScope: { environmentIds: [%s] } + }`,strings.Join(envList, ",")) + } + finalReqResConditionsQuery:="" + finalAttributeBasedConditionsQuery:="" + templateReqResConditions:=`{ + clauseType: MATCH_EXPRESSION + matchExpression: { + matchKey: %s + matchCategory: %s + matchOperator: %s + matchValue: "%s" + } + }` + templateAttributeBasedConditions:=` { + clauseType: ATTRIBUTE_KEY_VALUE_EXPRESSION + attributeKeyValueExpression: { + keyCondition: { operator: %s, value: "%s" } + } + }` + for _,req_res_cond := range req_res_conditions { + req_res_cond_data := req_res_cond.(map[string]interface{}) + finalReqResConditionsQuery+=fmt.Sprintf(templateReqResConditions,req_res_cond_data["match_key"],req_res_cond_data["match_category"],req_res_cond_data["match_operator"],req_res_cond_data["match_value"]) + } + + for _,att_based_cond := range attribute_based_conditions{ + att_based_cond_data := att_based_cond.(map[string]interface{}) + finalAttributeBasedConditionsQuery+=fmt.Sprintf(templateAttributeBasedConditions,att_based_cond_data["operator"],att_based_cond_data["value"]) + } + + if finalReqResConditionsQuery=="" && custom_sec_rule=="" && finalAttributeBasedConditionsQuery==""{ + return fmt.Errorf("please provide on of finalReqResConditionsQuery or custom_sec_rule") + } + + customSecRuleQuery:="" + if custom_sec_rule!=""{ + customSecRuleQuery=fmt.Sprintf(`{ + clauseType: CUSTOM_SEC_RULE + customSecRule: { + inputSecRuleString: "%s" + } + }`,custom_sec_rule) + } + + finalAgentEffectQuery:="" + agenEffectQueryTemplate:=`{ + agentModificationType: HEADER_INJECTION + headerInjection: { + key: "%s" + value: "%s" + headerCategory: REQUEST + } + }` + + for _,req_header := range inject_request_headers{ + req_header_key:=req_header.(map[string]interface{})["header_key"] + req_header_value:=req_header.(map[string]interface{})["header_value"] + finalAgentEffectQuery+=fmt.Sprintf(agenEffectQueryTemplate,req_header_key,req_header_value) + } + + if finalAgentEffectQuery!=""{ + finalAgentEffectQuery=fmt.Sprintf(`{ + ruleEffectType: AGENT_EFFECT + agentEffect: { + agentModifications: [ + %s + ] + } + }`,finalAgentEffectQuery) + } + expiryDurationString:="" + if block_expiry_duration!=""{ + expiryDurationString=fmt.Sprintf(`blockingExpirationDuration: "%s"`,block_expiry_duration) + } + + query:=fmt.Sprintf(`mutation { + updateCustomSignatureRule( + update: { + id: "%s" + disabled: %t + name: "%s" + description: "%s" + ruleEffect: { + eventType: NORMAL_DETECTION, + effects: [ + %s + ] + } + internal: false + ruleDefinition: { + labels: [] + clauseGroup: { + clauseOperator: AND + clauses: [ + %s + %s + %s + ] + } + } + %s + %s + } + ) { + id + __typename + } + } + `,id,disabled,name,description,finalAgentEffectQuery,finalReqResConditionsQuery,customSecRuleQuery,finalAttributeBasedConditionsQuery,envQuery,expiryDurationString) + + var response map[string]interface{} + responseStr, err := executeQuery(query, meta) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + log.Printf("This is the graphql query %s", query) + log.Printf("This is the graphql response %s", responseStr) + err = json.Unmarshal([]byte(responseStr), &response) + if err != nil { + return fmt.Errorf("Error: %s", err) + } + id = response["data"].(map[string]interface{})["updateCustomSignatureRule"].(map[string]interface{})["id"].(string) + + d.SetId(id) + return nil +} + +func resourceCustomSignatureTestingDelete(d *schema.ResourceData, meta interface{}) error { + id := d.Id() + query := fmt.Sprintf(`mutation { + deleteCustomSignatureRule(delete: {id: "%s"}) { + success + __typename + } + }`, id) + _, err := executeQuery(query, meta) + if err != nil { + return err + } + d.SetId("") + return nil +} \ No newline at end of file diff --git a/provider/util.go b/provider/util.go index 94cf94a..907f209 100644 --- a/provider/util.go +++ b/provider/util.go @@ -4,6 +4,8 @@ import ( "fmt" "log" "strings" + "regexp" + "strconv" ) func listToString(stringArray []string) string { @@ -185,3 +187,43 @@ func jsonifyList(list []interface{}) string { } return "[" + strings.Join(strList, ", ") + "]" } + +func escapeString(input string) string { + lines := strings.Split(input, "\n") + // Trim whitespace and escape each line + for i, line := range lines { +// line = strings.TrimSpace(line) + line = strings.ReplaceAll(line, `\`, `\\`) // Escape backslashes + line = strings.ReplaceAll(line, `"`, `\"`) // Escape double quotes +// line = strings.ReplaceAll(line, `%`, `%%`) // Escape % + lines[i] = line + } + // Join lines with explicit \n + return strings.Join(lines, `\n`) +} + +func ConvertDurationToSeconds(duration string) (string, error) { + re := regexp.MustCompile(`P(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?`) + matches := re.FindStringSubmatch(duration) + if len(matches) == 0 { + return "", fmt.Errorf("invalid duration format: %s", duration) + } + + totalSeconds := 0 + + // Parse hours, minutes, and seconds if present + if matches[2] != "" { // Hours + hours, _ := strconv.Atoi(matches[2]) + totalSeconds += hours * 3600 + } + if matches[3] != "" { // Minutes + minutes, _ := strconv.Atoi(matches[3]) + totalSeconds += minutes * 60 + } + if matches[4] != "" { // Seconds + seconds, _ := strconv.Atoi(matches[4]) + totalSeconds += seconds + } + + return fmt.Sprintf("PT%dS", totalSeconds), nil +} \ No newline at end of file From 22f9001a53f1409375c0ace8d1c6f96adf88c511 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Date: Thu, 21 Nov 2024 15:38:54 +0530 Subject: [PATCH 2/2] added ex --- main.tf | 26 +++++++++++++++++++++ provider/resource_custom_signature_block.go | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/main.tf b/main.tf index b3c0ed3..a3c6d67 100644 --- a/main.tf +++ b/main.tf @@ -210,4 +210,30 @@ resource "traceable_notification_rule_actor_severity_change" "rule1" { actor_severities = [] actor_ip_reputation_levels = ["HIGH"] +} + +resource traceable_custom_signature_allow "csruletf"{ + name="testtf2" + description="test1" + environments=["fintech-1","demo-test"] + custom_sec_rule=<