Skip to content

Commit

Permalink
Merge pull request #189 from dortam888/add_packages_suuport_for_secur…
Browse files Browse the repository at this point in the history
…ity_policy

Add packages support for security policy
  • Loading branch information
alexhung authored May 29, 2024
2 parents ec5999f + 268bba5 commit 43bce3f
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 14 deletions.
2 changes: 1 addition & 1 deletion docs/resources/custom_issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ resource "xray_custom_issue" "my-issue-1" {
- `cve` (Block Set, Min: 1) CVE of the custom issue (see [below for nested schema](#nestedblock--cve))
- `description` (String) Description of custom issue
- `name` (String) Name of the custom issue. It must not begin with 'xray' (case insensitive)
- `package_type` (String) Package Type of custom issue. Valid values are: alpine, bower, cargo, chef, cocoapods, composer, conan, conda, cran, debian, docker, gems, generic, gitlfs, go, gradle, helm, ivy, maven, npm, nuget, opkg, p2, puppet, pypi, rpm, sbt, swift, terraform, terraformbackend, vagrant, vcs
- `package_type` (String) Package Type of custom issue. Valid values are: alpine, bower, cargo, composer, conan, conda, cran, debian, docker, generic, go, gradle, huggingface, ivy, maven, npm, nuget, oci, pypi, rpm, rubygems, sbt, terraformbe
- `provider_name` (String) Provider of custom issue. It must not be 'jfrog' (case insensitive)
- `severity` (String) Severity of custom issue. Valid values: Critical, High, Medium, Low, Information
- `source` (Block Set, Min: 1) List of sources (see [below for nested schema](#nestedblock--source))
Expand Down
3 changes: 3 additions & 0 deletions docs/resources/security_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ Optional:
- `fix_version_dependant` (Boolean) Default value is `false`. Issues that do not have a fixed version are not generated until a fixed version is available. Must be `false` with `malicious_package` enabled.
- `malicious_package` (Boolean) Default value is `false`. Generating a violation on a malicious package.
- `min_severity` (String) The minimum security vulnerability severity that will be impacted by the policy. Valid values: `All Severities`, `Critical`, `High`, `Medium`, `Low`
- `package_name` (String) The package name to create a rule for
- `package_type` (String) The package type to create a rule for
- `package_versions` (Set of String) package versions to apply the rule on can be (,) for any version or a open range (1,4) or closed [1,4] or one version [1]
- `vulnerability_ids` (Set of String) Creates policy rules for specific vulnerability IDs that you input. You can add multiple vulnerabilities IDs up to 100. CVEs and Xray IDs are supported. Example - CVE-2015-20107, XRAY-2344

<a id="nestedblock--rule--criteria--cvss_range"></a>
Expand Down
36 changes: 36 additions & 0 deletions pkg/xray/resource/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,27 @@ import (
"github.com/jfrog/terraform-provider-shared/validator"
)

var validPackageTypesSupportedXraySecPolicies = []string{
"alpine",
"cargo",
"composer",
"conan",
"conda",
"cran",
"debian",
"docker",
"generic",
"go",
"huggingface",
"maven",
"npm",
"nuget",
"oci",
"pypi",
"rpm",
"rubygems",
}

var commonActionsSchema = map[string]*schema.Schema{
"webhooks": {
Type: schema.TypeSet,
Expand Down Expand Up @@ -203,6 +224,9 @@ type PolicyRuleCriteria struct {
MaliciousPackage bool `json:"malicious_package,omitempty"`
VulnerabilityIds []string `json:"vulnerability_ids,omitempty"`
Exposures *PolicyExposures `json:"exposures,omitempty"`
PackageName string `json:"package_name,omitempty"`
PackageType string `json:"package_type,omitempty"`
PackageVersions []string `json:"package_versions,omitempty"`
// We use pointer for CVSSRange to address nil-verification for non-primitive types.
// Unlike primitive types, when the non-primitive type in the struct is set
// to nil, the empty key will be created in the JSON body anyway.
Expand Down Expand Up @@ -316,6 +340,15 @@ func unpackSecurityCriteria(tfCriteria map[string]interface{}) *PolicyRuleCriter
if _, ok := tfCriteria["exposures"]; ok {
criteria.Exposures = unpackExposures(tfCriteria["exposures"].([]interface{}))
}
if v, ok := tfCriteria["package_name"]; ok {
criteria.PackageName = v.(string)
}
if v, ok := tfCriteria["package_type"]; ok {
criteria.PackageType = v.(string)
}
if v, ok := tfCriteria["package_versions"]; ok {
criteria.PackageVersions = sdk.CastToStringArr(v.(*schema.Set).List())
}
// This is also picky about not allowing empty values to be set
cvss := unpackCVSSRange(tfCriteria["cvss_range"].([]interface{}))
if cvss == nil {
Expand Down Expand Up @@ -620,6 +653,9 @@ func packSecurityCriteria(criteria *PolicyRuleCriteria) []interface{} {
m["fix_version_dependant"] = criteria.FixVersionDependant
m["malicious_package"] = criteria.MaliciousPackage
m["exposures"] = packExposures(criteria.Exposures)
m["package_name"] = criteria.PackageName
m["package_type"] = criteria.PackageType
m["package_versions"] = criteria.PackageVersions

return []interface{}{m}
}
Expand Down
17 changes: 4 additions & 13 deletions pkg/xray/resource/resource_xray_custom_issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,26 @@ var validPackageTypes = []string{
"alpine",
"bower",
"cargo",
"chef",
"cocoapods",
"composer",
"conan",
"conda",
"cran",
"debian",
"docker",
"gems",
"generic",
"gitlfs",
"go",
"gradle",
"helm",
"huggingface",
"ivy",
"maven",
"npm",
"nuget",
"opkg",
"p2",
"puppet",
"oci",
"pypi",
"rpm",
"rubygems",
"sbt",
"swift",
"terraform",
"terraformbackend",
"vagrant",
"vcs",
"terraformbe",
}

type VulnerableRange struct {
Expand Down
36 changes: 36 additions & 0 deletions pkg/xray/resource/resource_xray_security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ func ResourceXraySecurityPolicyV2() *schema.Resource {
},
},
},
"package_name": {
Type: schema.TypeString,
Optional: true,
Description: "The package name to create a rule for",
},
"package_type": {
Type: schema.TypeString,
Optional: true,
Description: "The package type to create a rule for",
ValidateDiagFunc: validator.StringInSlice(true, validPackageTypesSupportedXraySecPolicies...),
},
"package_versions": {
Type: schema.TypeSet,
Optional: true,
Description: "package versions to apply the rule on can be (,) for any version or an open range (1,4) or closed [1,4] or one version [1]",
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validation.ToDiagFunc(
validation.StringMatch(regexp.MustCompile(`((^(\(|\[)((\d+\.)?(\d+\.)?(\*|\d+)|(\s*))\,((\d+\.)?(\d+\.)?(\*|\d+)|(\s*))(\)|\])$|^\[(\d+\.)?(\d+\.)?(\*|\d+)\]$))`), "invalid Range, must be one of the follows: Any Version: (,) or Specific Version: [1.2], [3] or Range: (1,), [,1.2.3], (4.5.0,6.5.2]"),
),
},
},
}

return &schema.Resource{
Expand Down Expand Up @@ -144,6 +166,10 @@ var criteriaMaliciousPkgDiff = func(ctx context.Context, diff *schema.ResourceDi
vulnerabilityIDs := criterion["vulnerability_ids"].(*schema.Set).List()
maliciousPackage := criterion["malicious_package"].(bool)
exposures := criterion["exposures"].([]interface{})
package_name := criterion["package_name"].(string)
package_type := criterion["package_type"].(string)
package_versions := criterion["package_versions"].(*schema.Set).List()
isPackageSet := len(package_name) > 0 || len(package_type) > 0 || len(package_versions) > 0 //if one of them is not defined the API will return an error guiding which one is missing

if len(exposures) > 0 && maliciousPackage || (len(exposures) > 0 && len(cvssRange) > 0) ||
(len(exposures) > 0 && len(minSeverity) > 0) || (len(exposures) > 0 && len(vulnerabilityIDs) > 0) {
Expand All @@ -165,5 +191,15 @@ var criteriaMaliciousPkgDiff = func(ctx context.Context, diff *schema.ResourceDi
return fmt.Errorf("vulnerability_ids can't be set together with with malicious_package, min_severity, cvss_range and exposures")
}

if (isPackageSet && len(vulnerabilityIDs) > 0) || (isPackageSet && maliciousPackage) ||
(isPackageSet && len(cvssRange) > 0) || (isPackageSet && len(minSeverity) > 0) ||
(isPackageSet && len(exposures) > 0) {
return fmt.Errorf("package_name, package_type and package versions can't be set together with with vulnerability_ids, malicious_package, min_severity, cvss_range and exposures")
}

if isPackageSet && fixVersionDependant {
return fmt.Errorf("fix_version_dependant must be set to false if package type policy is used")
}

return nil
}
155 changes: 155 additions & 0 deletions pkg/xray/resource/resource_xray_security_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const criteriaTypeSeverity = "severity"
const criteriaTypeMaliciousPkg = "malicious_package"
const criteriaTypeVulnerabilityIds = "vulnerability_ids"
const criteriaTypeExposures = "exposures"
const criteriaTypePackageName = "package_name"

var testDataSecurity = map[string]string{
"resource_name": "",
Expand Down Expand Up @@ -664,6 +665,7 @@ func TestAccSecurityPolicy_conflictingAttributesFail(t *testing.T) {
"malicious_package = true",
"min_severity = \"High\"",
"exposures {\nmin_severity = \"High\" \nsecrets = true \n applications = true \n services = true \n iac = true\n}",
"package_name = \"nuget://RazorEngine\"",
}

for _, testAttribute := range testAttributes {
Expand Down Expand Up @@ -760,6 +762,96 @@ func TestAccSecurityPolicy_exposures(t *testing.T) {
})
}

func TestAccSecurityPolicy_Packages(t *testing.T) {
_, fqrn, resourceName := testutil.MkNames("policy-", "xray_security_policy")
testData := sdk.MergeMaps(testDataSecurity)

testData["resource_name"] = resourceName
testData["policy_name"] = fmt.Sprintf("terraform-security-policy-10-%d", testutil.RandomInt())
testData["rule_name"] = fmt.Sprintf("test-security-rule-10-%d", testutil.RandomInt())
testData["block_unscanned"] = "false"
testData["block_active"] = "false"
testData["package_name"] = "nuget:RazorEngine"
testData["package_type"] = "NuGet"
testData["package_version_1"] = "(1.2.3,3.10.2)"
testData["package_version_2"] = "[3.11,)"
testData["package_version_3"] = "[4.0.0]"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckPolicy),
ProtoV6ProviderFactories: acctest.ProtoV6MuxProviderFactories,
Steps: []resource.TestStep{
{
Config: util.ExecuteTemplate(fqrn, securityPolicyPackages, testData),
Check: verifySecurityPolicy(fqrn, testData, criteriaTypePackageName),
},
{
ResourceName: fqrn,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccSecurityPolicy_PackagesIncorrectVersionRangeFails(t *testing.T) {
_, fqrn, resourceName := testutil.MkNames("policy-", "xray_security_policy")
testData := sdk.MergeMaps(testDataSecurity)

for _, invalidVersionRange := range []string{"3.10.0", "[3,,4]", "(1,latest)", "[1.0.0.0]"} {
testData["resource_name"] = resourceName
testData["policy_name"] = fmt.Sprintf("terraform-security-policy-10-%d", testutil.RandomInt())
testData["rule_name"] = fmt.Sprintf("test-security-rule-10-%d", testutil.RandomInt())
testData["block_unscanned"] = "false"
testData["block_active"] = "false"
testData["package_name"] = "nuget://RazorEngine"
testData["package_type"] = "nuget"
testData["package_version_1"] = invalidVersionRange
testData["package_version_2"] = "(3.2.1,)"
testData["package_version_3"] = "[3.2.1,]"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckPolicy),
ProtoV6ProviderFactories: acctest.ProtoV6MuxProviderFactories,
Steps: []resource.TestStep{
{
Config: util.ExecuteTemplate(fqrn, securityPolicyPackages, testData),
ExpectError: regexp.MustCompile("invalid value for package_versions"),
},
},
})
}
}

func TestAccSecurityPolicy_createPackagesFail(t *testing.T) {
_, fqrn, resourceName := testutil.MkNames("policy-", "xray_security_policy")
testData := sdk.MergeMaps(testDataSecurity)

testData["resource_name"] = resourceName
testData["policy_name"] = fmt.Sprintf("terraform-security-policy-6-%d", testutil.RandomInt())
testData["rule_name"] = fmt.Sprintf("test-security-rule-6-%d", testutil.RandomInt())
testData["package_name"] = "nuget:RazorEngine"
testData["package_type"] = "NuGet"
testData["package_version_1"] = "(1.2.3,3.10.2)"
testData["package_version_2"] = "[3.11,)"
testData["package_version_3"] = "[4.0.0]"
testData["fix_version_dependant"] = "true"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckPolicy),
ProtoV6ProviderFactories: acctest.ProtoV6MuxProviderFactories,
Steps: []resource.TestStep{
{
Config: util.ExecuteTemplate(fqrn, securityPolicyPackagesFixVersionDep, testData),
ExpectError: regexp.MustCompile("fix_version_dependant must be set to false if package type policy is used"),
},
},
})
}

func testAccXraySecurityPolicy_badSecurityType(name, description, ruleName string, rangeTo int) string {
return fmt.Sprintf(`
resource "xray_security_policy" "test" {
Expand Down Expand Up @@ -859,6 +951,14 @@ func verifySecurityPolicy(fqrn string, testData map[string]string, criteriaType
resource.TestCheckResourceAttr(fqrn, "rule.0.criteria.0.exposures.0.iac", testData["exposures_iac"]),
)
}
if criteriaType == criteriaTypePackageName {
return resource.ComposeTestCheckFunc(
commonCheckList,
resource.TestCheckResourceAttr(fqrn, "rule.0.criteria.0.package_name", testData["package_name"]),
resource.TestCheckResourceAttr(fqrn, "rule.0.criteria.0.package_type", testData["package_type"]),
resource.TestCheckTypeSetElemAttr(fqrn, "rule.0.criteria.0.package_versions.*", testData["package_version_1"]),
)
}
return nil
}

Expand Down Expand Up @@ -1168,3 +1268,58 @@ const securityPolicyMaliciousPkgFixVersionDep = `resource "xray_security_policy"
}
}
}`

const securityPolicyPackagesFixVersionDep = `resource "xray_security_policy" "{{ .resource_name }}" {
name = "{{ .policy_name }}"
description = "{{ .policy_description }}"
type = "security"
rule {
name = "{{ .rule_name }}"
priority = 1
criteria {
package_name = "{{ .package_name }}"
package_type = "{{ .package_type }}"
package_versions = ["{{ .package_version_1 }}", "{{ .package_version_2 }}", "{{ .package_version_3 }}"]
fix_version_dependant = {{ .fix_version_dependant }}
}
actions {
block_release_bundle_distribution = {{ .block_release_bundle_distribution }}
fail_build = {{ .fail_build }}
notify_watch_recipients = {{ .notify_watch_recipients }}
notify_deployer = {{ .notify_deployer }}
create_ticket_enabled = {{ .create_ticket_enabled }}
build_failure_grace_period_in_days = {{ .grace_period_days }}
block_download {
unscanned = {{ .block_unscanned }}
active = {{ .block_active }}
}
}
}
}`

const securityPolicyPackages = `resource "xray_security_policy" "{{ .resource_name }}" {
name = "{{ .policy_name }}"
description = "{{ .policy_description }}"
type = "security"
rule {
name = "{{ .rule_name }}"
priority = 1
criteria {
package_name = "{{ .package_name }}"
package_type = "{{ .package_type }}"
package_versions = ["{{ .package_version_1 }}", "{{ .package_version_2 }}", "{{ .package_version_3 }}"]
}
actions {
block_release_bundle_distribution = {{ .block_release_bundle_distribution }}
fail_build = {{ .fail_build }}
notify_watch_recipients = {{ .notify_watch_recipients }}
notify_deployer = {{ .notify_deployer }}
create_ticket_enabled = {{ .create_ticket_enabled }}
build_failure_grace_period_in_days = {{ .grace_period_days }}
block_download {
unscanned = {{ .block_unscanned }}
active = {{ .block_active }}
}
}
}
}`

0 comments on commit 43bce3f

Please sign in to comment.