diff --git a/cmd/devguard-cli/commands/vulndb.go b/cmd/devguard-cli/commands/vulndb.go index 451d40578..6b5e020b6 100644 --- a/cmd/devguard-cli/commands/vulndb.go +++ b/cmd/devguard-cli/commands/vulndb.go @@ -83,6 +83,7 @@ func migrateDB() { fx.Invoke(func(DependencyVulnRouter router.DependencyVulnRouter) {}), fx.Invoke(func(FirstPartyVulnRouter router.FirstPartyVulnRouter) {}), fx.Invoke(func(LicenseRiskRouter router.LicenseRiskRouter) {}), + fx.Invoke(func(ComplianceRiskRouter router.ComplianceRiskRouter) {}), fx.Invoke(func(ShareRouter router.ShareRouter) {}), fx.Invoke(func(VulnDBRouter router.VulnDBRouter) {}), fx.Invoke(func(dependencyProxyRouter router.DependencyProxyRouter) {}), diff --git a/cmd/devguard-scanner/commands/attestations.go b/cmd/devguard-scanner/commands/attestations.go index 81704380f..af3e6b3fa 100644 --- a/cmd/devguard-scanner/commands/attestations.go +++ b/cmd/devguard-scanner/commands/attestations.go @@ -78,7 +78,7 @@ func attestationsCmd(cmd *cobra.Command, args []string) error { defer os.Remove(policyPath) } - sarifResult, evals, err := scanner.EvaluatePolicyAgainstAttestations(image, policyPath, attestations) + sarifResult, evals, err := scanner.EvaluatePolicyAgainstAttestations(fmt.Sprintf("oci://%s", image), policyPath, attestations) if err != nil { return err } diff --git a/cmd/devguard-scanner/scanner/eval_policy.go b/cmd/devguard-scanner/scanner/eval_policy.go index c1ee43527..5464e1e09 100644 --- a/cmd/devguard-scanner/scanner/eval_policy.go +++ b/cmd/devguard-scanner/scanner/eval_policy.go @@ -25,163 +25,40 @@ import ( "github.com/l3montree-dev/devguard/utils" ) -func EvaluatePolicyAgainstAttestations(image string, policyPath string, attestations []map[string]any) (*sarif.SarifSchema210Json, []compliance.PolicyEvaluation, error) { - policyContent, err := os.ReadFile(policyPath) +func EvaluatePolicyAgainstAttestations(srcPath string, policyPath string, attestations []map[string]any) (*sarif.SarifSchema210Json, []compliance.PolicyEvaluation, error) { + + content, err := os.ReadFile(policyPath) if err != nil { return nil, nil, fmt.Errorf("could not read policy file: %w", err) } - policy, err := compliance.NewPolicy(filepath.Base(policyPath), string(policyContent)) + policy, err := compliance.GetPolicyFromFile(filepath.Base(policyPath), string(content)) if err != nil { return nil, nil, fmt.Errorf("could not parse policy: %w", err) } - model := compliance.ConvertPolicyFsToModel(*policy) - - filtered := attestations - if policy.PredicateType != "" { - filtered = []map[string]any{} - for _, attestation := range attestations { - if predicateType, ok := attestation["predicateType"].(string); ok && predicateType == policy.PredicateType { - filtered = append(filtered, attestation) + evaluations := make([]compliance.PolicyEvaluation, 0) + + for _, attestation := range attestations { + var eval compliance.PolicyEvaluation + predicateType, _ := attestation["predicateType"].(string) + if predicateType != policy.PredicateType { + eval = compliance.Eval(policy, nil) + } else { + raw, err := json.Marshal(attestation) + if err != nil { + return nil, nil, fmt.Errorf("could not marshal attestation: %w", err) } + input, err := utils.ExtractAttestationPayload(string(raw)) + if err != nil { + return nil, nil, fmt.Errorf("could not extract attestation payload: %w", err) + } + eval = compliance.Eval(policy, input) } - if len(filtered) == 0 { - return nil, nil, fmt.Errorf("no attestations found for predicate type %s", policy.PredicateType) - } - } - - var evaluations []compliance.PolicyEvaluation - for _, attestation := range filtered { - raw, err := json.Marshal(attestation) - if err != nil { - return nil, nil, fmt.Errorf("could not marshal attestation: %w", err) - } - - input, err := utils.ExtractAttestationPayload(string(raw)) - if err != nil { - return nil, nil, fmt.Errorf("could not extract attestation payload: %w", err) - } - - evaluations = append(evaluations, compliance.Eval(model, input)) - } - - sarif := buildSarifFromPolicy(image, *policy, evaluations) - return &sarif, evaluations, nil -} - -func buildSarifFromPolicy(image string, policy compliance.PolicyFS, evaluations []compliance.PolicyEvaluation) sarif.SarifSchema210Json { - ruleID := policy.Filename - ruleName := policy.Title - - var helpURI *string - if len(policy.RelatedResources) > 0 { - helpURI = &policy.RelatedResources[0] - } - - rule := sarif.ReportingDescriptor{ - ID: ruleID, - Name: &ruleName, - ShortDescription: &sarif.MultiformatMessageString{ - Text: policy.Title, - }, - FullDescription: &sarif.MultiformatMessageString{ - Text: policy.Description, - }, - Help: &sarif.MultiformatMessageString{ - Text: policy.Description, - }, - HelpURI: helpURI, - Properties: &sarif.PropertyBag{ - Tags: policy.Tags, - AdditionalProperties: map[string]any{ - "priority": policy.Priority, - "relatedResources": policy.RelatedResources, - "complianceFrameworks": policy.ComplianceFrameworks, - "predicateType": policy.PredicateType, - }, - }, - } - - location := func(message string) sarif.Location { - uri := fmt.Sprintf("oci://%s", image) - - return sarif.Location{ - PhysicalLocation: sarif.PhysicalLocation{ - ArtifactLocation: sarif.ArtifactLocation{ - URI: &uri, - }, - }, - Message: sarif.Message{ - Text: message, - }, - } - } - - var results []sarif.Result - seen := make(map[string]bool) - addResult := func(r sarif.Result) { - key := string(r.Kind) + "|" + r.Message.Text - if !seen[key] { - seen[key] = true - results = append(results, r) - } - } - for _, evaluation := range evaluations { - if evaluation.Compliant != nil && *evaluation.Compliant { - addResult(sarif.Result{ - Kind: sarif.ResultKindPass, - RuleID: &ruleID, - Message: sarif.Message{ - Text: "Policy compliant", - }, - Locations: []sarif.Location{ - location("The attestation is compliant with the policy."), - }, - Properties: &sarif.PropertyBag{ - Tags: policy.Tags, - AdditionalProperties: map[string]any{ - "precision": "high", - }, - }, - }) - continue - } - for _, violation := range evaluation.Violations { - addResult(sarif.Result{ - Kind: sarif.ResultKindFail, - RuleID: &ruleID, - Message: sarif.Message{ - Text: violation, - }, - Locations: []sarif.Location{ - location(violation), - }, - Properties: &sarif.PropertyBag{ - Tags: policy.Tags, - AdditionalProperties: map[string]any{ - "precision": "high", - }, - }, - }) - } - } - driver := sarif.ToolComponent{ - Name: "devguard-attestations", - Rules: []sarif.ReportingDescriptor{rule}, + evaluations = append(evaluations, eval) } - return sarif.SarifSchema210Json{ - Version: sarif.SarifSchema210JsonVersionA210, - Schema: utils.Ptr("https://json.schemastore.org/sarif-2.1.0.json"), - Runs: []sarif.Run{ - { - Tool: sarif.Tool{ - Driver: driver, - }, - Results: results, - }, - }, - } + sarifResult := compliance.BuildSarifFromPoliciesEvaluations(srcPath, evaluations) + return &sarifResult, evaluations, nil } diff --git a/cmd/devguard-scanner/scanner/eval_policy_test.go b/cmd/devguard-scanner/scanner/eval_policy_test.go deleted file mode 100644 index cb69e7a71..000000000 --- a/cmd/devguard-scanner/scanner/eval_policy_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2025 l3montree GmbH -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package scanner - -import ( - "testing" - - "github.com/l3montree-dev/devguard/compliance" - "github.com/l3montree-dev/devguard/dtos/sarif" - "github.com/l3montree-dev/devguard/utils" -) - -func resultKey(r sarif.Result) string { - kind := string(r.Kind) - return kind + "|" + r.Message.Text -} - -func hasDuplicateResults(results []sarif.Result) bool { - seen := make(map[string]bool, len(results)) - for _, r := range results { - k := resultKey(r) - if seen[k] { - return true - } - seen[k] = true - } - return false -} - -func TestBuildSarifFromPolicy_NoDuplicateResults(t *testing.T) { - policy := compliance.PolicyFS{ - PolicyMetadata: compliance.PolicyMetadata{ - Filename: "test-policy.rego", - Title: "Test Policy", - Description: "A test policy", - Tags: []string{"test"}, - }, - } - - t.Run("duplicate violations across evaluations produce no duplicates", func(t *testing.T) { - compliant := false - evaluations := []compliance.PolicyEvaluation{ - {Compliant: &compliant, Violations: []string{"missing signature", "untrusted source"}}, - {Compliant: &compliant, Violations: []string{"missing signature", "untrusted source"}}, // same violations again - } - - result := buildSarifFromPolicy("registry.example.com/image:latest", policy, evaluations) - results := result.Runs[0].Results - - if hasDuplicateResults(results) { - t.Errorf("buildSarifFromPolicy returned duplicate result entries: %v", results) - } - }) - - t.Run("same violation repeated within one evaluation produces no duplicates", func(t *testing.T) { - compliant := false - evaluations := []compliance.PolicyEvaluation{ - {Compliant: &compliant, Violations: []string{"missing signature", "missing signature"}}, - } - - result := buildSarifFromPolicy("registry.example.com/image:latest", policy, evaluations) - results := result.Runs[0].Results - - if hasDuplicateResults(results) { - t.Errorf("buildSarifFromPolicy returned duplicate result entries: %v", results) - } - }) - - t.Run("multiple compliant evaluations produce no duplicate pass results", func(t *testing.T) { - compliant := true - evaluations := []compliance.PolicyEvaluation{ - {Compliant: &compliant, Violations: nil}, - {Compliant: &compliant, Violations: nil}, - {Compliant: &compliant, Violations: nil}, - } - - result := buildSarifFromPolicy("registry.example.com/image:latest", policy, evaluations) - results := result.Runs[0].Results - - if hasDuplicateResults(results) { - t.Errorf("buildSarifFromPolicy returned duplicate pass result entries: %v", results) - } - }) - - t.Run("mix of compliant and non-compliant evaluations with overlapping violations", func(t *testing.T) { - compliant := true - notCompliant := false - evaluations := []compliance.PolicyEvaluation{ - {Compliant: &compliant, Violations: nil}, - {Compliant: ¬Compliant, Violations: []string{"missing signature"}}, - {Compliant: ¬Compliant, Violations: []string{"missing signature"}}, - {Compliant: &compliant, Violations: nil}, - } - - result := buildSarifFromPolicy("registry.example.com/image:latest", policy, evaluations) - results := result.Runs[0].Results - - if hasDuplicateResults(results) { - t.Errorf("buildSarifFromPolicy returned duplicate result entries: %v", results) - } - }) - - t.Run("single evaluation with no violations produces no results", func(t *testing.T) { - evaluations := []compliance.PolicyEvaluation{ - {Compliant: utils.Ptr(true), Violations: nil}, - } - - result := buildSarifFromPolicy("registry.example.com/image:latest", policy, evaluations) - results := result.Runs[0].Results - - if hasDuplicateResults(results) { - t.Errorf("buildSarifFromPolicy returned duplicate result entries: %v", results) - } - }) -} diff --git a/cmd/devguard/main.go b/cmd/devguard/main.go index eca079150..1b1029304 100644 --- a/cmd/devguard/main.go +++ b/cmd/devguard/main.go @@ -136,6 +136,7 @@ func main() { fx.Invoke(func(DependencyVulnRouter router.DependencyVulnRouter) {}), fx.Invoke(func(FirstPartyVulnRouter router.FirstPartyVulnRouter) {}), fx.Invoke(func(LicenseRiskRouter router.LicenseRiskRouter) {}), + fx.Invoke(func(ComplianceRiskRouter router.ComplianceRiskRouter) {}), fx.Invoke(func(ShareRouter router.ShareRouter) {}), fx.Invoke(func(VulnDBRouter router.VulnDBRouter) {}), fx.Invoke(func(dependencyProxyRouter router.DependencyProxyRouter) {}), diff --git a/compliance/attestation-compliance-policies/policies/author_committer_email_is_from_org.rego b/compliance/attestation-compliance-policies/policies/author_committer_email_is_from_org.rego index a1fc322cb..151635102 100644 --- a/compliance/attestation-compliance-policies/policies/author_committer_email_is_from_org.rego +++ b/compliance/attestation-compliance-policies/policies/author_committer_email_is_from_org.rego @@ -7,7 +7,7 @@ # relatedResources: [] # tags: # - Legal -# complianceFrameworks: [] +# policyFrameworks: [] package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/branch_protection_enabled.rego b/compliance/attestation-compliance-policies/policies/branch_protection_enabled.rego index af9a60628..12ad9bc9e 100644 --- a/compliance/attestation-compliance-policies/policies/branch_protection_enabled.rego +++ b/compliance/attestation-compliance-policies/policies/branch_protection_enabled.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.8.4 Access to source code -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.8.4 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/build_from_signed_source.rego b/compliance/attestation-compliance-policies/policies/build_from_signed_source.rego index 7a452e727..29e4ff154 100644 --- a/compliance/attestation-compliance-policies/policies/build_from_signed_source.rego +++ b/compliance/attestation-compliance-policies/policies/build_from_signed_source.rego @@ -8,8 +8,10 @@ # tags: # - ISO 27001 # - A.8 Access Control -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.8 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/ci_image_has_digest_set.rego b/compliance/attestation-compliance-policies/policies/ci_image_has_digest_set.rego index 8ef158627..6d053bf30 100644 --- a/compliance/attestation-compliance-policies/policies/ci_image_has_digest_set.rego +++ b/compliance/attestation-compliance-policies/policies/ci_image_has_digest_set.rego @@ -8,7 +8,7 @@ # tags: # - GitLab CI # - Legal -# complianceFrameworks: [] +# policyFrameworks: [] package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/cia_requirements_set_for_asset.rego b/compliance/attestation-compliance-policies/policies/cia_requirements_set_for_asset.rego index e8701f162..f660ecc65 100644 --- a/compliance/attestation-compliance-policies/policies/cia_requirements_set_for_asset.rego +++ b/compliance/attestation-compliance-policies/policies/cia_requirements_set_for_asset.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.5.12 Classification of Information -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.12 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/code_review_for_changes_on_default_branch.rego b/compliance/attestation-compliance-policies/policies/code_review_for_changes_on_default_branch.rego index 409b034a7..1ba15c1bc 100644 --- a/compliance/attestation-compliance-policies/policies/code_review_for_changes_on_default_branch.rego +++ b/compliance/attestation-compliance-policies/policies/code_review_for_changes_on_default_branch.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.8.4 Access to source code -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.8.4 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/container_scanning_executed.rego b/compliance/attestation-compliance-policies/policies/container_scanning_executed.rego index 29cad1ee0..aa2e644e2 100644 --- a/compliance/attestation-compliance-policies/policies/container_scanning_executed.rego +++ b/compliance/attestation-compliance-policies/policies/container_scanning_executed.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.5.7 Threat intelligence -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.7 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/current_sbom_is_present.rego b/compliance/attestation-compliance-policies/policies/current_sbom_is_present.rego index a5a5d8aff..9dc027d23 100644 --- a/compliance/attestation-compliance-policies/policies/current_sbom_is_present.rego +++ b/compliance/attestation-compliance-policies/policies/current_sbom_is_present.rego @@ -10,8 +10,12 @@ # - A.5.7 Threat intelligence # - A.5.9 Inventory of information and other associated assets # - A.8.8 Management of technical vulnerabilities -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.7 +# - A.5.9 +# - A.8.8 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/notification_channel_for_new_vulnerabilities.rego b/compliance/attestation-compliance-policies/policies/notification_channel_for_new_vulnerabilities.rego index 53852a086..935a6310a 100644 --- a/compliance/attestation-compliance-policies/policies/notification_channel_for_new_vulnerabilities.rego +++ b/compliance/attestation-compliance-policies/policies/notification_channel_for_new_vulnerabilities.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.8.8 Management of technical vulnerabilities -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.8.8 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/only_osi_approved_licenses.rego b/compliance/attestation-compliance-policies/policies/only_osi_approved_licenses.rego index 3a4beb60c..9cc3884ce 100644 --- a/compliance/attestation-compliance-policies/policies/only_osi_approved_licenses.rego +++ b/compliance/attestation-compliance-policies/policies/only_osi_approved_licenses.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.5.32 Intellectual property rights -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.32 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/secret_scanning_executed.rego b/compliance/attestation-compliance-policies/policies/secret_scanning_executed.rego index 8f1b51f08..1743cbe2e 100644 --- a/compliance/attestation-compliance-policies/policies/secret_scanning_executed.rego +++ b/compliance/attestation-compliance-policies/policies/secret_scanning_executed.rego @@ -9,8 +9,11 @@ # tags: # - ISO 27001 # - A.5.7 Threat intelligence -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.7 + package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/security_policy_present_in_repo.rego b/compliance/attestation-compliance-policies/policies/security_policy_present_in_repo.rego index 9febffac9..caa2965d2 100644 --- a/compliance/attestation-compliance-policies/policies/security_policy_present_in_repo.rego +++ b/compliance/attestation-compliance-policies/policies/security_policy_present_in_repo.rego @@ -8,9 +8,9 @@ # - https://github.com/ossf/scorecard/blob/main/docs/checks.md#security-policy # tags: # - Best Practices -# complianceFrameworks: -# - Best Practices -# - OpenSSF Scorecard +# policyFrameworks: +# - framework: Best Practices +# - framework: OpenSSF Scorecard package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/signed_off_commit.rego b/compliance/attestation-compliance-policies/policies/signed_off_commit.rego index 18ca7587a..137562c94 100644 --- a/compliance/attestation-compliance-policies/policies/signed_off_commit.rego +++ b/compliance/attestation-compliance-policies/policies/signed_off_commit.rego @@ -7,7 +7,7 @@ # relatedResources: [] # tags: # - Legal -# complianceFrameworks: [] +# policyFrameworks: [] package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/software_composition_analysis_executed.rego b/compliance/attestation-compliance-policies/policies/software_composition_analysis_executed.rego index f1e917653..edbe1fed3 100644 --- a/compliance/attestation-compliance-policies/policies/software_composition_analysis_executed.rego +++ b/compliance/attestation-compliance-policies/policies/software_composition_analysis_executed.rego @@ -9,8 +9,10 @@ # tags: # - ISO 27001 # - A.5.7 Threat intelligence -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.7 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/uses_sbom.rego b/compliance/attestation-compliance-policies/policies/uses_sbom.rego index 24257b161..b9c697213 100644 --- a/compliance/attestation-compliance-policies/policies/uses_sbom.rego +++ b/compliance/attestation-compliance-policies/policies/uses_sbom.rego @@ -10,8 +10,12 @@ # - A.5.7 Threat intelligence # - A.5.9 Inventory of information and other associated assets # - A.8.8 Management of technical vulnerabilities -# complianceFrameworks: -# - ISO 27001 +# policyFrameworks: +# - framework: ISO 27001 +# controls: +# - A.5.7 +# - A.5.9 +# - A.8.8 package compliance import rego.v1 diff --git a/compliance/attestation-compliance-policies/policies/vulnerability_fix_time_sla.rego b/compliance/attestation-compliance-policies/policies/vulnerability_fix_time_sla.rego new file mode 100644 index 000000000..6523b3d47 --- /dev/null +++ b/compliance/attestation-compliance-policies/policies/vulnerability_fix_time_sla.rego @@ -0,0 +1,78 @@ +# METADATA +# title: Vulnerability fix time SLA +# custom: +# description: Ensures that the average time to remediate vulnerabilities meets defined SLA thresholds — critical < 1 day, high < 3 days, medium < 14 days, low < 30 days. +# priority: 1 +# predicateType: https://devguard.org/attestation/asset-metrics/v1 +# relatedResources: [] +# tags: +# - Security +# policyFrameworks: +# - framework: L3montree Compliance +# controls: +# - Vulnerability Management + +package compliance + +import rego.v1 + +default compliant := false + +# SLA thresholds in hours +critical_max_hours := 24 +high_max_hours := 72 +medium_max_hours := 336 +low_max_hours := 720 + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskCriticalAvgHours == 0 + msg := "Mean time to remediate for critical vulnerabilities is 0 — no data available" +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskHighAvgHours == 0 + msg := "Mean time to remediate for high vulnerabilities is 0 — no data available" +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskMediumAvgHours == 0 + msg := "Mean time to remediate for medium vulnerabilities is 0 — no data available" +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskLowAvgHours == 0 + msg := "Mean time to remediate for low vulnerabilities is 0 — no data available" +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskCriticalAvgHours >= critical_max_hours + msg := sprintf("Mean time to remediate critical vulnerabilities (%d h) exceeds SLA of %d h", [input.meanTimeToRemediate.riskCriticalAvgHours, critical_max_hours]) +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskHighAvgHours >= high_max_hours + msg := sprintf("Mean time to remediate high vulnerabilities (%d h) exceeds SLA of %d h", [input.meanTimeToRemediate.riskHighAvgHours, high_max_hours]) +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskMediumAvgHours >= medium_max_hours + msg := sprintf("Mean time to remediate medium vulnerabilities (%d h) exceeds SLA of %d h", [input.meanTimeToRemediate.riskMediumAvgHours, medium_max_hours]) +} + +violations contains msg if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + input.meanTimeToRemediate.riskLowAvgHours >= low_max_hours + msg := sprintf("Mean time to remediate low vulnerabilities (%d h) exceeds SLA of %d h", [input.meanTimeToRemediate.riskLowAvgHours, low_max_hours]) +} + +compliant if { + input.type == "https://devguard.org/attestation/asset-metrics/v1" + count(violations) == 0 +} diff --git a/compliance/rego.go b/compliance/rego.go index cb54544c1..9cbd81a9b 100644 --- a/compliance/rego.go +++ b/compliance/rego.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos/sarif" "github.com/l3montree-dev/devguard/utils" "github.com/open-policy-agent/opa/v1/rego" "gopkg.in/yaml.v2" @@ -21,36 +22,45 @@ type yamlPolicy struct { } type customYaml struct { - Description string `yaml:"description"` - Priority int `yaml:"priority"` - Tags []string + Description string `yaml:"description"` + Priority int `yaml:"priority"` + Tags []string `yaml:"tags"` // used for mapping from policies to attestations - PredicateType string `yaml:"predicateType"` - RelatedResources []string `yaml:"relatedResources"` - ComplianceFrameworks []string `yaml:"complianceFrameworks"` + PredicateType string `yaml:"predicateType"` + RelatedResources []string `yaml:"relatedResources"` + PolicyFrameworks []models.PolicyFrameworks `yaml:"policyFrameworks"` } type PolicyMetadata struct { - Title string `yaml:"title" json:"title"` - Description string `yaml:"description" json:"description"` - Priority int `yaml:"priority" json:"priority"` - Tags []string `yaml:"tags" json:"tags"` - RelatedResources []string `yaml:"relatedResources" json:"relatedResources"` - ComplianceFrameworks []string `yaml:"complianceFrameworks" json:"complianceFrameworks"` - Filename string `json:"filename"` - Content string `json:"content"` - PredicateType string `yaml:"predicateType" json:"predicateType"` + Title string `yaml:"title" json:"title"` + Description string `yaml:"description" json:"description"` + Priority int `yaml:"priority" json:"priority"` + Tags []string `yaml:"tags" json:"tags"` + RelatedResources []string `yaml:"relatedResources" json:"relatedResources"` + PolicyFrameworks []models.PolicyFrameworks `yaml:"policyFrameworks" json:"policyFrameworks"` + ComplianceFrameworks []string `yaml:"complianceFrameworks" json:"complianceFrameworks"` + Filename string `json:"filename"` + Content string `json:"content"` + PredicateType string `yaml:"predicateType" json:"predicateType"` } -type PolicyFS struct { +type Policy struct { PolicyMetadata Content string } type PolicyEvaluation struct { - models.Policy - Compliant *bool `json:"compliant"` - Violations []string `json:"violations"` - RawEvaluationResult map[string]any `json:"rawEvaluationResult"` + PolicyID string + PolicyTitle string + PolicyDescription string + PolicyRelatedResources []string + PolicyTags []string + PolicyPriority int + PolicyFrameworks []models.PolicyFrameworks + Compliant *bool + Violations []string + RawEvaluationResult map[string]any + EvidenceType string + EvidenceContent *string } var packageRegexp = regexp.MustCompile(`(?m)^package compliance`) @@ -94,78 +104,54 @@ func parseMetadata(fileName string, content string) (PolicyMetadata, error) { } return PolicyMetadata{ - Title: metadata.Title, - Description: metadata.Custom.Description, - Priority: metadata.Custom.Priority, - Tags: metadata.Custom.Tags, - RelatedResources: metadata.Custom.RelatedResources, - ComplianceFrameworks: metadata.Custom.ComplianceFrameworks, - Filename: fileName, - PredicateType: metadata.Custom.PredicateType, - Content: content, + Title: metadata.Title, + Description: metadata.Custom.Description, + Priority: metadata.Custom.Priority, + Tags: metadata.Custom.Tags, + RelatedResources: metadata.Custom.RelatedResources, + PolicyFrameworks: metadata.Custom.PolicyFrameworks, + Filename: fileName, + PredicateType: metadata.Custom.PredicateType, }, nil } -func ConvertPolicyFsToModel(policy PolicyFS) models.Policy { - return models.Policy{ - Rego: policy.Content, - Description: policy.Description, - Title: policy.Title, - PredicateType: policy.PredicateType, - OpaqueID: &policy.Filename, - OrganizationID: nil, +func Eval(policy Policy, input any) PolicyEvaluation { + result := PolicyEvaluation{ + PolicyID: policy.Filename, + PolicyTitle: policy.Title, + PolicyDescription: policy.Description, + PolicyRelatedResources: policy.RelatedResources, + PolicyTags: policy.Tags, + PolicyPriority: policy.Priority, + PolicyFrameworks: policy.PolicyFrameworks, + EvidenceType: "json", + EvidenceContent: &policy.Content, } -} - -func NewPolicy(filename string, content string) (*PolicyFS, error) { - metadata, err := parseMetadata(filename, content) - if err != nil { - return nil, err - } - - return &PolicyFS{ - PolicyMetadata: metadata, - Content: content, - }, nil -} - -func Eval(p models.Policy, input any) PolicyEvaluation { - if input == nil { - return PolicyEvaluation{ - Policy: p, - Compliant: nil, - } + return result } r := rego.New( rego.Query("data.compliance"), - rego.Module("", p.Rego), + rego.Module(policy.Filename, policy.Content), ) ctx := context.TODO() query, err := r.PrepareForEval(ctx) if err != nil { - return PolicyEvaluation{ - Policy: p, - Compliant: nil, - } + return result } - rs, err := query.Eval(context.TODO(), rego.EvalInput(input)) + rs, err := query.Eval(ctx, rego.EvalInput(input)) if err != nil { - return PolicyEvaluation{ - Policy: p, - Compliant: nil, - } + return result } - var violations = []string{} + var violations []string var rawEvalResult map[string]any var compliant *bool if len(rs) > 0 { value := rs[0].Expressions[0].Value - // cast value to map if v, ok := value.(map[string]any); ok { rawEvalResult = v if v["compliant"] != nil { @@ -181,12 +167,11 @@ func Eval(p models.Policy, input any) PolicyEvaluation { } } - return PolicyEvaluation{ - Policy: p, - Compliant: compliant, - Violations: violations, - RawEvaluationResult: rawEvalResult, - } + result.Compliant = compliant + result.Violations = violations + result.RawEvaluationResult = rawEvalResult + + return result } // embed the policies in the binary @@ -194,26 +179,31 @@ func Eval(p models.Policy, input any) PolicyEvaluation { //go:embed attestation-compliance-policies/policies/*.rego var policiesFs embed.FS -func GetCommunityManagedPoliciesFromFS() []PolicyFS { +func GetPoliciesFromFS(policyDir string) ([]Policy, error) { // fetch all policies - policyFiles, err := policiesFs.ReadDir("attestation-compliance-policies/policies") + policyFiles, err := policiesFs.ReadDir(policyDir) if err != nil { - return nil + return nil, err } - var policies []PolicyFS + var policies []Policy for _, file := range policyFiles { - content, err := policiesFs.ReadFile(filepath.Join("attestation-compliance-policies/policies", file.Name())) + content, err := policiesFs.ReadFile(filepath.Join(policyDir, file.Name())) if err != nil { continue } - policy, err := NewPolicy(file.Name(), string(content)) + metadata, err := parseMetadata(file.Name(), string(content)) if err != nil { - continue + return nil, err + } + + policy := Policy{ + PolicyMetadata: metadata, + Content: string(content), } - policies = append(policies, *policy) + policies = append(policies, policy) } // sort the policies by priority - use a stable sort @@ -221,5 +211,126 @@ func GetCommunityManagedPoliciesFromFS() []PolicyFS { return policies[i].Priority < policies[j].Priority }) - return policies + return policies, nil +} + +func GetPolicyFromFile(fileName, content string) (Policy, error) { + metadata, err := parseMetadata(fileName, content) + if err != nil { + return Policy{}, err + } + return Policy{PolicyMetadata: metadata, Content: content}, nil +} + +func BuildSarifFromPoliciesEvaluations(srcPath string, evaluations []PolicyEvaluation) sarif.SarifSchema210Json { + rules := make([]sarif.ReportingDescriptor, 0) + results := make([]sarif.Result, 0, len(evaluations)) + seenRules := make(map[string]bool) + addRule := func(r sarif.ReportingDescriptor) { + if !seenRules[r.ID] { + seenRules[r.ID] = true + rules = append(rules, r) + } + } + + seenResults := make(map[string]bool) + addResult := func(r sarif.Result) { + if !seenResults[*r.RuleID] { + seenResults[*r.RuleID] = true + results = append(results, r) + } + } + + for _, evaluation := range evaluations { + ruleID := evaluation.PolicyID + ruleName := evaluation.PolicyTitle + + var helpURI *string + if len(evaluation.PolicyRelatedResources) > 0 { + helpURI = &evaluation.PolicyRelatedResources[0] + } + + rule := sarif.ReportingDescriptor{ + ID: ruleID, + Name: &ruleName, + ShortDescription: &sarif.MultiformatMessageString{ + Text: evaluation.PolicyTitle, + }, + FullDescription: &sarif.MultiformatMessageString{ + Text: evaluation.PolicyDescription, + }, + Help: &sarif.MultiformatMessageString{ + Text: evaluation.PolicyDescription, + }, + HelpURI: helpURI, + Properties: &sarif.PropertyBag{ + Tags: evaluation.PolicyTags, + AdditionalProperties: map[string]any{ + "priority": evaluation.PolicyPriority, + "relatedResources": evaluation.PolicyRelatedResources, + "policyFrameworks": evaluation.PolicyFrameworks, + }, + }, + } + + addRule(rule) + + artifactLocation := sarif.ArtifactLocation{URI: &srcPath} + additionalProps := map[string]any{ + "precision": "high", + "evidenceType": evaluation.EvidenceType, + "violations": evaluation.Violations, + } + if evaluation.EvidenceContent != nil { + additionalProps["evidenceContent"] = *evaluation.EvidenceContent + } + + props := &sarif.PropertyBag{ + Tags: evaluation.PolicyTags, + AdditionalProperties: additionalProps, + } + var kind sarif.ResultKind + var message sarif.Message + var result sarif.Result + if evaluation.Compliant != nil && *evaluation.Compliant { + kind = sarif.ResultKindPass + message = sarif.Message{Text: "Policy compliant"} + } else if evaluation.Compliant != nil && !*evaluation.Compliant { + kind = sarif.ResultKindFail + message = sarif.Message{Text: "Policy not compliant"} + } else { + kind = sarif.ResultKindOpen + message = sarif.Message{Text: "No attestation found for policy — compliance could not be determined."} + } + + result = sarif.Result{ + Kind: kind, + RuleID: &ruleID, + Message: message, + Locations: []sarif.Location{ + {PhysicalLocation: sarif.PhysicalLocation{ArtifactLocation: artifactLocation}}, + }, + Properties: props, + } + + addResult(result) + } + + driver := sarif.ToolComponent{ + Name: "devguard-attestations", + Rules: rules, + } + + return sarif.SarifSchema210Json{ + Version: sarif.SarifSchema210JsonVersionA210, + Schema: utils.Ptr("https://json.schemastore.org/sarif-2.1.0.json"), + Runs: []sarif.Run{ + { + Tool: sarif.Tool{ + Driver: driver, + }, + Results: results, + }, + }, + } } diff --git a/compliance/rego_test.go b/compliance/rego_test.go index f4e9b275d..a8ddd4258 100644 --- a/compliance/rego_test.go +++ b/compliance/rego_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/l3montree-dev/devguard/dtos/sarif" "github.com/l3montree-dev/devguard/utils" "github.com/stretchr/testify/assert" ) @@ -28,41 +29,19 @@ func TestEval(t *testing.T) { t.Fatal(err) } - // create a new policy - policy, err := NewPolicy("", string(policyContent)) + metadata, err := parseMetadata("", string(policyContent)) if err != nil { t.Fatal(err) } - - model := ConvertPolicyFsToModel(*policy) + policy := Policy{PolicyMetadata: metadata, Content: string(policyContent)} // evaluate the policy - res := Eval(model, input) + res := Eval(policy, input) if res.Compliant == nil || *res.Compliant != true { t.Fatal(res) } } -func TestNewPolicy(t *testing.T) { - t.Run("should parse the metadata", func(t *testing.T) { - // read the example-policy.rego file - policyContent, err := os.ReadFile("testfiles/example-policy.rego") - if err != nil { - t.Fatal(err) - } - - // create a new policy - policy, err := NewPolicy("", string(policyContent)) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, "Build from signed source", policy.Title) - assert.Equal(t, "This policy checks if the build was done from a signed commit.", policy.Description) - assert.Equal(t, []string{"iso27001", "A.8 Access Control"}, policy.Tags) - }) -} - func TestOnlyOsiApprovedLicensesPolicy(t *testing.T) { sbomContent, err := os.ReadFile("./testfiles/sbom.json") if err != nil { @@ -74,10 +53,11 @@ func TestOnlyOsiApprovedLicensesPolicy(t *testing.T) { t.Fatal(err) } - policy, err := NewPolicy("", string(policyContent)) + metadata, err := parseMetadata("", string(policyContent)) if err != nil { t.Fatal(err) } + policy := Policy{PolicyMetadata: metadata, Content: string(policyContent)} // parse the sbom var input any @@ -86,11 +66,9 @@ func TestOnlyOsiApprovedLicensesPolicy(t *testing.T) { t.Fatal(err) } - model := ConvertPolicyFsToModel(*policy) - result := Eval(model, input) + result := Eval(policy, input) expectedResult := &PolicyEvaluation{ - Policy: model, Compliant: utils.Ptr(false), Violations: []string{ "Component \"github.com/cloudflare/circl\" uses non-OSI approved license \"non-standard\"", @@ -129,3 +107,101 @@ func TestOnlyOsiApprovedLicensesPolicy(t *testing.T) { assert.Subset(t, expectedResult.Violations, result.Violations) assert.Subset(t, result.Violations, expectedResult.Violations) } + +func resultKey(r sarif.Result) string { + return string(r.Kind) + "|" + r.Message.Text +} + +func hasDuplicateResults(results []sarif.Result) bool { + seen := make(map[string]bool, len(results)) + for _, r := range results { + k := resultKey(r) + if seen[k] { + return true + } + seen[k] = true + } + return false +} + +func makeEvaluations(policy Policy, evals []PolicyEvaluation) []PolicyEvaluation { + for i := range evals { + evals[i].PolicyID = policy.Filename + evals[i].PolicyTitle = policy.Title + evals[i].PolicyDescription = policy.Description + evals[i].PolicyTags = policy.Tags + } + return evals +} + +func TestBuildSarifFromPoliciesEvaluations_NoDuplicateResults(t *testing.T) { + policy := Policy{ + PolicyMetadata: PolicyMetadata{ + Filename: "test-policy.rego", + Title: "Test Policy", + Description: "A test policy", + Tags: []string{"test"}, + }, + } + + t.Run("duplicate violations across evaluations produce no duplicates", func(t *testing.T) { + compliant := false + evaluations := makeEvaluations(policy, []PolicyEvaluation{ + {Compliant: &compliant, Violations: []string{"missing signature", "untrusted source"}}, + {Compliant: &compliant, Violations: []string{"missing signature", "untrusted source"}}, + }) + results := BuildSarifFromPoliciesEvaluations("registry.example.com/image:latest", evaluations).Runs[0].Results + if hasDuplicateResults(results) { + t.Errorf("BuildSarifFromPoliciesEvaluations returned duplicate result entries: %v", results) + } + }) + + t.Run("same violation repeated within one evaluation produces no duplicates", func(t *testing.T) { + compliant := false + evaluations := makeEvaluations(policy, []PolicyEvaluation{ + {Compliant: &compliant, Violations: []string{"missing signature", "missing signature"}}, + }) + results := BuildSarifFromPoliciesEvaluations("registry.example.com/image:latest", evaluations).Runs[0].Results + if hasDuplicateResults(results) { + t.Errorf("BuildSarifFromPoliciesEvaluations returned duplicate result entries: %v", results) + } + }) + + t.Run("multiple compliant evaluations produce no duplicate pass results", func(t *testing.T) { + compliant := true + evaluations := makeEvaluations(policy, []PolicyEvaluation{ + {Compliant: &compliant}, + {Compliant: &compliant}, + {Compliant: &compliant}, + }) + results := BuildSarifFromPoliciesEvaluations("registry.example.com/image:latest", evaluations).Runs[0].Results + if hasDuplicateResults(results) { + t.Errorf("BuildSarifFromPoliciesEvaluations returned duplicate pass result entries: %v", results) + } + }) + + t.Run("mix of compliant and non-compliant evaluations with overlapping violations", func(t *testing.T) { + compliant := true + notCompliant := false + evaluations := makeEvaluations(policy, []PolicyEvaluation{ + {Compliant: &compliant}, + {Compliant: ¬Compliant, Violations: []string{"missing signature"}}, + {Compliant: ¬Compliant, Violations: []string{"missing signature"}}, + {Compliant: &compliant}, + }) + results := BuildSarifFromPoliciesEvaluations("registry.example.com/image:latest", evaluations).Runs[0].Results + if hasDuplicateResults(results) { + t.Errorf("BuildSarifFromPoliciesEvaluations returned duplicate result entries: %v", results) + } + }) + + t.Run("single evaluation with no violations produces no results", func(t *testing.T) { + evaluations := makeEvaluations(policy, []PolicyEvaluation{ + {Compliant: utils.Ptr(true)}, + }) + results := BuildSarifFromPoliciesEvaluations("registry.example.com/image:latest", evaluations).Runs[0].Results + if hasDuplicateResults(results) { + t.Errorf("BuildSarifFromPoliciesEvaluations returned duplicate result entries: %v", results) + } + }) +} diff --git a/controllers/attestation_controller.go b/controllers/attestation_controller.go index da07feba6..6c83e6bc8 100644 --- a/controllers/attestation_controller.go +++ b/controllers/attestation_controller.go @@ -12,14 +12,14 @@ import ( ) type AttestationController struct { - attestationRepository shared.AttestationRepository - artifactRepository shared.ArtifactRepository + attestationService shared.AttestationService + artifactRepository shared.ArtifactRepository } -func NewAttestationController(repository shared.AttestationRepository, artifactRepository shared.ArtifactRepository) *AttestationController { +func NewAttestationController(attestationService shared.AttestationService, artifactRepository shared.ArtifactRepository) *AttestationController { return &AttestationController{ - attestationRepository: repository, - artifactRepository: artifactRepository, + attestationService: attestationService, + artifactRepository: artifactRepository, } } @@ -38,7 +38,22 @@ func (a *AttestationController) List(ctx shared.Context) error { asset := shared.GetAsset(ctx) assetVersion := shared.GetAssetVersion(ctx) - attestationList, err := a.attestationRepository.GetByAssetVersionAndAssetID(ctx.Request().Context(), nil, asset.GetID(), assetVersion.Name) + attestationList, err := a.attestationService.GetByAssetVersionAndAssetID(ctx.Request().Context(), nil, asset.GetID(), assetVersion.Name) + if err != nil { + return err + } + + return ctx.JSON(200, attestationList) +} + +func (a *AttestationController) ListByArtifact(ctx shared.Context) error { + + asset := shared.GetAsset(ctx) + assetVersion := shared.GetAssetVersion(ctx) + + artifact := shared.GetArtifact(ctx) + + attestationList, err := a.attestationService.GetByArtifactAndAssetVersionAndAssetID(ctx.Request().Context(), nil, artifact.ArtifactName, assetVersion.Name, asset.GetID()) if err != nil { return err } @@ -100,7 +115,7 @@ func (a *AttestationController) Create(ctx shared.Context) error { return echo.NewHTTPError(400, fmt.Sprintf("could not validate request: %s", err.Error())) } attestation.Content = jsonContent - err = a.attestationRepository.Create(ctx.Request().Context(), nil, &attestation) + err = a.attestationService.Create(ctx.Request().Context(), nil, &attestation) if err != nil { return err } diff --git a/controllers/attestation_test.go b/controllers/attestation_test.go index 3ffeae3ee..625eba5dc 100644 --- a/controllers/attestation_test.go +++ b/controllers/attestation_test.go @@ -26,12 +26,12 @@ func TestList(t *testing.T) { shared.SetAsset(ctx, asset) shared.SetAssetVersion(ctx, assetVersion) - attestationRepository := mocks.NewAttestationRepository(t) - attestationRepository.On("GetByAssetVersionAndAssetID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]models.Attestation{ + attestationService := mocks.NewAttestationService(t) + attestationService.On("GetByAssetVersionAndAssetID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]models.Attestation{ {PredicateType: "not ocol name"}, }, nil) - attestationController := NewAttestationController(attestationRepository, mocks.NewArtifactRepository(t)) + attestationController := NewAttestationController(attestationService, mocks.NewArtifactRepository(t)) result := attestationController.List(ctx) if result != nil { t.Fail() @@ -49,9 +49,9 @@ func TestList(t *testing.T) { shared.SetAsset(ctx, asset) shared.SetAssetVersion(ctx, assetVersion) - attestationRepository := mocks.NewAttestationRepository(t) - attestationRepository.On("GetByAssetVersionAndAssetID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]models.Attestation{}, fmt.Errorf("Something went wrong")) - attestationController := NewAttestationController(attestationRepository, mocks.NewArtifactRepository(t)) + attestationService := mocks.NewAttestationService(t) + attestationService.On("GetByAssetVersionAndAssetID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]models.Attestation{}, fmt.Errorf("Something went wrong")) + attestationController := NewAttestationController(attestationService, mocks.NewArtifactRepository(t)) result := attestationController.List(ctx) diff --git a/controllers/compliance_controller.go b/controllers/compliance_controller.go deleted file mode 100644 index a4abaabaa..000000000 --- a/controllers/compliance_controller.go +++ /dev/null @@ -1,132 +0,0 @@ -package controllers - -import ( - "context" - _ "embed" - - "github.com/google/uuid" - "github.com/l3montree-dev/devguard/compliance" - "github.com/l3montree-dev/devguard/database/models" - "github.com/l3montree-dev/devguard/shared" -) - -type ComplianceController struct { - assetVersionRepository shared.AssetVersionRepository - attestationRepository shared.AttestationRepository - policyRepository shared.PolicyRepository -} - -func NewComplianceController(assetVersionRepository shared.AssetVersionRepository, attestationRepository shared.AttestationRepository, policyRepository shared.PolicyRepository) *ComplianceController { - return &ComplianceController{ - assetVersionRepository: assetVersionRepository, - policyRepository: policyRepository, - attestationRepository: attestationRepository, - } -} - -func (c *ComplianceController) getAssetVersionCompliance(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion) ([]compliance.PolicyEvaluation, error) { - // get the attestation - attestations, err := c.attestationRepository.GetByAssetVersionAndAssetID(ctx, nil, assetVersion.AssetID, assetVersion.Name) - if err != nil { - return nil, err - } - - policies, err := c.policyRepository.FindByProjectID(ctx, nil, projectID) - if err != nil { - return nil, err - } - - results := make([]compliance.PolicyEvaluation, 0, len(policies)) -foundMatch: - for _, policy := range policies { - // check if we find an attestation that matches - for _, attestation := range attestations { - if attestation.PredicateType != policy.PredicateType { - continue - } - res := compliance.Eval(policy, attestation.Content) - // this matches - lets add it - results = append(results, res) - continue foundMatch - } - // we did not find any attestation that matches - lets add the policy with a nil result - results = append(results, compliance.Eval(policy, nil)) - } - - // compliance.Evaluate the policy - return results, nil -} - -func (c *ComplianceController) Details(ctx shared.Context) error { - assetVersion := shared.GetAssetVersion(ctx) - - p := ctx.Param("policy") - // parse the uuid - policyID, err := uuid.Parse(p) - if err != nil { - return ctx.JSON(400, nil) - } - // get all policies - policy, err := c.policyRepository.Read(ctx.Request().Context(), nil, policyID) - if err != nil { - return ctx.JSON(404, nil) - } - - attestations, err := c.attestationRepository.GetByAssetVersionAndAssetID(ctx.Request().Context(), nil, assetVersion.AssetID, assetVersion.Name) - - if err != nil { - return ctx.JSON(500, nil) - } - - // look for the right attestations - for _, attestation := range attestations { - if attestation.PredicateType == policy.PredicateType { - res := compliance.Eval(policy, attestation.Content) - return ctx.JSON(200, res) - } - } - // we did not find any attestation that matches - lets add the policy with a nil result - return ctx.JSON(200, compliance.Eval(policy, nil)) -} - -func (c *ComplianceController) AssetCompliance(ctx shared.Context) error { - asset := shared.GetAsset(ctx) - assetVersion, err := shared.MaybeGetAssetVersion(ctx) - if err != nil { - // we need to get the default asset version - assetVersion, err = c.assetVersionRepository.GetDefaultAssetVersion(ctx.Request().Context(), nil, asset.ID) - if err != nil { - return ctx.JSON(404, nil) - } - } - - project := shared.GetProject(ctx) - - results, err := c.getAssetVersionCompliance(ctx.Request().Context(), project.ID, assetVersion) - if err != nil { - return ctx.JSON(500, nil) - } - - return ctx.JSON(200, results) -} - -func (c *ComplianceController) ProjectCompliance(ctx shared.Context) error { - // get all default asset version from the project - project := shared.GetProject(ctx) - assetVersions, err := c.assetVersionRepository.GetDefaultAssetVersionsByProjectID(ctx.Request().Context(), nil, project.ID) - - if err != nil { - return ctx.JSON(500, nil) - } - - results := make([][]compliance.PolicyEvaluation, 0, len(assetVersions)) - for _, assetVersion := range assetVersions { - compliance, err := c.getAssetVersionCompliance(ctx.Request().Context(), project.ID, assetVersion) - if err != nil { - return ctx.JSON(500, nil) - } - - results = append(results, compliance) - } - return ctx.JSON(200, results) -} diff --git a/controllers/compliance_risk_controller.go b/controllers/compliance_risk_controller.go new file mode 100644 index 000000000..8171dd547 --- /dev/null +++ b/controllers/compliance_risk_controller.go @@ -0,0 +1,313 @@ +package controllers + +import ( + "archive/zip" + "bytes" + "encoding/json" + "io" + "log/slog" + + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/dtos/sarif" + "github.com/l3montree-dev/devguard/shared" + "github.com/l3montree-dev/devguard/transformer" + "github.com/l3montree-dev/devguard/utils" + "github.com/labstack/echo/v4" +) + +type ComplianceRiskController struct { + complianceRiskRepository shared.ComplianceRiskRepository + complianceRiskService shared.ComplianceRiskService + complianceService shared.ComplianceService + attestationService shared.AttestationService + artifactRepository shared.ArtifactRepository +} + +func NewComplianceRiskController( + repo shared.ComplianceRiskRepository, + svc shared.ComplianceRiskService, + complianceService shared.ComplianceService, + attestationService shared.AttestationService, + artifactRepository shared.ArtifactRepository, +) *ComplianceRiskController { + return &ComplianceRiskController{ + complianceRiskRepository: repo, + complianceRiskService: svc, + complianceService: complianceService, + attestationService: attestationService, + artifactRepository: artifactRepository, + } +} + +type complianceRiskStatus struct { + StatusType string `json:"status"` + Justification string `json:"justification"` + MechanicalJustification dtos.MechanicalJustificationType `json:"mechanicalJustification"` +} + +func convertComplianceRiskToDetailedDTO(r models.ComplianceRisk) dtos.DetailedComplianceRiskDTO { + return dtos.DetailedComplianceRiskDTO{ + ComplianceRiskDTO: transformer.ComplianceRiskToDTO(r), + Events: utils.Map(r.Events, func(ev models.VulnEvent) dtos.VulnEventDTO { + return dtos.VulnEventDTO{ + ID: ev.ID, + Type: ev.Type, + VulnID: ev.GetVulnID(), + UserID: ev.UserID, + Justification: ev.Justification, + MechanicalJustification: ev.MechanicalJustification, + OriginalAssetVersionName: ev.OriginalAssetVersionName, + VulnerabilityName: r.PolicyID, + ArbitraryJSONData: ev.GetArbitraryJSONData(), + CreatedAt: ev.CreatedAt, + CreatedByVexRule: ev.CreatedByVexRule, + } + }), + } +} + +func (c *ComplianceRiskController) ListPaged(ctx shared.Context) error { + assetVersion := shared.GetAssetVersion(ctx) + + pagedResp, err := c.complianceRiskRepository.GetAllComplianceRisksForAssetVersionPaged( + ctx.Request().Context(), nil, + assetVersion.AssetID, + assetVersion.Name, + shared.GetPageInfo(ctx), + ctx.QueryParam("search"), + shared.GetFilterQuery(ctx), + shared.GetSortQuery(ctx), + ) + if err != nil { + return echo.NewHTTPError(500, "could not get compliance risks").WithInternal(err) + } + + frameworks, err := c.complianceRiskRepository.GetDistinctFrameworksForAssetVersion( + ctx.Request().Context(), nil, + assetVersion.AssetID, + assetVersion.Name, + ) + if err != nil { + return echo.NewHTTPError(500, "could not get frameworks").WithInternal(err) + } + + return ctx.JSON(200, struct { + shared.Paged[any] + Frameworks []string `json:"frameworks"` + }{ + Paged: pagedResp.Map(func(r models.ComplianceRisk) any { return convertComplianceRiskToDetailedDTO(r) }), + Frameworks: frameworks, + }) +} + +func (c *ComplianceRiskController) Read(ctx shared.Context) error { + riskID, _, err := shared.GetVulnID(ctx) + if err != nil { + return echo.NewHTTPError(400, "could not get compliance risk ID") + } + risk, err := c.complianceRiskRepository.Read(ctx.Request().Context(), nil, riskID) + if err != nil { + return echo.NewHTTPError(404, "could not find compliance risk") + } + return ctx.JSON(200, convertComplianceRiskToDetailedDTO(risk)) +} + +func (c *ComplianceRiskController) GetEvidence(ctx shared.Context) error { + riskID, _, err := shared.GetVulnID(ctx) + if err != nil { + return echo.NewHTTPError(400, "could not get compliance risk ID") + } + risk, err := c.complianceRiskRepository.Read(ctx.Request().Context(), nil, riskID) + if err != nil { + return echo.NewHTTPError(404, "could not find compliance risk") + } + if len(risk.EvidenceContent) == 0 { + return echo.NewHTTPError(404, "no evidence available") + } + + contentType := risk.EvidenceType + if contentType == "" || contentType == "json" { + contentType = "application/json" + } + + return ctx.Blob(200, contentType, risk.EvidenceContent) +} + +func (c *ComplianceRiskController) CreateEvent(ctx shared.Context) error { + thirdPartyIntegration := shared.GetThirdPartyIntegration(ctx) + riskID, _, err := shared.GetVulnID(ctx) + if err != nil { + return echo.NewHTTPError(400, "invalid compliance risk id") + } + + risk, err := c.complianceRiskRepository.Read(ctx.Request().Context(), nil, riskID) + if err != nil { + return echo.NewHTTPError(404, "could not find compliance risk") + } + + var status complianceRiskStatus + if err := json.NewDecoder(ctx.Request().Body).Decode(&status); err != nil { + return echo.NewHTTPError(400, "invalid payload").WithInternal(err) + } + if err := models.CheckStatusType(status.StatusType); err != nil { + return echo.NewHTTPError(400, "invalid status type") + } + + userID := shared.GetSession(ctx).GetUserID() + userAgent := ctx.Request().UserAgent() + + event, err := c.complianceRiskService.UpdateComplianceRiskState(ctx.Request().Context(), nil, userID, &risk, status.StatusType, status.Justification, status.MechanicalJustification, &userAgent) + if err != nil { + return echo.NewHTTPError(500, "could not create compliance risk event").WithInternal(err) + } + + if err := thirdPartyIntegration.HandleEvent(ctx.Request().Context(), shared.VulnEvent{ + Ctx: ctx, + Event: event, + }, &userAgent); err != nil { + slog.Error("could not handle third-party event for compliance risk", "err", err) + return echo.NewHTTPError(500, "could not create compliance risk event").WithInternal(err) + } + + return ctx.JSON(200, convertComplianceRiskToDetailedDTO(risk)) +} + +func (c *ComplianceRiskController) Mitigate(ctx shared.Context) error { + var justification struct { + Comment string `json:"comment"` + } + if err := ctx.Bind(&justification); err != nil { + return echo.NewHTTPError(500, "could not bind the request to a justification") + } + + riskID, _, err := shared.GetVulnID(ctx) + if err != nil { + return echo.NewHTTPError(400, "invalid compliance risk id") + } + + userAgent := ctx.Request().UserAgent() + thirdPartyIntegrations := shared.GetThirdPartyIntegration(ctx) + + if err := thirdPartyIntegrations.HandleEvent(ctx.Request().Context(), shared.ManualMitigateEvent{ + Ctx: ctx, + Justification: justification.Comment, + }, &userAgent); err != nil { + return echo.NewHTTPError(500, "could not mitigate compliance risk").WithInternal(err) + } + + risk, err := c.complianceRiskRepository.Read(ctx.Request().Context(), nil, riskID) + if err != nil { + return echo.NewHTTPError(404, "could not find compliance risk") + } + return ctx.JSON(200, convertComplianceRiskToDetailedDTO(risk)) +} + +// RunAttestationEvaluation fetches evaluations via complianceService.EvaluateArtifactAttestations and recalculates risks. +func (c *ComplianceRiskController) RunAttestationEvaluation(ctx shared.Context) error { + assetVersion := shared.GetAssetVersion(ctx) + artifact := shared.GetArtifact(ctx) + project := shared.GetProject(ctx) + userAgent := ctx.Request().UserAgent() + userID := shared.GetSession(ctx).GetUserID() + + sarifDoc, err := c.complianceService.EvaluateArtifactAttestations(ctx.Request().Context(), project.ID, assetVersion, artifact) + if err != nil { + return echo.NewHTTPError(500, "could not evaluate artifact compliance").WithInternal(err) + } + + if err := c.complianceRiskService.HandleArtifactCompliance(ctx.Request().Context(), nil, userID, &userAgent, assetVersion, artifact, sarifDoc); err != nil { + return echo.NewHTTPError(500, "could not handle artifact compliance risks").WithInternal(err) + } + + return ctx.JSON(200, sarifDoc) +} + +// UploadZip accepts a ZIP file containing attestation JSON files and a sarif.json. +// It saves each attestation and then recalculates compliance risks based on the SARIF. +func (c *ComplianceRiskController) UploadZip(ctx shared.Context) error { + assetVersion := shared.GetAssetVersion(ctx) + artifact := shared.GetArtifact(ctx) + userAgent := ctx.Request().UserAgent() + userID := shared.GetSession(ctx).GetUserID() + + file, err := ctx.FormFile("file") + if err != nil { + return echo.NewHTTPError(400, "missing zip file").WithInternal(err) + } + + src, err := file.Open() + if err != nil { + return echo.NewHTTPError(500, "could not open zip file").WithInternal(err) + } + defer src.Close() + + data, err := io.ReadAll(src) + if err != nil { + return echo.NewHTTPError(500, "could not read zip file").WithInternal(err) + } + + zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + return echo.NewHTTPError(400, "invalid zip file").WithInternal(err) + } + + var sarifDoc *sarif.SarifSchema210Json + + for _, f := range zr.File { + rc, err := f.Open() + if err != nil { + slog.Warn("could not open file in zip", "name", f.Name, "err", err) + continue + } + content, err := io.ReadAll(rc) + rc.Close() + if err != nil { + slog.Warn("could not read file in zip", "name", f.Name, "err", err) + continue + } + + if f.Name == "sarif.json" { + var doc sarif.SarifSchema210Json + if err := json.Unmarshal(content, &doc); err != nil { + return echo.NewHTTPError(400, "invalid sarif.json in zip").WithInternal(err) + } + sarifDoc = &doc + continue + } + + // treat remaining files as attestations; predicateType is read from the JSON content + var contentMap map[string]any + if err := json.Unmarshal(content, &contentMap); err != nil { + slog.Warn("skipping non-JSON attestation file in zip", "name", f.Name, "err", err) + continue + } + + predicateType, ok := contentMap["predicateType"].(string) + if !ok || predicateType == "" { + slog.Warn("attestation file missing predicateType field, skipping", "name", f.Name) + continue + } + + attestation := models.Attestation{ + AssetID: assetVersion.AssetID, + AssetVersionName: assetVersion.Name, + ArtifactName: artifact.ArtifactName, + PredicateType: predicateType, + Content: contentMap, + } + if err := c.attestationService.Create(ctx.Request().Context(), nil, &attestation); err != nil { + slog.Error("could not save attestation from zip", "name", f.Name, "err", err) + } + } + + if sarifDoc == nil { + return echo.NewHTTPError(400, "sarif.json not found in zip") + } + + if err := c.complianceRiskService.HandleArtifactCompliance(ctx.Request().Context(), nil, userID, &userAgent, assetVersion, artifact, *sarifDoc); err != nil { + return echo.NewHTTPError(500, "could not handle artifact compliance risks").WithInternal(err) + } + + return ctx.JSON(200, sarifDoc) +} diff --git a/controllers/policy_controller.go b/controllers/policy_controller.go deleted file mode 100644 index b580df62e..000000000 --- a/controllers/policy_controller.go +++ /dev/null @@ -1,265 +0,0 @@ -package controllers - -import ( - "context" - "log/slog" - "sync" - - "github.com/google/uuid" - "github.com/l3montree-dev/devguard/compliance" - "github.com/l3montree-dev/devguard/database/models" - "github.com/l3montree-dev/devguard/dtos" - "github.com/l3montree-dev/devguard/shared" - "github.com/l3montree-dev/devguard/utils" -) - -type PolicyController struct { - policyRepository shared.PolicyRepository - projectRepository shared.ProjectRepository -} - -func NewPolicyController(policyRepository shared.PolicyRepository, projectRepository shared.ProjectRepository) *PolicyController { - c := &PolicyController{ - policyRepository: policyRepository, - projectRepository: projectRepository, - } - - if err := c.migratePolicies(); err != nil { - panic(err) - } - return c -} - -func (c *PolicyController) migratePolicies() error { - ctx := context.Background() - // we need to migrate the policies from the old format to the new format - // this is only needed for the first time we run the application - // after that we can remove this function - policies := compliance.GetCommunityManagedPoliciesFromFS() - policyModels := make([]models.Policy, len(policies)) - for i, policy := range policies { - policyModels[i] = compliance.ConvertPolicyFsToModel(policy) - } - - // get all community managed policies from the database - dbPolicies, err := c.policyRepository.FindCommunityManagedPolicies(ctx, nil) - if err != nil { - return err - } - - // compare the policies - comp := utils.CompareSlices(policyModels, dbPolicies, func(p models.Policy) string { - return *p.OpaqueID - }) - - toCreate := comp.OnlyInA - toUpdate := comp.InBothB // use the B elements - those are the new policies read from disk - toDelete := comp.OnlyInB - - // set the id for the policies to update - for i := range toUpdate { - for j := range dbPolicies { - if dbPolicies[j].OpaqueID == toUpdate[i].OpaqueID { - toUpdate[i].ID = dbPolicies[j].ID - break - } - } - } - - // create the policies - if len(toCreate) > 0 { - if err := c.policyRepository.CreateBatch(ctx, nil, toCreate); err != nil { - return err - } - } - - // update the policies - if len(toUpdate) > 0 { - if err := c.policyRepository.SaveBatch(ctx, nil, toUpdate); err != nil { - return err - } - } - - // delete the policies - if len(toDelete) > 0 { - wg := sync.WaitGroup{} - for _, policy := range toDelete { - wg.Add(1) - go func(p models.Policy) { - defer wg.Done() - err := c.policyRepository.GetDB(ctx, nil).Model(&p).Association("Projects").Clear() - if err != nil { - slog.Warn("failed to clear projects association for policy", "policyID", p.ID, "error", err) - return - } - }(policy) - } - wg.Wait() - if err := c.policyRepository.DeleteBatch(ctx, nil, toDelete); err != nil { - return err - } - } - - return nil -} - -func (c *PolicyController) GetOrganizationPolicies(ctx shared.Context) error { - - org := shared.GetOrg(ctx) - policies, err := c.policyRepository.FindByOrganizationID(ctx.Request().Context(), nil, org.ID) - - if err != nil { - return err - } - - // include the community managed policies - communityPolicies, err := c.policyRepository.FindCommunityManagedPolicies(ctx.Request().Context(), nil) - if err != nil { - return err - } - - return ctx.JSON(200, append(policies, communityPolicies...)) -} - -func (c *PolicyController) GetProjectPolicies(ctx shared.Context) error { - project := shared.GetProject(ctx) - policies, err := c.policyRepository.FindByProjectID(ctx.Request().Context(), nil, project.ID) - - if err != nil { - return err - } - - return ctx.JSON(200, policies) -} - -func (c *PolicyController) GetPolicy(ctx shared.Context) error { - policyID := ctx.Param("policyID") - - // parse the uuid - policyUUID, err := uuid.Parse(policyID) - if err != nil { - return err - } - - policy, err := c.policyRepository.Read(ctx.Request().Context(), nil, policyUUID) - - if err != nil { - return err - } - - return ctx.JSON(200, policy) -} - -func (c *PolicyController) CreatePolicy(ctx shared.Context) error { - policy := dtos.PolicyDTO{} - if err := ctx.Bind(&policy); err != nil { - return err - } - - org := shared.GetOrg(ctx) - - // create a new policy model - policyModel := models.Policy{ - Rego: policy.Rego, - Description: policy.Description, - Title: policy.Title, - PredicateType: policy.PredicateType, - OrganizationID: utils.Ptr(org.ID), - OpaqueID: nil, - } - - // create the policy - if err := c.policyRepository.Create(ctx.Request().Context(), nil, &policyModel); err != nil { - return err - } - - return ctx.JSON(201, policy) -} - -func (c *PolicyController) UpdatePolicy(ctx shared.Context) error { - policyID := ctx.Param("policyID") - - // parse the uuid - policyUUID, err := uuid.Parse(policyID) - if err != nil { - return err - } - - policy := dtos.PolicyDTO{} - if err := ctx.Bind(&policy); err != nil { - return err - } - - org := shared.GetOrg(ctx) - - // create a new policy model - policyModel := models.Policy{ - ID: policyUUID, - Rego: policy.Rego, - Description: policy.Description, - Title: policy.Title, - PredicateType: policy.PredicateType, - OrganizationID: utils.Ptr(org.ID), - } - - if err := c.policyRepository.Save(ctx.Request().Context(), nil, &policyModel); err != nil { - return err - } - - return ctx.JSON(200, policyModel) -} - -func (c *PolicyController) DeletePolicy(ctx shared.Context) error { - policyID := ctx.Param("policyID") - - // parse the uuid - policyUUID, err := uuid.Parse(policyID) - if err != nil { - return err - } - - // delete the policy - if err := c.policyRepository.Delete(ctx.Request().Context(), nil, policyUUID); err != nil { - return err - } - - return ctx.NoContent(204) -} - -func (c *PolicyController) EnablePolicyForProject(ctx shared.Context) error { - policyID := ctx.Param("policyID") - - project := shared.GetProject(ctx) - - // parse the uuid - policyUUID, err := uuid.Parse(policyID) - if err != nil { - return err - } - - // enable the policy for the project - if err := c.projectRepository.EnablePolicyForProject(ctx.Request().Context(), nil, project.ID, policyUUID); err != nil { - return err - } - - return ctx.NoContent(204) -} - -func (c *PolicyController) DisablePolicyForProject(ctx shared.Context) error { - policyID := ctx.Param("policyID") - - // parse the uuid - policyUUID, err := uuid.Parse(policyID) - if err != nil { - return err - } - - project := shared.GetProject(ctx) - - // disable the policy for the project - if err := c.projectRepository.DisablePolicyForProject(ctx.Request().Context(), nil, project.ID, policyUUID); err != nil { - return err - } - - return ctx.NoContent(204) -} diff --git a/controllers/providers.go b/controllers/providers.go index 193f9c996..fb96666f9 100644 --- a/controllers/providers.go +++ b/controllers/providers.go @@ -90,10 +90,9 @@ var ControllerModule = fx.Options( // Security & Compliance fx.Provide(NewCSAFController), - fx.Provide(NewComplianceController), fx.Provide(NewAttestationController), fx.Provide(NewInToToController), - fx.Provide(NewPolicyController), + fx.Provide(NewComplianceRiskController), // Integrations fx.Provide(NewIntegrationController), diff --git a/daemons/attestation_daemon.go b/daemons/attestation_daemon.go new file mode 100644 index 000000000..4aa921bef --- /dev/null +++ b/daemons/attestation_daemon.go @@ -0,0 +1,87 @@ +package daemons + +import ( + "log/slog" + + "github.com/l3montree-dev/devguard/monitoring" +) + +func (runner *DaemonRunner) CheckArtifactCompliance(input <-chan assetWithProjectAndOrg, errChan chan<- pipelineError) <-chan assetWithProjectAndOrg { + out := make(chan assetWithProjectAndOrg) + + go func() { + defer func() { + close(out) + monitoring.RecoverPanic("check artifact compliance panic") + }() + + for assetWithDetails := range input { + stageCtx, span := daemonTracer.Start(assetWithDetails.ctx, "pipeline.check-artifact-compliance") + + for _, assetVersion := range assetWithDetails.assetVersions { + for _, artifact := range assetVersion.Artifacts { + sarifDoc, err := runner.complianceService.EvaluateArtifactAttestations(stageCtx, assetWithDetails.project.ID, assetVersion, artifact) + if err != nil { + slog.Error("could not evaluate artifact compliance", + "assetID", assetWithDetails.asset.ID, + "assetVersion", assetVersion.Name, + "artifactName", artifact.ArtifactName, + "err", err, + ) + continue + } + if err := runner.complianceRiskService.HandleArtifactCompliance(stageCtx, nil, "system", nil, assetVersion, artifact, sarifDoc); err != nil { + slog.Error("could not handle artifact compliance risks", + "assetID", assetWithDetails.asset.ID, + "assetVersion", assetVersion.Name, + "artifactName", artifact.ArtifactName, + "err", err, + ) + } + } + } + + span.End() + out <- assetWithDetails + } + }() + + return out +} + +// GenerateDevguardAttestations is a pipeline stage that computes the DevGuard asset +// metrics attestation for every asset version and upserts it into the attestations table. +// It runs after CollectStats so that risk data is already up to date. +func (runner *DaemonRunner) GenerateDevguardAttestations(input <-chan assetWithProjectAndOrg, errChan chan<- pipelineError) <-chan assetWithProjectAndOrg { + out := make(chan assetWithProjectAndOrg) + + go func() { + defer func() { + close(out) + monitoring.RecoverPanic("generate devguard attestations panic") + }() + + for assetWithDetails := range input { + stageCtx, span := daemonTracer.Start(assetWithDetails.ctx, "pipeline.generate-devguard-attestations") + + for _, assetVersion := range assetWithDetails.assetVersions { + for _, artifact := range assetVersion.Artifacts { + if err := runner.attestationService.GenerateAndStoreDevguardAttestation(stageCtx, assetVersion.AssetID, assetVersion.Name, artifact.ArtifactName); err != nil { + slog.Error("could not generate devguard attestation", + "assetID", assetWithDetails.asset.ID, + "assetVersion", assetVersion.Name, + "artifactName", artifact.ArtifactName, + "err", err, + ) + // non-fatal: log and continue to next artifact + } + } + } + + span.End() + out <- assetWithDetails + } + }() + + return out +} diff --git a/daemons/daemon_asset_pipeline.go b/daemons/daemon_asset_pipeline.go index 13833668b..6417c1256 100644 --- a/daemons/daemon_asset_pipeline.go +++ b/daemons/daemon_asset_pipeline.go @@ -61,6 +61,8 @@ func (runner *DaemonRunner) runPipeline(ctx context.Context, idsChan <-chan uuid ch = runner.SyncTickets(ch, errChan) ch = runner.ResolveDifferencesInTicketState(ch, errChan) ch = runner.CollectStats(ch, errChan) + ch = runner.GenerateDevguardAttestations(ch, errChan) + ch = runner.CheckArtifactCompliance(ch, errChan) utils.WaitForChannelDrain(ch) // we can close the error channel now // since it is a chan<-pipelineError we can be sure that all errors have been sent diff --git a/daemons/providers.go b/daemons/providers.go index d8870d294..c84f09b7e 100644 --- a/daemons/providers.go +++ b/daemons/providers.go @@ -62,6 +62,10 @@ type DaemonRunner struct { maliciousPackageChecker shared.MaliciousPackageChecker vulnDBImportService shared.VulnDBService vexRuleService shared.VEXRuleService + attestationService shared.AttestationService + statisticsRepository shared.StatisticsRepository + complianceService shared.ComplianceService + complianceRiskService shared.ComplianceRiskService debugOptions DebugOptions fixedVersionResolver shared.FixedVersionResolver @@ -106,6 +110,10 @@ func NewDaemonRunner( vulnDBImportService shared.VulnDBService, vexRuleService shared.VEXRuleService, fixedVersionResolver shared.FixedVersionResolver, + attestationService shared.AttestationService, + statisticsRepository shared.StatisticsRepository, + complianceService shared.ComplianceService, + complianceRiskService shared.ComplianceRiskService, ) *DaemonRunner { return &DaemonRunner{ db: db, @@ -137,6 +145,10 @@ func NewDaemonRunner( vulnDBImportService: vulnDBImportService, vexRuleService: vexRuleService, fixedVersionResolver: fixedVersionResolver, + attestationService: attestationService, + statisticsRepository: statisticsRepository, + complianceService: complianceService, + complianceRiskService: complianceRiskService, } } diff --git a/database/migrations/20260602000000_add_compliance_risks_drop_policies.up.sql b/database/migrations/20260602000000_add_compliance_risks_drop_policies.up.sql new file mode 100644 index 000000000..8e646f129 --- /dev/null +++ b/database/migrations/20260602000000_add_compliance_risks_drop_policies.up.sql @@ -0,0 +1,70 @@ +-- Drop policy tables (data is now embedded in compliance_risks) +DROP TABLE IF EXISTS public.project_enabled_policies; +DROP TABLE IF EXISTS public.policies; + +CREATE TABLE IF NOT EXISTS public.compliance_risks ( + id uuid NOT NULL, + asset_version_name text NOT NULL, + asset_id uuid NOT NULL, + message text, + state text DEFAULT 'open' NOT NULL, + last_detected timestamp with time zone DEFAULT now() NOT NULL, + ticket_id text, + ticket_url text, + manual_ticket_creation boolean DEFAULT false, + created_at timestamp with time zone, + updated_at timestamp with time zone, + deleted_at timestamp with time zone, + policy_id text NOT NULL, + policy_title text NOT NULL DEFAULT '', + policy_description text, + evidence_type text NOT NULL DEFAULT '', + policy_related_resources jsonb DEFAULT '[]', + policy_tags jsonb DEFAULT '[]', + policy_priority integer, + "policyFrameworks" jsonb, + evidence_content bytea, + violations jsonb DEFAULT '[]', + CONSTRAINT compliance_risks_pkey PRIMARY KEY (id), + CONSTRAINT fk_compliance_risks_asset_versions FOREIGN KEY (asset_version_name, asset_id) + REFERENCES public.asset_versions (name, asset_id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS public.artifact_compliance_risks ( + artifact_artifact_name text NOT NULL, + artifact_asset_version_name text NOT NULL, + artifact_asset_id uuid NOT NULL, + compliance_risk_id uuid NOT NULL, + CONSTRAINT artifact_compliance_risks_pkey PRIMARY KEY (artifact_artifact_name, artifact_asset_version_name, artifact_asset_id, compliance_risk_id), + CONSTRAINT fk_artifact_compliance_risks_artifact FOREIGN KEY (artifact_artifact_name, artifact_asset_version_name, artifact_asset_id) + REFERENCES public.artifacts (artifact_name, asset_version_name, asset_id) ON DELETE CASCADE, + CONSTRAINT fk_artifact_compliance_risks_compliance_risk FOREIGN KEY (compliance_risk_id) + REFERENCES public.compliance_risks (id) ON DELETE CASCADE +); + +-- Add compliance_risk_id column to vuln_events +ALTER TABLE public.vuln_events + ADD COLUMN IF NOT EXISTS compliance_risk_id uuid; + +ALTER TABLE public.vuln_events + DROP CONSTRAINT IF EXISTS fk_vuln_events_compliance_risk; + +ALTER TABLE public.vuln_events + ADD CONSTRAINT fk_vuln_events_compliance_risk FOREIGN KEY (compliance_risk_id) + REFERENCES public.compliance_risks (id) ON DELETE CASCADE; + +-- Drop old one_vuln_parent check (only 3 columns) and replace with updated version including compliance_risk_id +ALTER TABLE public.vuln_events DROP CONSTRAINT IF EXISTS one_vuln_parent; + +ALTER TABLE public.vuln_events ADD CONSTRAINT one_vuln_parent CHECK ( + (dependency_vuln_id IS NOT NULL)::int + + (license_risk_id IS NOT NULL)::int + + (first_party_vuln_id IS NOT NULL)::int + + (compliance_risk_id IS NOT NULL)::int = 1 +); + +CREATE INDEX IF NOT EXISTS idx_compliance_risks_asset_version + ON public.compliance_risks (asset_version_name, asset_id); + +CREATE INDEX IF NOT EXISTS idx_artifact_compliance_risks_compliance_risk_id + ON public.artifact_compliance_risks (compliance_risk_id); diff --git a/database/models/compliance_risk_model.go b/database/models/compliance_risk_model.go new file mode 100644 index 000000000..f9b80442b --- /dev/null +++ b/database/models/compliance_risk_model.go @@ -0,0 +1,98 @@ +// Copyright (C) 2026 l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +package models + +import ( + "fmt" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/utils" + "gorm.io/gorm" +) + +type PolicyFrameworks struct { + Framework string `yaml:"framework" json:"framework"` + Controls []string `yaml:"controls" json:"controls"` +} +type ComplianceRisk struct { + Vulnerability + + PolicyID string `json:"policyId" gorm:"type:text;"` + PolicyTitle string `json:"policyTitle" gorm:"type:text;"` + PolicyDescription *string `json:"policyDescription" gorm:"type:text;"` + PolicyRelatedResources []string `json:"policyRelatedResources" gorm:"type:jsonb;serializer:json"` + PolicyTags []string `json:"policyTags" gorm:"type:jsonb;serializer:json"` + PolicyPriority int `json:"policyPriority"` + PolicyFrameworks []PolicyFrameworks `json:"policyFrameworks" gorm:"column:policyFrameworks;type:jsonb;serializer:json"` + EvidenceType string `json:"evidenceType" gorm:"type:text;"` + EvidenceContent []byte `json:"evidenceContent" gorm:"type:bytea;"` + + Message string `json:"message" gorm:"type:text;"` + + Violations []string `json:"violations" gorm:"type:jsonb;serializer:json"` + + Events []VulnEvent `gorm:"foreignKey:ComplianceRiskID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;" json:"events"` + + Artifacts []Artifact `json:"artifacts" gorm:"many2many:artifact_compliance_risks;constraint:OnDelete:CASCADE"` +} + +func (complianceRisk ComplianceRisk) TableName() string { + return "compliance_risks" +} + +func (complianceRisk ComplianceRisk) GetType() dtos.VulnType { + return dtos.VulnTypeComplianceRisk +} + +func (complianceRisk *ComplianceRisk) CalculateHash() uuid.UUID { + return utils.HashToUUID(fmt.Sprintf("%s/%s/%s", complianceRisk.PolicyID, complianceRisk.AssetVersionName, complianceRisk.AssetID)) +} + +func (complianceRisk *ComplianceRisk) BeforeSave(tx *gorm.DB) error { + complianceRisk.ID = complianceRisk.CalculateHash() + return nil +} + +func (complianceRisk ComplianceRisk) GetEvents() []VulnEvent { + return complianceRisk.Events +} + +func (complianceRisk *ComplianceRisk) GetArtifacts() []Artifact { + return complianceRisk.Artifacts +} + +func (complianceRisk ComplianceRisk) GetAssetVersionName() string { + return complianceRisk.AssetVersionName +} + +func (complianceRisk ComplianceRisk) AssetVersionIndependentHash() string { + return utils.HashString(complianceRisk.PolicyID) +} + +func (complianceRisk *ComplianceRisk) GetArtifactNames() string { + names := "" + for _, a := range complianceRisk.Artifacts { + if names != "" { + names += ", " + } + names += a.ArtifactName + } + return names +} + +func (complianceRisk ComplianceRisk) Title() string { + return fmt.Sprintf("Compliance risk for policy %s", complianceRisk.PolicyID) +} diff --git a/database/models/policy_model.go b/database/models/policy_model.go deleted file mode 100644 index 3c1966b64..000000000 --- a/database/models/policy_model.go +++ /dev/null @@ -1,21 +0,0 @@ -package models - -import "github.com/google/uuid" - -type Policy struct { - ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey;default:gen_random_uuid()"` - Rego string `json:"rego"` - Title string `json:"title"` - PredicateType string `json:"predicateType"` - Description string `json:"description"` - - OrganizationID *uuid.UUID `json:"organizationId"` // will be null for global policies - Organization *Org `json:"organization" gorm:"foreignKey:OrganizationID;references:ID;constraint:OnDelete:CASCADE;"` - - OpaqueID *string `json:"opaqueId" gorm:"unique"` // only used by global policies maintained by the community and migrated by the system - Projects []Project `json:"projects" gorm:"many2many:project_enabled_policies;constraint:OnDelete:CASCADE;"` -} - -func (m Policy) TableName() string { - return "policies" -} diff --git a/database/models/project_model.go b/database/models/project_model.go index deec3e8dd..aa7450861 100644 --- a/database/models/project_model.go +++ b/database/models/project_model.go @@ -43,8 +43,6 @@ type Project struct { ConfigFiles databasetypes.JSONB `json:"configFiles" gorm:"type:jsonb"` - EnabledPolicies []Policy `json:"enabledPolicies" gorm:"many2many:project_enabled_policies;constraint:OnDelete:CASCADE;"` - ExternalEntityID *string `json:"externalEntityId" gorm:"uniqueIndex:unique_external_entity;"` ExternalEntityProviderID *string `json:"externalEntityProviderId" gorm:"uniqueIndex:unique_external_entity;"` ExternalEntityParentID *string `json:"externalEntityProviderParentId" gorm:"type:text;"` diff --git a/database/models/vulnevent_model.go b/database/models/vulnevent_model.go index 1e97aabd7..e614746ca 100644 --- a/database/models/vulnevent_model.go +++ b/database/models/vulnevent_model.go @@ -17,6 +17,7 @@ type VulnEvent struct { DependencyVulnID *uuid.UUID `json:"dependencyVulnId" gorm:"type:uuid;column:dependency_vuln_id"` LicenseRiskID *uuid.UUID `json:"licenseRiskId" gorm:"type:uuid;column:license_risk_id"` FirstPartyVulnID *uuid.UUID `json:"firstPartyVulnId" gorm:"type:uuid;column:first_party_vuln_id"` + ComplianceRiskID *uuid.UUID `json:"complianceRiskId" gorm:"type:uuid;column:compliance_risk_id"` UserID string `json:"userId"` Justification *string `json:"justification" gorm:"type:text;"` MechanicalJustification dtos.MechanicalJustificationType `json:"mechanicalJustification" gorm:"type:text;"` @@ -45,6 +46,9 @@ func (event VulnEvent) GetVulnID() uuid.UUID { if event.FirstPartyVulnID != nil { return *event.FirstPartyVulnID } + if event.ComplianceRiskID != nil { + return *event.ComplianceRiskID + } return uuid.Nil } @@ -59,6 +63,9 @@ func (event VulnEvent) GetVulnType() dtos.VulnType { if event.FirstPartyVulnID != nil { return dtos.VulnTypeFirstPartyVuln } + if event.ComplianceRiskID != nil { + return dtos.VulnTypeComplianceRisk + } return "" } @@ -71,6 +78,8 @@ func SetVulnIDOnEvent(event *VulnEvent, vulnID uuid.UUID, vulnType dtos.VulnType event.LicenseRiskID = &vulnID case dtos.VulnTypeFirstPartyVuln: event.FirstPartyVulnID = &vulnID + case dtos.VulnTypeComplianceRisk: + event.ComplianceRiskID = &vulnID } } diff --git a/database/repositories/attestation_repository.go b/database/repositories/attestation_repository.go index c3b6491ce..2085dad5d 100644 --- a/database/repositories/attestation_repository.go +++ b/database/repositories/attestation_repository.go @@ -43,6 +43,15 @@ func (a *attestationRepository) GetByAssetVersionAndAssetID(ctx context.Context, return attestationList, nil } +func (a *attestationRepository) GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx *gorm.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) { + var attestationList []models.Attestation + err := a.GetDB(ctx, tx).Where("asset_id = ? AND asset_version_name = ? AND artifact_name = ?", assetID, assetVersion, artifactName).Find(&attestationList).Error + if err != nil { + return attestationList, err + } + return attestationList, nil +} + func (a *attestationRepository) Create(ctx context.Context, tx *gorm.DB, attestation *models.Attestation) error { return a.GetDB(ctx, tx).Clauses(clause.OnConflict{ Columns: []clause.Column{ diff --git a/database/repositories/compliance_risk_repository.go b/database/repositories/compliance_risk_repository.go new file mode 100644 index 000000000..b5c345224 --- /dev/null +++ b/database/repositories/compliance_risk_repository.go @@ -0,0 +1,136 @@ +package repositories + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/shared" + "github.com/l3montree-dev/devguard/statemachine" + "github.com/l3montree-dev/devguard/utils" + "gorm.io/gorm" +) + +type ComplianceRiskRepository struct { + utils.Repository[uuid.UUID, models.ComplianceRisk, *gorm.DB] + db *gorm.DB +} + +func NewComplianceRiskRepository(db *gorm.DB) *ComplianceRiskRepository { + return &ComplianceRiskRepository{ + db: db, + Repository: newGormRepository[uuid.UUID, models.ComplianceRisk](db), + } +} + +func (r *ComplianceRiskRepository) GetAllComplianceRisksForAssetVersion(ctx context.Context, tx *gorm.DB, assetID uuid.UUID, assetVersionName string) ([]models.ComplianceRisk, error) { + var result []models.ComplianceRisk + err := r.GetDB(ctx, tx).Preload("Artifacts").Where("asset_id = ? AND asset_version_name = ?", assetID, assetVersionName).Find(&result).Error + return result, err +} + +func (r *ComplianceRiskRepository) GetAllComplianceRisksForAssetVersionPaged(ctx context.Context, tx *gorm.DB, assetID uuid.UUID, assetVersionName string, pageInfo shared.PageInfo, search string, filter []shared.FilterQuery, sort []shared.SortQuery) (shared.Paged[models.ComplianceRisk], error) { + var count int64 + var risks []models.ComplianceRisk + + q := r.GetDB(ctx, tx).Model(&models.ComplianceRisk{}). + Preload("Artifacts"). + Joins("LEFT JOIN artifact_compliance_risks ON artifact_compliance_risks.compliance_risk_id = compliance_risks.id"). + Where("compliance_risks.asset_version_name = ?", assetVersionName). + Where("compliance_risks.asset_id = ?", assetID). + Distinct() + + for _, f := range filter { + q = q.Where(f.SQL(), f.Value()) + } + + if len(search) > 2 { + q = q.Where("compliance_risks.policy_id ILIKE ?", "%"+search+"%") + } + + if err := q.Session(&gorm.Session{}).Distinct("compliance_risks.id").Count(&count).Error; err != nil { + return shared.Paged[models.ComplianceRisk]{}, err + } + + err := q.Limit(pageInfo.PageSize).Offset((pageInfo.Page - 1) * pageInfo.PageSize).Find(&risks).Error + if err != nil { + return shared.Paged[models.ComplianceRisk]{}, err + } + return shared.NewPaged(pageInfo, count, risks), nil +} + +func (r *ComplianceRiskRepository) Read(ctx context.Context, tx *gorm.DB, id uuid.UUID) (models.ComplianceRisk, error) { + var risk models.ComplianceRisk + err := r.GetDB(ctx, tx).Where("id = ?", id). + Preload("Artifacts"). + Preload("Events", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at ASC") + }). + First(&risk).Error + return risk, err +} + +func (r *ComplianceRiskRepository) ApplyAndSave(ctx context.Context, tx *gorm.DB, risk *models.ComplianceRisk, ev *models.VulnEvent) error { + if tx == nil { + return r.Transaction(ctx, func(d *gorm.DB) error { + return r.applyAndSave(ctx, d, risk, ev) + }) + } + return r.applyAndSave(ctx, tx, risk, ev) +} + +func (r *ComplianceRiskRepository) applyAndSave(ctx context.Context, tx *gorm.DB, risk *models.ComplianceRisk, ev *models.VulnEvent) error { + statemachine.Apply(risk, *ev) + if err := r.Save(ctx, tx, risk); err != nil { + return err + } + if err := r.GetDB(ctx, tx).Save(ev).Error; err != nil { + return err + } + risk.Events = append(risk.Events, *ev) + return nil +} + +func (r *ComplianceRiskRepository) GetComplianceRisksByOtherAssetVersions(ctx context.Context, tx *gorm.DB, assetVersionName string, assetID uuid.UUID) ([]models.ComplianceRisk, error) { + var risks []models.ComplianceRisk + q := r.GetDB(ctx, tx). + Preload("Events", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at ASC") + }). + Preload("Artifacts"). + Where("compliance_risks.asset_version_name != ? AND compliance_risks.asset_id = ?", assetVersionName, assetID) + if err := q.Find(&risks).Error; err != nil { + return nil, err + } + return risks, nil +} + +func (r *ComplianceRiskRepository) GetDistinctFrameworksForAssetVersion(ctx context.Context, tx *gorm.DB, assetID uuid.UUID, assetVersionName string) ([]string, error) { + type result struct { + Framework string + } + var rows []result + err := r.GetDB(ctx, tx).Raw(` + SELECT DISTINCT elem->>'framework' AS framework + FROM compliance_risks, + jsonb_array_elements("policyFrameworks") AS elem + WHERE asset_id = ? AND asset_version_name = ? + AND elem->>'framework' IS NOT NULL + ORDER BY framework + `, assetID, assetVersionName).Scan(&rows).Error + if err != nil { + return nil, err + } + frameworks := make([]string, len(rows)) + for i, row := range rows { + frameworks[i] = row.Framework + } + return frameworks, nil +} + +func (r *ComplianceRiskRepository) SaveBatch(ctx context.Context, tx *gorm.DB, risks []models.ComplianceRisk) error { + if len(risks) == 0 { + return nil + } + return r.GetDB(ctx, tx).Save(&risks).Error +} diff --git a/database/repositories/policy_repository.go b/database/repositories/policy_repository.go deleted file mode 100644 index 717fba7a3..000000000 --- a/database/repositories/policy_repository.go +++ /dev/null @@ -1,51 +0,0 @@ -package repositories - -import ( - "context" - - "github.com/google/uuid" - "github.com/l3montree-dev/devguard/database/models" - "github.com/l3montree-dev/devguard/utils" - "gorm.io/gorm" -) - -type policyRepository struct { - db *gorm.DB - utils.Repository[uuid.UUID, models.Policy, *gorm.DB] -} - -func NewPolicyRepository(db *gorm.DB) *policyRepository { - return &policyRepository{ - db: db, - Repository: newGormRepository[uuid.UUID, models.Policy](db), - } -} - -func (r *policyRepository) FindByProjectID(ctx context.Context, tx *gorm.DB, projectID uuid.UUID) ([]models.Policy, error) { - // we need to use the project_enabled_policies pivot table to get the policies for a project - var policies []models.Policy - if err := r.GetDB(ctx, tx).Joins("JOIN project_enabled_policies ON project_enabled_policies.policy_id = policies.id"). - Where("project_enabled_policies.project_id = ?", projectID). - Find(&policies).Error; err != nil { - return nil, err - } - - return policies, nil -} - -func (r *policyRepository) FindCommunityManagedPolicies(ctx context.Context, tx *gorm.DB) ([]models.Policy, error) { - // where organization id is nil - var policies []models.Policy - if err := r.GetDB(ctx, tx).Where("organization_id IS NULL").Find(&policies).Error; err != nil { - return nil, err - } - return policies, nil -} - -func (r *policyRepository) FindByOrganizationID(ctx context.Context, tx *gorm.DB, organizationID uuid.UUID) ([]models.Policy, error) { - var policies []models.Policy - if err := r.GetDB(ctx, tx).Find(&policies, "organization_id = ?", organizationID).Error; err != nil { - return nil, err - } - return policies, nil -} diff --git a/database/repositories/project_repository.go b/database/repositories/project_repository.go index 9400464e1..b253d20cb 100644 --- a/database/repositories/project_repository.go +++ b/database/repositories/project_repository.go @@ -426,31 +426,6 @@ func (g *projectRepository) GetDirectChildProjects(ctx context.Context, tx *gorm return projects, err } -func (g *projectRepository) EnablePolicyForProject(ctx context.Context, tx *gorm.DB, projectID uuid.UUID, policyID uuid.UUID) error { - return g.GetDB(ctx, tx).Model(&models.Project{ - Model: models.Model{ - ID: projectID, - }, - }).Association("EnabledPolicies").Append(&models.Policy{ID: policyID}) -} -func (g *projectRepository) DisablePolicyForProject(ctx context.Context, tx *gorm.DB, projectID uuid.UUID, policyID uuid.UUID) error { - return g.GetDB(ctx, tx).Model(&models.Project{ - Model: models.Model{ - ID: projectID, - }, - }).Association("EnabledPolicies").Delete(&models.Policy{ID: policyID}) -} - -func (g *projectRepository) EnableCommunityManagedPolicies(ctx context.Context, tx *gorm.DB, projectID uuid.UUID) error { - // community policies can be identified by their "organization_id" being nil - return g.GetDB(ctx, tx).Exec(` - INSERT INTO project_enabled_policies (project_id, policy_id) - SELECT ?, id - FROM policies - WHERE organization_id IS NULL - `, projectID).Error -} - func (g *projectRepository) Create(ctx context.Context, tx *gorm.DB, project *models.Project) error { // set the slug if not set slug, err := g.firstFreeSlug(ctx, tx, project.OrganizationID, project.Slug) diff --git a/database/repositories/providers.go b/database/repositories/providers.go index 886db5816..0c57020f5 100644 --- a/database/repositories/providers.go +++ b/database/repositories/providers.go @@ -41,8 +41,8 @@ var Module = fx.Options( fx.Provide(fx.Annotate(NewInTotoLinkRepository, fx.As(new(shared.InTotoLinkRepository)))), fx.Provide(fx.Annotate(NewSupplyChainRepository, fx.As(new(shared.SupplyChainRepository)))), fx.Provide(fx.Annotate(NewAttestationRepository, fx.As(new(shared.AttestationRepository)))), - fx.Provide(fx.Annotate(NewPolicyRepository, fx.As(new(shared.PolicyRepository)))), fx.Provide(fx.Annotate(NewLicenseRiskRepository, fx.As(new(shared.LicenseRiskRepository)))), + fx.Provide(fx.Annotate(NewComplianceRiskRepository, fx.As(new(shared.ComplianceRiskRepository)))), fx.Provide(fx.Annotate(NewWebhookRepository, fx.As(new(shared.WebhookIntegrationRepository)))), fx.Provide(fx.Annotate(NewArtifactRepository, fx.As(new(shared.ArtifactRepository)))), fx.Provide(fx.Annotate(NewInvitationRepository, fx.As(new(shared.InvitationRepository)))), diff --git a/dtos/compliance_risk_dto.go b/dtos/compliance_risk_dto.go new file mode 100644 index 000000000..aa221045f --- /dev/null +++ b/dtos/compliance_risk_dto.go @@ -0,0 +1,42 @@ +package dtos + +import ( + "time" + + "github.com/google/uuid" +) + +type PolicyFrameworks struct { + Framework string `yaml:"framework" json:"framework"` + Controls []string `yaml:"controls" json:"controls"` +} + +type ComplianceRiskDTO struct { + ID uuid.UUID `json:"id"` + AssetVersionName string `json:"assetVersionName"` + AssetID string `json:"assetId"` + Artifacts []ArtifactDTO `json:"artifacts,omitempty"` + + PolicyID string `json:"policyId"` + PolicyTitle string `json:"policyTitle"` + PolicyDescription *string `json:"policyDescription"` + PolicyRelatedResources []string `json:"policyRelatedResources"` + PolicyTags []string `json:"policyTags"` + PolicyPriority int `json:"policyPriority"` + PolicyFrameworks []PolicyFrameworks `json:"policyFrameworks"` + + State VulnState `json:"state"` + CreatedAt time.Time `json:"createdAt"` + TicketID *string `json:"ticketId"` + TicketURL *string `json:"ticketUrl"` + ManualTicketCreation bool `json:"manualTicketCreation"` + + Message string `json:"message"` + EvidenceType string `json:"evidenceType"` + Violations []string `json:"violations"` +} + +type DetailedComplianceRiskDTO struct { + ComplianceRiskDTO + Events []VulnEventDTO `json:"events"` +} diff --git a/dtos/devguard_asset_attestation_dto.go b/dtos/devguard_asset_attestation_dto.go new file mode 100644 index 000000000..f29b607d9 --- /dev/null +++ b/dtos/devguard_asset_attestation_dto.go @@ -0,0 +1,34 @@ +package dtos + +import "time" + +// DevguardAssetAttestationPredicateType is the predicate type URI used when storing this attestation in the attestations table. +const DevguardAssetAttestationPredicateType = "https://devguard.org/attestation/asset-metrics/v1" + +// DevguardAssetAttestationDTO represents a security attestation for an asset. +// It captures measurable metrics about how quickly vulnerabilities are being addressed. +type DevguardAssetAttestationDTO struct { + // Metadata + Type string `json:"type"` + GeneratedAt time.Time `json:"generatedAt"` + SchemaVersion string `json:"schemaVersion"` + + // Average time (in hours) to close vulnerabilities, grouped by severity. + // Based on risk score classification. + MeanTimeToRemediate MeanTimeToRemediateDTO `json:"meanTimeToRemediate"` +} + +// MeanTimeToRemediateDTO holds average remediation durations in hours, split by severity. +type MeanTimeToRemediateDTO struct { + // Risk-based severity buckets (DevGuard risk score) + RiskLowAvgHours float64 `json:"riskLowAvgHours"` + RiskMediumAvgHours float64 `json:"riskMediumAvgHours"` + RiskHighAvgHours float64 `json:"riskHighAvgHours"` + RiskCriticalAvgHours float64 `json:"riskCriticalAvgHours"` + + // CVSS-based severity buckets + CVSSLowAvgHours float64 `json:"cvssLowAvgHours"` + CVSSMediumAvgHours float64 `json:"cvsssMediumAvgHours"` + CVSSHighAvgHours float64 `json:"cvssHighAvgHours"` + CVSSCriticalAvgHours float64 `json:"cvssCriticalAvgHours"` +} diff --git a/dtos/devguard_asset_attestation_schema.json b/dtos/devguard_asset_attestation_schema.json new file mode 100644 index 000000000..55df35f50 --- /dev/null +++ b/dtos/devguard_asset_attestation_schema.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://devguard.org/schemas/asset-attestation/v1", + "title": "DevguardAssetAttestation", + "description": "Security attestation for a DevGuard asset. Captures measurable metrics about how quickly vulnerabilities are being addressed.", + "type": "object", + "required": [ + "type", + "generatedAt", + "schemaVersion", + "meanTimeToRemediate" + ], + "properties": { + "type": { + "type": "string", + "description": "Predicate type URI identifying this attestation format.", + "const": "https://devguard.org/attestation/asset-metrics/v1" + }, + "generatedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when this attestation was generated." + }, + "schemaVersion": { + "type": "string", + "description": "Version of the attestation schema (semver).", + "example": "1.0.0" + }, + "meanTimeToRemediate": { + "type": "object", + "description": "Average time in hours to close (fix/accept/false-positive) vulnerabilities, grouped by severity.", + "required": [ + "riskLowAvgHours", + "riskMediumAvgHours", + "riskHighAvgHours", + "riskCriticalAvgHours", + "cvssLowAvgHours", + "cvsssMediumAvgHours", + "cvssHighAvgHours", + "cvssCriticalAvgHours" + ], + "properties": { + "riskLowAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate low-risk vulnerabilities (DevGuard risk score)." + }, + "riskMediumAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate medium-risk vulnerabilities (DevGuard risk score)." + }, + "riskHighAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate high-risk vulnerabilities (DevGuard risk score)." + }, + "riskCriticalAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate critical-risk vulnerabilities (DevGuard risk score)." + }, + "cvssLowAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate low-CVSS vulnerabilities." + }, + "cvssMediumAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate medium-CVSS vulnerabilities." + }, + "cvssHighAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate high-CVSS vulnerabilities." + }, + "cvssCriticalAvgHours": { + "type": "number", + "minimum": 0, + "description": "Average hours to remediate critical-CVSS vulnerabilities." + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/dtos/policy_dto.go b/dtos/policy_dto.go deleted file mode 100644 index 2a67d4981..000000000 --- a/dtos/policy_dto.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2025 l3montree UG (haftungsbeschraenkt) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package dtos - -type PolicyDTO struct { - Title string `json:"title"` - Description string `json:"description"` - Priority int `json:"priority"` - PredicateType string `json:"predicateType"` - Rego string `json:"rego"` -} diff --git a/dtos/vulnevent_dto.go b/dtos/vulnevent_dto.go index 8ed1b7977..890fa79b4 100644 --- a/dtos/vulnevent_dto.go +++ b/dtos/vulnevent_dto.go @@ -14,6 +14,7 @@ const ( VulnTypeDependencyVuln VulnType = "dependencyVuln" VulnTypeFirstPartyVuln VulnType = "firstPartyVuln" VulnTypeLicenseRisk VulnType = "licenseRisk" + VulnTypeComplianceRisk VulnType = "complianceRisk" ) const ( diff --git a/mocks/mock_AttestationRepository.go b/mocks/mock_AttestationRepository.go index 6cd045a27..49cc07340 100644 --- a/mocks/mock_AttestationRepository.go +++ b/mocks/mock_AttestationRepository.go @@ -277,8 +277,8 @@ func (_c *AttestationRepository_CleanupOrphanedRecords_Call) RunAndReturn(run fu } // Create provides a mock function for the type AttestationRepository -func (_mock *AttestationRepository) Create(ctx context.Context, tx shared.DB, t *models.Attestation) error { - ret := _mock.Called(ctx, tx, t) +func (_mock *AttestationRepository) Create(ctx context.Context, tx shared.DB, attestation *models.Attestation) error { + ret := _mock.Called(ctx, tx, attestation) if len(ret) == 0 { panic("no return value specified for Create") @@ -286,7 +286,7 @@ func (_mock *AttestationRepository) Create(ctx context.Context, tx shared.DB, t var r0 error if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.Attestation) error); ok { - r0 = returnFunc(ctx, tx, t) + r0 = returnFunc(ctx, tx, attestation) } else { r0 = ret.Error(0) } @@ -301,12 +301,12 @@ type AttestationRepository_Create_Call struct { // Create is a helper method to define mock.On call // - ctx context.Context // - tx shared.DB -// - t *models.Attestation -func (_e *AttestationRepository_Expecter) Create(ctx interface{}, tx interface{}, t interface{}) *AttestationRepository_Create_Call { - return &AttestationRepository_Create_Call{Call: _e.mock.On("Create", ctx, tx, t)} +// - attestation *models.Attestation +func (_e *AttestationRepository_Expecter) Create(ctx interface{}, tx interface{}, attestation interface{}) *AttestationRepository_Create_Call { + return &AttestationRepository_Create_Call{Call: _e.mock.On("Create", ctx, tx, attestation)} } -func (_c *AttestationRepository_Create_Call) Run(run func(ctx context.Context, tx shared.DB, t *models.Attestation)) *AttestationRepository_Create_Call { +func (_c *AttestationRepository_Create_Call) Run(run func(ctx context.Context, tx shared.DB, attestation *models.Attestation)) *AttestationRepository_Create_Call { _c.Call.Run(func(args mock.Arguments) { var arg0 context.Context if args[0] != nil { @@ -334,7 +334,7 @@ func (_c *AttestationRepository_Create_Call) Return(err error) *AttestationRepos return _c } -func (_c *AttestationRepository_Create_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *models.Attestation) error) *AttestationRepository_Create_Call { +func (_c *AttestationRepository_Create_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, attestation *models.Attestation) error) *AttestationRepository_Create_Call { _c.Call.Return(run) return _c } @@ -528,6 +528,92 @@ func (_c *AttestationRepository_DeleteBatch_Call) RunAndReturn(run func(ctx cont return _c } +// GetByArtifactAndAssetVersionAndAssetID provides a mock function for the type AttestationRepository +func (_mock *AttestationRepository) GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) { + ret := _mock.Called(ctx, tx, artifactName, assetVersion, assetID) + + if len(ret) == 0 { + panic("no return value specified for GetByArtifactAndAssetVersionAndAssetID") + } + + var r0 []models.Attestation + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, string, uuid.UUID) ([]models.Attestation, error)); ok { + return returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, string, uuid.UUID) []models.Attestation); ok { + r0 = returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Attestation) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, string, string, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByArtifactAndAssetVersionAndAssetID' +type AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call struct { + *mock.Call +} + +// GetByArtifactAndAssetVersionAndAssetID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - artifactName string +// - assetVersion string +// - assetID uuid.UUID +func (_e *AttestationRepository_Expecter) GetByArtifactAndAssetVersionAndAssetID(ctx interface{}, tx interface{}, artifactName interface{}, assetVersion interface{}, assetID interface{}) *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call { + return &AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call{Call: _e.mock.On("GetByArtifactAndAssetVersionAndAssetID", ctx, tx, artifactName, assetVersion, assetID)} +} + +func (_c *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call) Run(run func(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID)) *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + var arg4 uuid.UUID + if args[4] != nil { + arg4 = args[4].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call) Return(attestations []models.Attestation, err error) *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Return(attestations, err) + return _c +} + +func (_c *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error)) *AttestationRepository_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Return(run) + return _c +} + // GetByAssetID provides a mock function for the type AttestationRepository func (_mock *AttestationRepository) GetByAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Attestation, error) { ret := _mock.Called(ctx, tx, assetID) diff --git a/mocks/mock_AttestationService.go b/mocks/mock_AttestationService.go new file mode 100644 index 000000000..05b11ed52 --- /dev/null +++ b/mocks/mock_AttestationService.go @@ -0,0 +1,413 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/shared" + mock "github.com/stretchr/testify/mock" +) + +// NewAttestationService creates a new instance of AttestationService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewAttestationService(t interface { + mock.TestingT + Cleanup(func()) +}) *AttestationService { + mock := &AttestationService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// AttestationService is an autogenerated mock type for the AttestationService type +type AttestationService struct { + mock.Mock +} + +type AttestationService_Expecter struct { + mock *mock.Mock +} + +func (_m *AttestationService) EXPECT() *AttestationService_Expecter { + return &AttestationService_Expecter{mock: &_m.Mock} +} + +// Create provides a mock function for the type AttestationService +func (_mock *AttestationService) Create(ctx context.Context, tx shared.DB, attestation *models.Attestation) error { + ret := _mock.Called(ctx, tx, attestation) + + if len(ret) == 0 { + panic("no return value specified for Create") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.Attestation) error); ok { + r0 = returnFunc(ctx, tx, attestation) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// AttestationService_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' +type AttestationService_Create_Call struct { + *mock.Call +} + +// Create is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - attestation *models.Attestation +func (_e *AttestationService_Expecter) Create(ctx interface{}, tx interface{}, attestation interface{}) *AttestationService_Create_Call { + return &AttestationService_Create_Call{Call: _e.mock.On("Create", ctx, tx, attestation)} +} + +func (_c *AttestationService_Create_Call) Run(run func(ctx context.Context, tx shared.DB, attestation *models.Attestation)) *AttestationService_Create_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 *models.Attestation + if args[2] != nil { + arg2 = args[2].(*models.Attestation) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *AttestationService_Create_Call) Return(err error) *AttestationService_Create_Call { + _c.Call.Return(err) + return _c +} + +func (_c *AttestationService_Create_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, attestation *models.Attestation) error) *AttestationService_Create_Call { + _c.Call.Return(run) + return _c +} + +// GenerateAndStoreDevguardAttestation provides a mock function for the type AttestationService +func (_mock *AttestationService) GenerateAndStoreDevguardAttestation(ctx context.Context, assetID uuid.UUID, assetVersionName string, artifactName string) error { + ret := _mock.Called(ctx, assetID, assetVersionName, artifactName) + + if len(ret) == 0 { + panic("no return value specified for GenerateAndStoreDevguardAttestation") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, string, string) error); ok { + r0 = returnFunc(ctx, assetID, assetVersionName, artifactName) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// AttestationService_GenerateAndStoreDevguardAttestation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateAndStoreDevguardAttestation' +type AttestationService_GenerateAndStoreDevguardAttestation_Call struct { + *mock.Call +} + +// GenerateAndStoreDevguardAttestation is a helper method to define mock.On call +// - ctx context.Context +// - assetID uuid.UUID +// - assetVersionName string +// - artifactName string +func (_e *AttestationService_Expecter) GenerateAndStoreDevguardAttestation(ctx interface{}, assetID interface{}, assetVersionName interface{}, artifactName interface{}) *AttestationService_GenerateAndStoreDevguardAttestation_Call { + return &AttestationService_GenerateAndStoreDevguardAttestation_Call{Call: _e.mock.On("GenerateAndStoreDevguardAttestation", ctx, assetID, assetVersionName, artifactName)} +} + +func (_c *AttestationService_GenerateAndStoreDevguardAttestation_Call) Run(run func(ctx context.Context, assetID uuid.UUID, assetVersionName string, artifactName string)) *AttestationService_GenerateAndStoreDevguardAttestation_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *AttestationService_GenerateAndStoreDevguardAttestation_Call) Return(err error) *AttestationService_GenerateAndStoreDevguardAttestation_Call { + _c.Call.Return(err) + return _c +} + +func (_c *AttestationService_GenerateAndStoreDevguardAttestation_Call) RunAndReturn(run func(ctx context.Context, assetID uuid.UUID, assetVersionName string, artifactName string) error) *AttestationService_GenerateAndStoreDevguardAttestation_Call { + _c.Call.Return(run) + return _c +} + +// GetByArtifactAndAssetVersionAndAssetID provides a mock function for the type AttestationService +func (_mock *AttestationService) GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) { + ret := _mock.Called(ctx, tx, artifactName, assetVersion, assetID) + + if len(ret) == 0 { + panic("no return value specified for GetByArtifactAndAssetVersionAndAssetID") + } + + var r0 []models.Attestation + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, string, uuid.UUID) ([]models.Attestation, error)); ok { + return returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, string, uuid.UUID) []models.Attestation); ok { + r0 = returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Attestation) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, string, string, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, artifactName, assetVersion, assetID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByArtifactAndAssetVersionAndAssetID' +type AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call struct { + *mock.Call +} + +// GetByArtifactAndAssetVersionAndAssetID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - artifactName string +// - assetVersion string +// - assetID uuid.UUID +func (_e *AttestationService_Expecter) GetByArtifactAndAssetVersionAndAssetID(ctx interface{}, tx interface{}, artifactName interface{}, assetVersion interface{}, assetID interface{}) *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call { + return &AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call{Call: _e.mock.On("GetByArtifactAndAssetVersionAndAssetID", ctx, tx, artifactName, assetVersion, assetID)} +} + +func (_c *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call) Run(run func(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID)) *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + var arg4 uuid.UUID + if args[4] != nil { + arg4 = args[4].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call) Return(attestations []models.Attestation, err error) *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Return(attestations, err) + return _c +} + +func (_c *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error)) *AttestationService_GetByArtifactAndAssetVersionAndAssetID_Call { + _c.Call.Return(run) + return _c +} + +// GetByAssetID provides a mock function for the type AttestationService +func (_mock *AttestationService) GetByAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Attestation, error) { + ret := _mock.Called(ctx, tx, assetID) + + if len(ret) == 0 { + panic("no return value specified for GetByAssetID") + } + + var r0 []models.Attestation + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) ([]models.Attestation, error)); ok { + return returnFunc(ctx, tx, assetID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) []models.Attestation); ok { + r0 = returnFunc(ctx, tx, assetID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Attestation) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, assetID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AttestationService_GetByAssetID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByAssetID' +type AttestationService_GetByAssetID_Call struct { + *mock.Call +} + +// GetByAssetID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +func (_e *AttestationService_Expecter) GetByAssetID(ctx interface{}, tx interface{}, assetID interface{}) *AttestationService_GetByAssetID_Call { + return &AttestationService_GetByAssetID_Call{Call: _e.mock.On("GetByAssetID", ctx, tx, assetID)} +} + +func (_c *AttestationService_GetByAssetID_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID)) *AttestationService_GetByAssetID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *AttestationService_GetByAssetID_Call) Return(attestations []models.Attestation, err error) *AttestationService_GetByAssetID_Call { + _c.Call.Return(attestations, err) + return _c +} + +func (_c *AttestationService_GetByAssetID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Attestation, error)) *AttestationService_GetByAssetID_Call { + _c.Call.Return(run) + return _c +} + +// GetByAssetVersionAndAssetID provides a mock function for the type AttestationService +func (_mock *AttestationService) GetByAssetVersionAndAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersion string) ([]models.Attestation, error) { + ret := _mock.Called(ctx, tx, assetID, assetVersion) + + if len(ret) == 0 { + panic("no return value specified for GetByAssetVersionAndAssetID") + } + + var r0 []models.Attestation + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) ([]models.Attestation, error)); ok { + return returnFunc(ctx, tx, assetID, assetVersion) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) []models.Attestation); ok { + r0 = returnFunc(ctx, tx, assetID, assetVersion) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.Attestation) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, assetID, assetVersion) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// AttestationService_GetByAssetVersionAndAssetID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByAssetVersionAndAssetID' +type AttestationService_GetByAssetVersionAndAssetID_Call struct { + *mock.Call +} + +// GetByAssetVersionAndAssetID is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +// - assetVersion string +func (_e *AttestationService_Expecter) GetByAssetVersionAndAssetID(ctx interface{}, tx interface{}, assetID interface{}, assetVersion interface{}) *AttestationService_GetByAssetVersionAndAssetID_Call { + return &AttestationService_GetByAssetVersionAndAssetID_Call{Call: _e.mock.On("GetByAssetVersionAndAssetID", ctx, tx, assetID, assetVersion)} +} + +func (_c *AttestationService_GetByAssetVersionAndAssetID_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersion string)) *AttestationService_GetByAssetVersionAndAssetID_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *AttestationService_GetByAssetVersionAndAssetID_Call) Return(attestations []models.Attestation, err error) *AttestationService_GetByAssetVersionAndAssetID_Call { + _c.Call.Return(attestations, err) + return _c +} + +func (_c *AttestationService_GetByAssetVersionAndAssetID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersion string) ([]models.Attestation, error)) *AttestationService_GetByAssetVersionAndAssetID_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/mock_ComplianceRiskRepository.go b/mocks/mock_ComplianceRiskRepository.go new file mode 100644 index 000000000..2c96a1284 --- /dev/null +++ b/mocks/mock_ComplianceRiskRepository.go @@ -0,0 +1,1466 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/shared" + mock "github.com/stretchr/testify/mock" + "gorm.io/gorm/clause" +) + +// NewComplianceRiskRepository creates a new instance of ComplianceRiskRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewComplianceRiskRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *ComplianceRiskRepository { + mock := &ComplianceRiskRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ComplianceRiskRepository is an autogenerated mock type for the ComplianceRiskRepository type +type ComplianceRiskRepository struct { + mock.Mock +} + +type ComplianceRiskRepository_Expecter struct { + mock *mock.Mock +} + +func (_m *ComplianceRiskRepository) EXPECT() *ComplianceRiskRepository_Expecter { + return &ComplianceRiskRepository_Expecter{mock: &_m.Mock} +} + +// Activate provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Activate(ctx context.Context, tx shared.DB, id uuid.UUID) error { + ret := _mock.Called(ctx, tx, id) + + if len(ret) == 0 { + panic("no return value specified for Activate") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) error); ok { + r0 = returnFunc(ctx, tx, id) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Activate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Activate' +type ComplianceRiskRepository_Activate_Call struct { + *mock.Call +} + +// Activate is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - id uuid.UUID +func (_e *ComplianceRiskRepository_Expecter) Activate(ctx interface{}, tx interface{}, id interface{}) *ComplianceRiskRepository_Activate_Call { + return &ComplianceRiskRepository_Activate_Call{Call: _e.mock.On("Activate", ctx, tx, id)} +} + +func (_c *ComplianceRiskRepository_Activate_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *ComplianceRiskRepository_Activate_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Activate_Call) Return(err error) *ComplianceRiskRepository_Activate_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Activate_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) error) *ComplianceRiskRepository_Activate_Call { + _c.Call.Return(run) + return _c +} + +// All provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) All(ctx context.Context, tx shared.DB) ([]models.ComplianceRisk, error) { + ret := _mock.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for All") + } + + var r0 []models.ComplianceRisk + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) ([]models.ComplianceRisk, error)); ok { + return returnFunc(ctx, tx) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) []models.ComplianceRisk); ok { + r0 = returnFunc(ctx, tx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.ComplianceRisk) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB) error); ok { + r1 = returnFunc(ctx, tx) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_All_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'All' +type ComplianceRiskRepository_All_Call struct { + *mock.Call +} + +// All is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +func (_e *ComplianceRiskRepository_Expecter) All(ctx interface{}, tx interface{}) *ComplianceRiskRepository_All_Call { + return &ComplianceRiskRepository_All_Call{Call: _e.mock.On("All", ctx, tx)} +} + +func (_c *ComplianceRiskRepository_All_Call) Run(run func(ctx context.Context, tx shared.DB)) *ComplianceRiskRepository_All_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_All_Call) Return(complianceRisks []models.ComplianceRisk, err error) *ComplianceRiskRepository_All_Call { + _c.Call.Return(complianceRisks, err) + return _c +} + +func (_c *ComplianceRiskRepository_All_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB) ([]models.ComplianceRisk, error)) *ComplianceRiskRepository_All_Call { + _c.Call.Return(run) + return _c +} + +// ApplyAndSave provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) ApplyAndSave(ctx context.Context, tx shared.DB, risk *models.ComplianceRisk, ev *models.VulnEvent) error { + ret := _mock.Called(ctx, tx, risk, ev) + + if len(ret) == 0 { + panic("no return value specified for ApplyAndSave") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.ComplianceRisk, *models.VulnEvent) error); ok { + r0 = returnFunc(ctx, tx, risk, ev) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_ApplyAndSave_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyAndSave' +type ComplianceRiskRepository_ApplyAndSave_Call struct { + *mock.Call +} + +// ApplyAndSave is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - risk *models.ComplianceRisk +// - ev *models.VulnEvent +func (_e *ComplianceRiskRepository_Expecter) ApplyAndSave(ctx interface{}, tx interface{}, risk interface{}, ev interface{}) *ComplianceRiskRepository_ApplyAndSave_Call { + return &ComplianceRiskRepository_ApplyAndSave_Call{Call: _e.mock.On("ApplyAndSave", ctx, tx, risk, ev)} +} + +func (_c *ComplianceRiskRepository_ApplyAndSave_Call) Run(run func(ctx context.Context, tx shared.DB, risk *models.ComplianceRisk, ev *models.VulnEvent)) *ComplianceRiskRepository_ApplyAndSave_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 *models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].(*models.ComplianceRisk) + } + var arg3 *models.VulnEvent + if args[3] != nil { + arg3 = args[3].(*models.VulnEvent) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_ApplyAndSave_Call) Return(err error) *ComplianceRiskRepository_ApplyAndSave_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_ApplyAndSave_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, risk *models.ComplianceRisk, ev *models.VulnEvent) error) *ComplianceRiskRepository_ApplyAndSave_Call { + _c.Call.Return(run) + return _c +} + +// Begin provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Begin(ctx context.Context) shared.DB { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Begin") + } + + var r0 shared.DB + if returnFunc, ok := ret.Get(0).(func(context.Context) shared.DB); ok { + r0 = returnFunc(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shared.DB) + } + } + return r0 +} + +// ComplianceRiskRepository_Begin_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Begin' +type ComplianceRiskRepository_Begin_Call struct { + *mock.Call +} + +// Begin is a helper method to define mock.On call +// - ctx context.Context +func (_e *ComplianceRiskRepository_Expecter) Begin(ctx interface{}) *ComplianceRiskRepository_Begin_Call { + return &ComplianceRiskRepository_Begin_Call{Call: _e.mock.On("Begin", ctx)} +} + +func (_c *ComplianceRiskRepository_Begin_Call) Run(run func(ctx context.Context)) *ComplianceRiskRepository_Begin_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Begin_Call) Return(v shared.DB) *ComplianceRiskRepository_Begin_Call { + _c.Call.Return(v) + return _c +} + +func (_c *ComplianceRiskRepository_Begin_Call) RunAndReturn(run func(ctx context.Context) shared.DB) *ComplianceRiskRepository_Begin_Call { + _c.Call.Return(run) + return _c +} + +// CleanupOrphanedRecords provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) CleanupOrphanedRecords(ctx context.Context) error { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for CleanupOrphanedRecords") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(ctx) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_CleanupOrphanedRecords_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanupOrphanedRecords' +type ComplianceRiskRepository_CleanupOrphanedRecords_Call struct { + *mock.Call +} + +// CleanupOrphanedRecords is a helper method to define mock.On call +// - ctx context.Context +func (_e *ComplianceRiskRepository_Expecter) CleanupOrphanedRecords(ctx interface{}) *ComplianceRiskRepository_CleanupOrphanedRecords_Call { + return &ComplianceRiskRepository_CleanupOrphanedRecords_Call{Call: _e.mock.On("CleanupOrphanedRecords", ctx)} +} + +func (_c *ComplianceRiskRepository_CleanupOrphanedRecords_Call) Run(run func(ctx context.Context)) *ComplianceRiskRepository_CleanupOrphanedRecords_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_CleanupOrphanedRecords_Call) Return(err error) *ComplianceRiskRepository_CleanupOrphanedRecords_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_CleanupOrphanedRecords_Call) RunAndReturn(run func(ctx context.Context) error) *ComplianceRiskRepository_CleanupOrphanedRecords_Call { + _c.Call.Return(run) + return _c +} + +// Create provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Create(ctx context.Context, tx shared.DB, t *models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, t) + + if len(ret) == 0 { + panic("no return value specified for Create") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, t) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' +type ComplianceRiskRepository_Create_Call struct { + *mock.Call +} + +// Create is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - t *models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) Create(ctx interface{}, tx interface{}, t interface{}) *ComplianceRiskRepository_Create_Call { + return &ComplianceRiskRepository_Create_Call{Call: _e.mock.On("Create", ctx, tx, t)} +} + +func (_c *ComplianceRiskRepository_Create_Call) Run(run func(ctx context.Context, tx shared.DB, t *models.ComplianceRisk)) *ComplianceRiskRepository_Create_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 *models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].(*models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Create_Call) Return(err error) *ComplianceRiskRepository_Create_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Create_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *models.ComplianceRisk) error) *ComplianceRiskRepository_Create_Call { + _c.Call.Return(run) + return _c +} + +// CreateBatch provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) CreateBatch(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, ts) + + if len(ret) == 0 { + panic("no return value specified for CreateBatch") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, ts) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_CreateBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateBatch' +type ComplianceRiskRepository_CreateBatch_Call struct { + *mock.Call +} + +// CreateBatch is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - ts []models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) CreateBatch(ctx interface{}, tx interface{}, ts interface{}) *ComplianceRiskRepository_CreateBatch_Call { + return &ComplianceRiskRepository_CreateBatch_Call{Call: _e.mock.On("CreateBatch", ctx, tx, ts)} +} + +func (_c *ComplianceRiskRepository_CreateBatch_Call) Run(run func(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk)) *ComplianceRiskRepository_CreateBatch_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].([]models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_CreateBatch_Call) Return(err error) *ComplianceRiskRepository_CreateBatch_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_CreateBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk) error) *ComplianceRiskRepository_CreateBatch_Call { + _c.Call.Return(run) + return _c +} + +// Delete provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Delete(ctx context.Context, tx shared.DB, id uuid.UUID) error { + ret := _mock.Called(ctx, tx, id) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) error); ok { + r0 = returnFunc(ctx, tx, id) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type ComplianceRiskRepository_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - id uuid.UUID +func (_e *ComplianceRiskRepository_Expecter) Delete(ctx interface{}, tx interface{}, id interface{}) *ComplianceRiskRepository_Delete_Call { + return &ComplianceRiskRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, tx, id)} +} + +func (_c *ComplianceRiskRepository_Delete_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *ComplianceRiskRepository_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Delete_Call) Return(err error) *ComplianceRiskRepository_Delete_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) error) *ComplianceRiskRepository_Delete_Call { + _c.Call.Return(run) + return _c +} + +// DeleteBatch provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) DeleteBatch(ctx context.Context, tx shared.DB, ids []models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, ids) + + if len(ret) == 0 { + panic("no return value specified for DeleteBatch") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, ids) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_DeleteBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteBatch' +type ComplianceRiskRepository_DeleteBatch_Call struct { + *mock.Call +} + +// DeleteBatch is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - ids []models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) DeleteBatch(ctx interface{}, tx interface{}, ids interface{}) *ComplianceRiskRepository_DeleteBatch_Call { + return &ComplianceRiskRepository_DeleteBatch_Call{Call: _e.mock.On("DeleteBatch", ctx, tx, ids)} +} + +func (_c *ComplianceRiskRepository_DeleteBatch_Call) Run(run func(ctx context.Context, tx shared.DB, ids []models.ComplianceRisk)) *ComplianceRiskRepository_DeleteBatch_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].([]models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_DeleteBatch_Call) Return(err error) *ComplianceRiskRepository_DeleteBatch_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_DeleteBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ids []models.ComplianceRisk) error) *ComplianceRiskRepository_DeleteBatch_Call { + _c.Call.Return(run) + return _c +} + +// GetAllComplianceRisksForAssetVersion provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) GetAllComplianceRisksForAssetVersion(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string) ([]models.ComplianceRisk, error) { + ret := _mock.Called(ctx, tx, assetID, assetVersionName) + + if len(ret) == 0 { + panic("no return value specified for GetAllComplianceRisksForAssetVersion") + } + + var r0 []models.ComplianceRisk + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) ([]models.ComplianceRisk, error)); ok { + return returnFunc(ctx, tx, assetID, assetVersionName) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) []models.ComplianceRisk); ok { + r0 = returnFunc(ctx, tx, assetID, assetVersionName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.ComplianceRisk) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, assetID, assetVersionName) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllComplianceRisksForAssetVersion' +type ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call struct { + *mock.Call +} + +// GetAllComplianceRisksForAssetVersion is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +// - assetVersionName string +func (_e *ComplianceRiskRepository_Expecter) GetAllComplianceRisksForAssetVersion(ctx interface{}, tx interface{}, assetID interface{}, assetVersionName interface{}) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call { + return &ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call{Call: _e.mock.On("GetAllComplianceRisksForAssetVersion", ctx, tx, assetID, assetVersionName)} +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string)) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call) Return(complianceRisks []models.ComplianceRisk, err error) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call { + _c.Call.Return(complianceRisks, err) + return _c +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string) ([]models.ComplianceRisk, error)) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersion_Call { + _c.Call.Return(run) + return _c +} + +// GetAllComplianceRisksForAssetVersionPaged provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) GetAllComplianceRisksForAssetVersionPaged(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string, pageInfo shared.PageInfo, search string, filter []shared.FilterQuery, sort []shared.SortQuery) (shared.Paged[models.ComplianceRisk], error) { + ret := _mock.Called(ctx, tx, assetID, assetVersionName, pageInfo, search, filter, sort) + + if len(ret) == 0 { + panic("no return value specified for GetAllComplianceRisksForAssetVersionPaged") + } + + var r0 shared.Paged[models.ComplianceRisk] + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string, shared.PageInfo, string, []shared.FilterQuery, []shared.SortQuery) (shared.Paged[models.ComplianceRisk], error)); ok { + return returnFunc(ctx, tx, assetID, assetVersionName, pageInfo, search, filter, sort) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string, shared.PageInfo, string, []shared.FilterQuery, []shared.SortQuery) shared.Paged[models.ComplianceRisk]); ok { + r0 = returnFunc(ctx, tx, assetID, assetVersionName, pageInfo, search, filter, sort) + } else { + r0 = ret.Get(0).(shared.Paged[models.ComplianceRisk]) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string, shared.PageInfo, string, []shared.FilterQuery, []shared.SortQuery) error); ok { + r1 = returnFunc(ctx, tx, assetID, assetVersionName, pageInfo, search, filter, sort) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllComplianceRisksForAssetVersionPaged' +type ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call struct { + *mock.Call +} + +// GetAllComplianceRisksForAssetVersionPaged is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +// - assetVersionName string +// - pageInfo shared.PageInfo +// - search string +// - filter []shared.FilterQuery +// - sort []shared.SortQuery +func (_e *ComplianceRiskRepository_Expecter) GetAllComplianceRisksForAssetVersionPaged(ctx interface{}, tx interface{}, assetID interface{}, assetVersionName interface{}, pageInfo interface{}, search interface{}, filter interface{}, sort interface{}) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call { + return &ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call{Call: _e.mock.On("GetAllComplianceRisksForAssetVersionPaged", ctx, tx, assetID, assetVersionName, pageInfo, search, filter, sort)} +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string, pageInfo shared.PageInfo, search string, filter []shared.FilterQuery, sort []shared.SortQuery)) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + var arg4 shared.PageInfo + if args[4] != nil { + arg4 = args[4].(shared.PageInfo) + } + var arg5 string + if args[5] != nil { + arg5 = args[5].(string) + } + var arg6 []shared.FilterQuery + if args[6] != nil { + arg6 = args[6].([]shared.FilterQuery) + } + var arg7 []shared.SortQuery + if args[7] != nil { + arg7 = args[7].([]shared.SortQuery) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call) Return(paged shared.Paged[models.ComplianceRisk], err error) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call { + _c.Call.Return(paged, err) + return _c +} + +func (_c *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string, pageInfo shared.PageInfo, search string, filter []shared.FilterQuery, sort []shared.SortQuery) (shared.Paged[models.ComplianceRisk], error)) *ComplianceRiskRepository_GetAllComplianceRisksForAssetVersionPaged_Call { + _c.Call.Return(run) + return _c +} + +// GetComplianceRisksByOtherAssetVersions provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) GetComplianceRisksByOtherAssetVersions(ctx context.Context, tx shared.DB, assetVersionName string, assetID uuid.UUID) ([]models.ComplianceRisk, error) { + ret := _mock.Called(ctx, tx, assetVersionName, assetID) + + if len(ret) == 0 { + panic("no return value specified for GetComplianceRisksByOtherAssetVersions") + } + + var r0 []models.ComplianceRisk + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, uuid.UUID) ([]models.ComplianceRisk, error)); ok { + return returnFunc(ctx, tx, assetVersionName, assetID) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, uuid.UUID) []models.ComplianceRisk); ok { + r0 = returnFunc(ctx, tx, assetVersionName, assetID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.ComplianceRisk) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, string, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, assetVersionName, assetID) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetComplianceRisksByOtherAssetVersions' +type ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call struct { + *mock.Call +} + +// GetComplianceRisksByOtherAssetVersions is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetVersionName string +// - assetID uuid.UUID +func (_e *ComplianceRiskRepository_Expecter) GetComplianceRisksByOtherAssetVersions(ctx interface{}, tx interface{}, assetVersionName interface{}, assetID interface{}) *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call { + return &ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call{Call: _e.mock.On("GetComplianceRisksByOtherAssetVersions", ctx, tx, assetVersionName, assetID)} +} + +func (_c *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call) Run(run func(ctx context.Context, tx shared.DB, assetVersionName string, assetID uuid.UUID)) *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 uuid.UUID + if args[3] != nil { + arg3 = args[3].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call) Return(complianceRisks []models.ComplianceRisk, err error) *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call { + _c.Call.Return(complianceRisks, err) + return _c +} + +func (_c *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetVersionName string, assetID uuid.UUID) ([]models.ComplianceRisk, error)) *ComplianceRiskRepository_GetComplianceRisksByOtherAssetVersions_Call { + _c.Call.Return(run) + return _c +} + +// GetDB provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) GetDB(ctx context.Context, tx shared.DB) shared.DB { + ret := _mock.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for GetDB") + } + + var r0 shared.DB + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) shared.DB); ok { + r0 = returnFunc(ctx, tx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shared.DB) + } + } + return r0 +} + +// ComplianceRiskRepository_GetDB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDB' +type ComplianceRiskRepository_GetDB_Call struct { + *mock.Call +} + +// GetDB is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +func (_e *ComplianceRiskRepository_Expecter) GetDB(ctx interface{}, tx interface{}) *ComplianceRiskRepository_GetDB_Call { + return &ComplianceRiskRepository_GetDB_Call{Call: _e.mock.On("GetDB", ctx, tx)} +} + +func (_c *ComplianceRiskRepository_GetDB_Call) Run(run func(ctx context.Context, tx shared.DB)) *ComplianceRiskRepository_GetDB_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_GetDB_Call) Return(v shared.DB) *ComplianceRiskRepository_GetDB_Call { + _c.Call.Return(v) + return _c +} + +func (_c *ComplianceRiskRepository_GetDB_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB) shared.DB) *ComplianceRiskRepository_GetDB_Call { + _c.Call.Return(run) + return _c +} + +// GetDistinctFrameworksForAssetVersion provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) GetDistinctFrameworksForAssetVersion(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string) ([]string, error) { + ret := _mock.Called(ctx, tx, assetID, assetVersionName) + + if len(ret) == 0 { + panic("no return value specified for GetDistinctFrameworksForAssetVersion") + } + + var r0 []string + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) ([]string, error)); ok { + return returnFunc(ctx, tx, assetID, assetVersionName) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, string) []string); ok { + r0 = returnFunc(ctx, tx, assetID, assetVersionName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID, string) error); ok { + r1 = returnFunc(ctx, tx, assetID, assetVersionName) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDistinctFrameworksForAssetVersion' +type ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call struct { + *mock.Call +} + +// GetDistinctFrameworksForAssetVersion is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - assetID uuid.UUID +// - assetVersionName string +func (_e *ComplianceRiskRepository_Expecter) GetDistinctFrameworksForAssetVersion(ctx interface{}, tx interface{}, assetID interface{}, assetVersionName interface{}) *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call { + return &ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call{Call: _e.mock.On("GetDistinctFrameworksForAssetVersion", ctx, tx, assetID, assetVersionName)} +} + +func (_c *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call) Run(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string)) *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + var arg3 string + if args[3] != nil { + arg3 = args[3].(string) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call) Return(strings []string, err error) *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call { + _c.Call.Return(strings, err) + return _c +} + +func (_c *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersionName string) ([]string, error)) *ComplianceRiskRepository_GetDistinctFrameworksForAssetVersion_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) List(ctx context.Context, tx shared.DB, ids []uuid.UUID) ([]models.ComplianceRisk, error) { + ret := _mock.Called(ctx, tx, ids) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 []models.ComplianceRisk + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) ([]models.ComplianceRisk, error)); ok { + return returnFunc(ctx, tx, ids) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) []models.ComplianceRisk); ok { + r0 = returnFunc(ctx, tx, ids) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]models.ComplianceRisk) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, ids) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type ComplianceRiskRepository_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - ids []uuid.UUID +func (_e *ComplianceRiskRepository_Expecter) List(ctx interface{}, tx interface{}, ids interface{}) *ComplianceRiskRepository_List_Call { + return &ComplianceRiskRepository_List_Call{Call: _e.mock.On("List", ctx, tx, ids)} +} + +func (_c *ComplianceRiskRepository_List_Call) Run(run func(ctx context.Context, tx shared.DB, ids []uuid.UUID)) *ComplianceRiskRepository_List_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []uuid.UUID + if args[2] != nil { + arg2 = args[2].([]uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_List_Call) Return(complianceRisks []models.ComplianceRisk, err error) *ComplianceRiskRepository_List_Call { + _c.Call.Return(complianceRisks, err) + return _c +} + +func (_c *ComplianceRiskRepository_List_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ids []uuid.UUID) ([]models.ComplianceRisk, error)) *ComplianceRiskRepository_List_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Read(ctx context.Context, tx shared.DB, id uuid.UUID) (models.ComplianceRisk, error) { + ret := _mock.Called(ctx, tx, id) + + if len(ret) == 0 { + panic("no return value specified for Read") + } + + var r0 models.ComplianceRisk + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) (models.ComplianceRisk, error)); ok { + return returnFunc(ctx, tx, id) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) models.ComplianceRisk); ok { + r0 = returnFunc(ctx, tx, id) + } else { + r0 = ret.Get(0).(models.ComplianceRisk) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { + r1 = returnFunc(ctx, tx, id) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskRepository_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type ComplianceRiskRepository_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - id uuid.UUID +func (_e *ComplianceRiskRepository_Expecter) Read(ctx interface{}, tx interface{}, id interface{}) *ComplianceRiskRepository_Read_Call { + return &ComplianceRiskRepository_Read_Call{Call: _e.mock.On("Read", ctx, tx, id)} +} + +func (_c *ComplianceRiskRepository_Read_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *ComplianceRiskRepository_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 uuid.UUID + if args[2] != nil { + arg2 = args[2].(uuid.UUID) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Read_Call) Return(complianceRisk models.ComplianceRisk, err error) *ComplianceRiskRepository_Read_Call { + _c.Call.Return(complianceRisk, err) + return _c +} + +func (_c *ComplianceRiskRepository_Read_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) (models.ComplianceRisk, error)) *ComplianceRiskRepository_Read_Call { + _c.Call.Return(run) + return _c +} + +// Save provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Save(ctx context.Context, tx shared.DB, t *models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, t) + + if len(ret) == 0 { + panic("no return value specified for Save") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, t) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' +type ComplianceRiskRepository_Save_Call struct { + *mock.Call +} + +// Save is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - t *models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) Save(ctx interface{}, tx interface{}, t interface{}) *ComplianceRiskRepository_Save_Call { + return &ComplianceRiskRepository_Save_Call{Call: _e.mock.On("Save", ctx, tx, t)} +} + +func (_c *ComplianceRiskRepository_Save_Call) Run(run func(ctx context.Context, tx shared.DB, t *models.ComplianceRisk)) *ComplianceRiskRepository_Save_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 *models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].(*models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Save_Call) Return(err error) *ComplianceRiskRepository_Save_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Save_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *models.ComplianceRisk) error) *ComplianceRiskRepository_Save_Call { + _c.Call.Return(run) + return _c +} + +// SaveBatch provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) SaveBatch(ctx context.Context, tx shared.DB, risks []models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, risks) + + if len(ret) == 0 { + panic("no return value specified for SaveBatch") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, risks) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_SaveBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveBatch' +type ComplianceRiskRepository_SaveBatch_Call struct { + *mock.Call +} + +// SaveBatch is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - risks []models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) SaveBatch(ctx interface{}, tx interface{}, risks interface{}) *ComplianceRiskRepository_SaveBatch_Call { + return &ComplianceRiskRepository_SaveBatch_Call{Call: _e.mock.On("SaveBatch", ctx, tx, risks)} +} + +func (_c *ComplianceRiskRepository_SaveBatch_Call) Run(run func(ctx context.Context, tx shared.DB, risks []models.ComplianceRisk)) *ComplianceRiskRepository_SaveBatch_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].([]models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_SaveBatch_Call) Return(err error) *ComplianceRiskRepository_SaveBatch_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_SaveBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, risks []models.ComplianceRisk) error) *ComplianceRiskRepository_SaveBatch_Call { + _c.Call.Return(run) + return _c +} + +// SaveBatchBestEffort provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) SaveBatchBestEffort(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk) error { + ret := _mock.Called(ctx, tx, ts) + + if len(ret) == 0 { + panic("no return value specified for SaveBatchBestEffort") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.ComplianceRisk) error); ok { + r0 = returnFunc(ctx, tx, ts) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_SaveBatchBestEffort_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveBatchBestEffort' +type ComplianceRiskRepository_SaveBatchBestEffort_Call struct { + *mock.Call +} + +// SaveBatchBestEffort is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - ts []models.ComplianceRisk +func (_e *ComplianceRiskRepository_Expecter) SaveBatchBestEffort(ctx interface{}, tx interface{}, ts interface{}) *ComplianceRiskRepository_SaveBatchBestEffort_Call { + return &ComplianceRiskRepository_SaveBatchBestEffort_Call{Call: _e.mock.On("SaveBatchBestEffort", ctx, tx, ts)} +} + +func (_c *ComplianceRiskRepository_SaveBatchBestEffort_Call) Run(run func(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk)) *ComplianceRiskRepository_SaveBatchBestEffort_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 []models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].([]models.ComplianceRisk) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_SaveBatchBestEffort_Call) Return(err error) *ComplianceRiskRepository_SaveBatchBestEffort_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_SaveBatchBestEffort_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ts []models.ComplianceRisk) error) *ComplianceRiskRepository_SaveBatchBestEffort_Call { + _c.Call.Return(run) + return _c +} + +// Transaction provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Transaction(ctx context.Context, fn func(tx shared.DB) error) error { + ret := _mock.Called(ctx, fn) + + if len(ret) == 0 { + panic("no return value specified for Transaction") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, func(tx shared.DB) error) error); ok { + r0 = returnFunc(ctx, fn) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Transaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Transaction' +type ComplianceRiskRepository_Transaction_Call struct { + *mock.Call +} + +// Transaction is a helper method to define mock.On call +// - ctx context.Context +// - fn func(tx shared.DB) error +func (_e *ComplianceRiskRepository_Expecter) Transaction(ctx interface{}, fn interface{}) *ComplianceRiskRepository_Transaction_Call { + return &ComplianceRiskRepository_Transaction_Call{Call: _e.mock.On("Transaction", ctx, fn)} +} + +func (_c *ComplianceRiskRepository_Transaction_Call) Run(run func(ctx context.Context, fn func(tx shared.DB) error)) *ComplianceRiskRepository_Transaction_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 func(tx shared.DB) error + if args[1] != nil { + arg1 = args[1].(func(tx shared.DB) error) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Transaction_Call) Return(err error) *ComplianceRiskRepository_Transaction_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Transaction_Call) RunAndReturn(run func(ctx context.Context, fn func(tx shared.DB) error) error) *ComplianceRiskRepository_Transaction_Call { + _c.Call.Return(run) + return _c +} + +// Upsert provides a mock function for the type ComplianceRiskRepository +func (_mock *ComplianceRiskRepository) Upsert(ctx context.Context, tx shared.DB, t *[]*models.ComplianceRisk, conflictingColumns []clause.Column, updateOnly []string) error { + ret := _mock.Called(ctx, tx, t, conflictingColumns, updateOnly) + + if len(ret) == 0 { + panic("no return value specified for Upsert") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *[]*models.ComplianceRisk, []clause.Column, []string) error); ok { + r0 = returnFunc(ctx, tx, t, conflictingColumns, updateOnly) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskRepository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' +type ComplianceRiskRepository_Upsert_Call struct { + *mock.Call +} + +// Upsert is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - t *[]*models.ComplianceRisk +// - conflictingColumns []clause.Column +// - updateOnly []string +func (_e *ComplianceRiskRepository_Expecter) Upsert(ctx interface{}, tx interface{}, t interface{}, conflictingColumns interface{}, updateOnly interface{}) *ComplianceRiskRepository_Upsert_Call { + return &ComplianceRiskRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, tx, t, conflictingColumns, updateOnly)} +} + +func (_c *ComplianceRiskRepository_Upsert_Call) Run(run func(ctx context.Context, tx shared.DB, t *[]*models.ComplianceRisk, conflictingColumns []clause.Column, updateOnly []string)) *ComplianceRiskRepository_Upsert_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 *[]*models.ComplianceRisk + if args[2] != nil { + arg2 = args[2].(*[]*models.ComplianceRisk) + } + var arg3 []clause.Column + if args[3] != nil { + arg3 = args[3].([]clause.Column) + } + var arg4 []string + if args[4] != nil { + arg4 = args[4].([]string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + ) + }) + return _c +} + +func (_c *ComplianceRiskRepository_Upsert_Call) Return(err error) *ComplianceRiskRepository_Upsert_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *[]*models.ComplianceRisk, conflictingColumns []clause.Column, updateOnly []string) error) *ComplianceRiskRepository_Upsert_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/mock_ComplianceRiskService.go b/mocks/mock_ComplianceRiskService.go new file mode 100644 index 000000000..8080ba3d0 --- /dev/null +++ b/mocks/mock_ComplianceRiskService.go @@ -0,0 +1,231 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/dtos/sarif" + "github.com/l3montree-dev/devguard/shared" + mock "github.com/stretchr/testify/mock" +) + +// NewComplianceRiskService creates a new instance of ComplianceRiskService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewComplianceRiskService(t interface { + mock.TestingT + Cleanup(func()) +}) *ComplianceRiskService { + mock := &ComplianceRiskService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ComplianceRiskService is an autogenerated mock type for the ComplianceRiskService type +type ComplianceRiskService struct { + mock.Mock +} + +type ComplianceRiskService_Expecter struct { + mock *mock.Mock +} + +func (_m *ComplianceRiskService) EXPECT() *ComplianceRiskService_Expecter { + return &ComplianceRiskService_Expecter{mock: &_m.Mock} +} + +// HandleArtifactCompliance provides a mock function for the type ComplianceRiskService +func (_mock *ComplianceRiskService) HandleArtifactCompliance(ctx context.Context, tx shared.DB, userID string, userAgent *string, assetVersion models.AssetVersion, artifact models.Artifact, sarifDoc sarif.SarifSchema210Json) error { + ret := _mock.Called(ctx, tx, userID, userAgent, assetVersion, artifact, sarifDoc) + + if len(ret) == 0 { + panic("no return value specified for HandleArtifactCompliance") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, *string, models.AssetVersion, models.Artifact, sarif.SarifSchema210Json) error); ok { + r0 = returnFunc(ctx, tx, userID, userAgent, assetVersion, artifact, sarifDoc) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// ComplianceRiskService_HandleArtifactCompliance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HandleArtifactCompliance' +type ComplianceRiskService_HandleArtifactCompliance_Call struct { + *mock.Call +} + +// HandleArtifactCompliance is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - userID string +// - userAgent *string +// - assetVersion models.AssetVersion +// - artifact models.Artifact +// - sarifDoc sarif.SarifSchema210Json +func (_e *ComplianceRiskService_Expecter) HandleArtifactCompliance(ctx interface{}, tx interface{}, userID interface{}, userAgent interface{}, assetVersion interface{}, artifact interface{}, sarifDoc interface{}) *ComplianceRiskService_HandleArtifactCompliance_Call { + return &ComplianceRiskService_HandleArtifactCompliance_Call{Call: _e.mock.On("HandleArtifactCompliance", ctx, tx, userID, userAgent, assetVersion, artifact, sarifDoc)} +} + +func (_c *ComplianceRiskService_HandleArtifactCompliance_Call) Run(run func(ctx context.Context, tx shared.DB, userID string, userAgent *string, assetVersion models.AssetVersion, artifact models.Artifact, sarifDoc sarif.SarifSchema210Json)) *ComplianceRiskService_HandleArtifactCompliance_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 *string + if args[3] != nil { + arg3 = args[3].(*string) + } + var arg4 models.AssetVersion + if args[4] != nil { + arg4 = args[4].(models.AssetVersion) + } + var arg5 models.Artifact + if args[5] != nil { + arg5 = args[5].(models.Artifact) + } + var arg6 sarif.SarifSchema210Json + if args[6] != nil { + arg6 = args[6].(sarif.SarifSchema210Json) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + ) + }) + return _c +} + +func (_c *ComplianceRiskService_HandleArtifactCompliance_Call) Return(err error) *ComplianceRiskService_HandleArtifactCompliance_Call { + _c.Call.Return(err) + return _c +} + +func (_c *ComplianceRiskService_HandleArtifactCompliance_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, userID string, userAgent *string, assetVersion models.AssetVersion, artifact models.Artifact, sarifDoc sarif.SarifSchema210Json) error) *ComplianceRiskService_HandleArtifactCompliance_Call { + _c.Call.Return(run) + return _c +} + +// UpdateComplianceRiskState provides a mock function for the type ComplianceRiskService +func (_mock *ComplianceRiskService) UpdateComplianceRiskState(ctx context.Context, tx shared.DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string) (models.VulnEvent, error) { + ret := _mock.Called(ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent) + + if len(ret) == 0 { + panic("no return value specified for UpdateComplianceRiskState") + } + + var r0 models.VulnEvent + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, *models.ComplianceRisk, string, string, dtos.MechanicalJustificationType, *string) (models.VulnEvent, error)); ok { + return returnFunc(ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, string, *models.ComplianceRisk, string, string, dtos.MechanicalJustificationType, *string) models.VulnEvent); ok { + r0 = returnFunc(ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent) + } else { + r0 = ret.Get(0).(models.VulnEvent) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, string, *models.ComplianceRisk, string, string, dtos.MechanicalJustificationType, *string) error); ok { + r1 = returnFunc(ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceRiskService_UpdateComplianceRiskState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateComplianceRiskState' +type ComplianceRiskService_UpdateComplianceRiskState_Call struct { + *mock.Call +} + +// UpdateComplianceRiskState is a helper method to define mock.On call +// - ctx context.Context +// - tx shared.DB +// - userID string +// - risk *models.ComplianceRisk +// - statusType string +// - justification string +// - mechanicalJustification dtos.MechanicalJustificationType +// - userAgent *string +func (_e *ComplianceRiskService_Expecter) UpdateComplianceRiskState(ctx interface{}, tx interface{}, userID interface{}, risk interface{}, statusType interface{}, justification interface{}, mechanicalJustification interface{}, userAgent interface{}) *ComplianceRiskService_UpdateComplianceRiskState_Call { + return &ComplianceRiskService_UpdateComplianceRiskState_Call{Call: _e.mock.On("UpdateComplianceRiskState", ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent)} +} + +func (_c *ComplianceRiskService_UpdateComplianceRiskState_Call) Run(run func(ctx context.Context, tx shared.DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string)) *ComplianceRiskService_UpdateComplianceRiskState_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 shared.DB + if args[1] != nil { + arg1 = args[1].(shared.DB) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + var arg3 *models.ComplianceRisk + if args[3] != nil { + arg3 = args[3].(*models.ComplianceRisk) + } + var arg4 string + if args[4] != nil { + arg4 = args[4].(string) + } + var arg5 string + if args[5] != nil { + arg5 = args[5].(string) + } + var arg6 dtos.MechanicalJustificationType + if args[6] != nil { + arg6 = args[6].(dtos.MechanicalJustificationType) + } + var arg7 *string + if args[7] != nil { + arg7 = args[7].(*string) + } + run( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + ) + }) + return _c +} + +func (_c *ComplianceRiskService_UpdateComplianceRiskState_Call) Return(vulnEvent models.VulnEvent, err error) *ComplianceRiskService_UpdateComplianceRiskState_Call { + _c.Call.Return(vulnEvent, err) + return _c +} + +func (_c *ComplianceRiskService_UpdateComplianceRiskState_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string) (models.VulnEvent, error)) *ComplianceRiskService_UpdateComplianceRiskState_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/mock_ComplianceService.go b/mocks/mock_ComplianceService.go new file mode 100644 index 000000000..f674a5778 --- /dev/null +++ b/mocks/mock_ComplianceService.go @@ -0,0 +1,119 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos/sarif" + mock "github.com/stretchr/testify/mock" +) + +// NewComplianceService creates a new instance of ComplianceService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewComplianceService(t interface { + mock.TestingT + Cleanup(func()) +}) *ComplianceService { + mock := &ComplianceService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ComplianceService is an autogenerated mock type for the ComplianceService type +type ComplianceService struct { + mock.Mock +} + +type ComplianceService_Expecter struct { + mock *mock.Mock +} + +func (_m *ComplianceService) EXPECT() *ComplianceService_Expecter { + return &ComplianceService_Expecter{mock: &_m.Mock} +} + +// EvaluateArtifactAttestations provides a mock function for the type ComplianceService +func (_mock *ComplianceService) EvaluateArtifactAttestations(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion, artifact models.Artifact) (sarif.SarifSchema210Json, error) { + ret := _mock.Called(ctx, projectID, assetVersion, artifact) + + if len(ret) == 0 { + panic("no return value specified for EvaluateArtifactAttestations") + } + + var r0 sarif.SarifSchema210Json + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, models.AssetVersion, models.Artifact) (sarif.SarifSchema210Json, error)); ok { + return returnFunc(ctx, projectID, assetVersion, artifact) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, models.AssetVersion, models.Artifact) sarif.SarifSchema210Json); ok { + r0 = returnFunc(ctx, projectID, assetVersion, artifact) + } else { + r0 = ret.Get(0).(sarif.SarifSchema210Json) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, uuid.UUID, models.AssetVersion, models.Artifact) error); ok { + r1 = returnFunc(ctx, projectID, assetVersion, artifact) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ComplianceService_EvaluateArtifactAttestations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EvaluateArtifactAttestations' +type ComplianceService_EvaluateArtifactAttestations_Call struct { + *mock.Call +} + +// EvaluateArtifactAttestations is a helper method to define mock.On call +// - ctx context.Context +// - projectID uuid.UUID +// - assetVersion models.AssetVersion +// - artifact models.Artifact +func (_e *ComplianceService_Expecter) EvaluateArtifactAttestations(ctx interface{}, projectID interface{}, assetVersion interface{}, artifact interface{}) *ComplianceService_EvaluateArtifactAttestations_Call { + return &ComplianceService_EvaluateArtifactAttestations_Call{Call: _e.mock.On("EvaluateArtifactAttestations", ctx, projectID, assetVersion, artifact)} +} + +func (_c *ComplianceService_EvaluateArtifactAttestations_Call) Run(run func(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion, artifact models.Artifact)) *ComplianceService_EvaluateArtifactAttestations_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 models.AssetVersion + if args[2] != nil { + arg2 = args[2].(models.AssetVersion) + } + var arg3 models.Artifact + if args[3] != nil { + arg3 = args[3].(models.Artifact) + } + run( + arg0, + arg1, + arg2, + arg3, + ) + }) + return _c +} + +func (_c *ComplianceService_EvaluateArtifactAttestations_Call) Return(sarifSchema210Json sarif.SarifSchema210Json, err error) *ComplianceService_EvaluateArtifactAttestations_Call { + _c.Call.Return(sarifSchema210Json, err) + return _c +} + +func (_c *ComplianceService_EvaluateArtifactAttestations_Call) RunAndReturn(run func(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion, artifact models.Artifact) (sarif.SarifSchema210Json, error)) *ComplianceService_EvaluateArtifactAttestations_Call { + _c.Call.Return(run) + return _c +} diff --git a/mocks/mock_ExternalPropertyFileReference.go b/mocks/mock_ExternalPropertyFileReference.go deleted file mode 100644 index a2163049d..000000000 --- a/mocks/mock_ExternalPropertyFileReference.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" -) - -// NewExternalPropertyFileReference creates a new instance of ExternalPropertyFileReference. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewExternalPropertyFileReference(t interface { - mock.TestingT - Cleanup(func()) -}) *ExternalPropertyFileReference { - mock := &ExternalPropertyFileReference{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// ExternalPropertyFileReference is an autogenerated mock type for the ExternalPropertyFileReference type -type ExternalPropertyFileReference struct { - mock.Mock -} - -type ExternalPropertyFileReference_Expecter struct { - mock *mock.Mock -} - -func (_m *ExternalPropertyFileReference) EXPECT() *ExternalPropertyFileReference_Expecter { - return &ExternalPropertyFileReference_Expecter{mock: &_m.Mock} -} diff --git a/mocks/mock_PolicyRepository.go b/mocks/mock_PolicyRepository.go deleted file mode 100644 index 0f5fee02e..000000000 --- a/mocks/mock_PolicyRepository.go +++ /dev/null @@ -1,1271 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - "context" - - "github.com/google/uuid" - "github.com/l3montree-dev/devguard/database/models" - "github.com/l3montree-dev/devguard/shared" - mock "github.com/stretchr/testify/mock" - "gorm.io/gorm/clause" -) - -// NewPolicyRepository creates a new instance of PolicyRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewPolicyRepository(t interface { - mock.TestingT - Cleanup(func()) -}) *PolicyRepository { - mock := &PolicyRepository{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// PolicyRepository is an autogenerated mock type for the PolicyRepository type -type PolicyRepository struct { - mock.Mock -} - -type PolicyRepository_Expecter struct { - mock *mock.Mock -} - -func (_m *PolicyRepository) EXPECT() *PolicyRepository_Expecter { - return &PolicyRepository_Expecter{mock: &_m.Mock} -} - -// Activate provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Activate(ctx context.Context, tx shared.DB, id uuid.UUID) error { - ret := _mock.Called(ctx, tx, id) - - if len(ret) == 0 { - panic("no return value specified for Activate") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) error); ok { - r0 = returnFunc(ctx, tx, id) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Activate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Activate' -type PolicyRepository_Activate_Call struct { - *mock.Call -} - -// Activate is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - id uuid.UUID -func (_e *PolicyRepository_Expecter) Activate(ctx interface{}, tx interface{}, id interface{}) *PolicyRepository_Activate_Call { - return &PolicyRepository_Activate_Call{Call: _e.mock.On("Activate", ctx, tx, id)} -} - -func (_c *PolicyRepository_Activate_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *PolicyRepository_Activate_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_Activate_Call) Return(err error) *PolicyRepository_Activate_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Activate_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) error) *PolicyRepository_Activate_Call { - _c.Call.Return(run) - return _c -} - -// All provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) All(ctx context.Context, tx shared.DB) ([]models.Policy, error) { - ret := _mock.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for All") - } - - var r0 []models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) ([]models.Policy, error)); ok { - return returnFunc(ctx, tx) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) []models.Policy); ok { - r0 = returnFunc(ctx, tx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Policy) - } - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB) error); ok { - r1 = returnFunc(ctx, tx) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_All_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'All' -type PolicyRepository_All_Call struct { - *mock.Call -} - -// All is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -func (_e *PolicyRepository_Expecter) All(ctx interface{}, tx interface{}) *PolicyRepository_All_Call { - return &PolicyRepository_All_Call{Call: _e.mock.On("All", ctx, tx)} -} - -func (_c *PolicyRepository_All_Call) Run(run func(ctx context.Context, tx shared.DB)) *PolicyRepository_All_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *PolicyRepository_All_Call) Return(policys []models.Policy, err error) *PolicyRepository_All_Call { - _c.Call.Return(policys, err) - return _c -} - -func (_c *PolicyRepository_All_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB) ([]models.Policy, error)) *PolicyRepository_All_Call { - _c.Call.Return(run) - return _c -} - -// Begin provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Begin(ctx context.Context) shared.DB { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Begin") - } - - var r0 shared.DB - if returnFunc, ok := ret.Get(0).(func(context.Context) shared.DB); ok { - r0 = returnFunc(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(shared.DB) - } - } - return r0 -} - -// PolicyRepository_Begin_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Begin' -type PolicyRepository_Begin_Call struct { - *mock.Call -} - -// Begin is a helper method to define mock.On call -// - ctx context.Context -func (_e *PolicyRepository_Expecter) Begin(ctx interface{}) *PolicyRepository_Begin_Call { - return &PolicyRepository_Begin_Call{Call: _e.mock.On("Begin", ctx)} -} - -func (_c *PolicyRepository_Begin_Call) Run(run func(ctx context.Context)) *PolicyRepository_Begin_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *PolicyRepository_Begin_Call) Return(v shared.DB) *PolicyRepository_Begin_Call { - _c.Call.Return(v) - return _c -} - -func (_c *PolicyRepository_Begin_Call) RunAndReturn(run func(ctx context.Context) shared.DB) *PolicyRepository_Begin_Call { - _c.Call.Return(run) - return _c -} - -// CleanupOrphanedRecords provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) CleanupOrphanedRecords(ctx context.Context) error { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for CleanupOrphanedRecords") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = returnFunc(ctx) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_CleanupOrphanedRecords_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanupOrphanedRecords' -type PolicyRepository_CleanupOrphanedRecords_Call struct { - *mock.Call -} - -// CleanupOrphanedRecords is a helper method to define mock.On call -// - ctx context.Context -func (_e *PolicyRepository_Expecter) CleanupOrphanedRecords(ctx interface{}) *PolicyRepository_CleanupOrphanedRecords_Call { - return &PolicyRepository_CleanupOrphanedRecords_Call{Call: _e.mock.On("CleanupOrphanedRecords", ctx)} -} - -func (_c *PolicyRepository_CleanupOrphanedRecords_Call) Run(run func(ctx context.Context)) *PolicyRepository_CleanupOrphanedRecords_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *PolicyRepository_CleanupOrphanedRecords_Call) Return(err error) *PolicyRepository_CleanupOrphanedRecords_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_CleanupOrphanedRecords_Call) RunAndReturn(run func(ctx context.Context) error) *PolicyRepository_CleanupOrphanedRecords_Call { - _c.Call.Return(run) - return _c -} - -// Create provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Create(ctx context.Context, tx shared.DB, t *models.Policy) error { - ret := _mock.Called(ctx, tx, t) - - if len(ret) == 0 { - panic("no return value specified for Create") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.Policy) error); ok { - r0 = returnFunc(ctx, tx, t) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' -type PolicyRepository_Create_Call struct { - *mock.Call -} - -// Create is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - t *models.Policy -func (_e *PolicyRepository_Expecter) Create(ctx interface{}, tx interface{}, t interface{}) *PolicyRepository_Create_Call { - return &PolicyRepository_Create_Call{Call: _e.mock.On("Create", ctx, tx, t)} -} - -func (_c *PolicyRepository_Create_Call) Run(run func(ctx context.Context, tx shared.DB, t *models.Policy)) *PolicyRepository_Create_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 *models.Policy - if args[2] != nil { - arg2 = args[2].(*models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_Create_Call) Return(err error) *PolicyRepository_Create_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Create_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *models.Policy) error) *PolicyRepository_Create_Call { - _c.Call.Return(run) - return _c -} - -// CreateBatch provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) CreateBatch(ctx context.Context, tx shared.DB, ts []models.Policy) error { - ret := _mock.Called(ctx, tx, ts) - - if len(ret) == 0 { - panic("no return value specified for CreateBatch") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.Policy) error); ok { - r0 = returnFunc(ctx, tx, ts) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_CreateBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateBatch' -type PolicyRepository_CreateBatch_Call struct { - *mock.Call -} - -// CreateBatch is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - ts []models.Policy -func (_e *PolicyRepository_Expecter) CreateBatch(ctx interface{}, tx interface{}, ts interface{}) *PolicyRepository_CreateBatch_Call { - return &PolicyRepository_CreateBatch_Call{Call: _e.mock.On("CreateBatch", ctx, tx, ts)} -} - -func (_c *PolicyRepository_CreateBatch_Call) Run(run func(ctx context.Context, tx shared.DB, ts []models.Policy)) *PolicyRepository_CreateBatch_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 []models.Policy - if args[2] != nil { - arg2 = args[2].([]models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_CreateBatch_Call) Return(err error) *PolicyRepository_CreateBatch_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_CreateBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ts []models.Policy) error) *PolicyRepository_CreateBatch_Call { - _c.Call.Return(run) - return _c -} - -// Delete provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Delete(ctx context.Context, tx shared.DB, id uuid.UUID) error { - ret := _mock.Called(ctx, tx, id) - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) error); ok { - r0 = returnFunc(ctx, tx, id) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type PolicyRepository_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - id uuid.UUID -func (_e *PolicyRepository_Expecter) Delete(ctx interface{}, tx interface{}, id interface{}) *PolicyRepository_Delete_Call { - return &PolicyRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, tx, id)} -} - -func (_c *PolicyRepository_Delete_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *PolicyRepository_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_Delete_Call) Return(err error) *PolicyRepository_Delete_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) error) *PolicyRepository_Delete_Call { - _c.Call.Return(run) - return _c -} - -// DeleteBatch provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) DeleteBatch(ctx context.Context, tx shared.DB, ids []models.Policy) error { - ret := _mock.Called(ctx, tx, ids) - - if len(ret) == 0 { - panic("no return value specified for DeleteBatch") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.Policy) error); ok { - r0 = returnFunc(ctx, tx, ids) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_DeleteBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteBatch' -type PolicyRepository_DeleteBatch_Call struct { - *mock.Call -} - -// DeleteBatch is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - ids []models.Policy -func (_e *PolicyRepository_Expecter) DeleteBatch(ctx interface{}, tx interface{}, ids interface{}) *PolicyRepository_DeleteBatch_Call { - return &PolicyRepository_DeleteBatch_Call{Call: _e.mock.On("DeleteBatch", ctx, tx, ids)} -} - -func (_c *PolicyRepository_DeleteBatch_Call) Run(run func(ctx context.Context, tx shared.DB, ids []models.Policy)) *PolicyRepository_DeleteBatch_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 []models.Policy - if args[2] != nil { - arg2 = args[2].([]models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_DeleteBatch_Call) Return(err error) *PolicyRepository_DeleteBatch_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_DeleteBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ids []models.Policy) error) *PolicyRepository_DeleteBatch_Call { - _c.Call.Return(run) - return _c -} - -// FindByOrganizationID provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) FindByOrganizationID(ctx context.Context, tx shared.DB, organizationID uuid.UUID) ([]models.Policy, error) { - ret := _mock.Called(ctx, tx, organizationID) - - if len(ret) == 0 { - panic("no return value specified for FindByOrganizationID") - } - - var r0 []models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) ([]models.Policy, error)); ok { - return returnFunc(ctx, tx, organizationID) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) []models.Policy); ok { - r0 = returnFunc(ctx, tx, organizationID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Policy) - } - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { - r1 = returnFunc(ctx, tx, organizationID) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_FindByOrganizationID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByOrganizationID' -type PolicyRepository_FindByOrganizationID_Call struct { - *mock.Call -} - -// FindByOrganizationID is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - organizationID uuid.UUID -func (_e *PolicyRepository_Expecter) FindByOrganizationID(ctx interface{}, tx interface{}, organizationID interface{}) *PolicyRepository_FindByOrganizationID_Call { - return &PolicyRepository_FindByOrganizationID_Call{Call: _e.mock.On("FindByOrganizationID", ctx, tx, organizationID)} -} - -func (_c *PolicyRepository_FindByOrganizationID_Call) Run(run func(ctx context.Context, tx shared.DB, organizationID uuid.UUID)) *PolicyRepository_FindByOrganizationID_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_FindByOrganizationID_Call) Return(policys []models.Policy, err error) *PolicyRepository_FindByOrganizationID_Call { - _c.Call.Return(policys, err) - return _c -} - -func (_c *PolicyRepository_FindByOrganizationID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, organizationID uuid.UUID) ([]models.Policy, error)) *PolicyRepository_FindByOrganizationID_Call { - _c.Call.Return(run) - return _c -} - -// FindByProjectID provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) FindByProjectID(ctx context.Context, tx shared.DB, projectID uuid.UUID) ([]models.Policy, error) { - ret := _mock.Called(ctx, tx, projectID) - - if len(ret) == 0 { - panic("no return value specified for FindByProjectID") - } - - var r0 []models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) ([]models.Policy, error)); ok { - return returnFunc(ctx, tx, projectID) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) []models.Policy); ok { - r0 = returnFunc(ctx, tx, projectID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Policy) - } - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { - r1 = returnFunc(ctx, tx, projectID) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_FindByProjectID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByProjectID' -type PolicyRepository_FindByProjectID_Call struct { - *mock.Call -} - -// FindByProjectID is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - projectID uuid.UUID -func (_e *PolicyRepository_Expecter) FindByProjectID(ctx interface{}, tx interface{}, projectID interface{}) *PolicyRepository_FindByProjectID_Call { - return &PolicyRepository_FindByProjectID_Call{Call: _e.mock.On("FindByProjectID", ctx, tx, projectID)} -} - -func (_c *PolicyRepository_FindByProjectID_Call) Run(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID)) *PolicyRepository_FindByProjectID_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_FindByProjectID_Call) Return(policys []models.Policy, err error) *PolicyRepository_FindByProjectID_Call { - _c.Call.Return(policys, err) - return _c -} - -func (_c *PolicyRepository_FindByProjectID_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID) ([]models.Policy, error)) *PolicyRepository_FindByProjectID_Call { - _c.Call.Return(run) - return _c -} - -// FindCommunityManagedPolicies provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) FindCommunityManagedPolicies(ctx context.Context, tx shared.DB) ([]models.Policy, error) { - ret := _mock.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for FindCommunityManagedPolicies") - } - - var r0 []models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) ([]models.Policy, error)); ok { - return returnFunc(ctx, tx) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) []models.Policy); ok { - r0 = returnFunc(ctx, tx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Policy) - } - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB) error); ok { - r1 = returnFunc(ctx, tx) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_FindCommunityManagedPolicies_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindCommunityManagedPolicies' -type PolicyRepository_FindCommunityManagedPolicies_Call struct { - *mock.Call -} - -// FindCommunityManagedPolicies is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -func (_e *PolicyRepository_Expecter) FindCommunityManagedPolicies(ctx interface{}, tx interface{}) *PolicyRepository_FindCommunityManagedPolicies_Call { - return &PolicyRepository_FindCommunityManagedPolicies_Call{Call: _e.mock.On("FindCommunityManagedPolicies", ctx, tx)} -} - -func (_c *PolicyRepository_FindCommunityManagedPolicies_Call) Run(run func(ctx context.Context, tx shared.DB)) *PolicyRepository_FindCommunityManagedPolicies_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *PolicyRepository_FindCommunityManagedPolicies_Call) Return(policys []models.Policy, err error) *PolicyRepository_FindCommunityManagedPolicies_Call { - _c.Call.Return(policys, err) - return _c -} - -func (_c *PolicyRepository_FindCommunityManagedPolicies_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB) ([]models.Policy, error)) *PolicyRepository_FindCommunityManagedPolicies_Call { - _c.Call.Return(run) - return _c -} - -// GetDB provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) GetDB(ctx context.Context, tx shared.DB) shared.DB { - ret := _mock.Called(ctx, tx) - - if len(ret) == 0 { - panic("no return value specified for GetDB") - } - - var r0 shared.DB - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB) shared.DB); ok { - r0 = returnFunc(ctx, tx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(shared.DB) - } - } - return r0 -} - -// PolicyRepository_GetDB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDB' -type PolicyRepository_GetDB_Call struct { - *mock.Call -} - -// GetDB is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -func (_e *PolicyRepository_Expecter) GetDB(ctx interface{}, tx interface{}) *PolicyRepository_GetDB_Call { - return &PolicyRepository_GetDB_Call{Call: _e.mock.On("GetDB", ctx, tx)} -} - -func (_c *PolicyRepository_GetDB_Call) Run(run func(ctx context.Context, tx shared.DB)) *PolicyRepository_GetDB_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *PolicyRepository_GetDB_Call) Return(v shared.DB) *PolicyRepository_GetDB_Call { - _c.Call.Return(v) - return _c -} - -func (_c *PolicyRepository_GetDB_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB) shared.DB) *PolicyRepository_GetDB_Call { - _c.Call.Return(run) - return _c -} - -// List provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) List(ctx context.Context, tx shared.DB, ids []uuid.UUID) ([]models.Policy, error) { - ret := _mock.Called(ctx, tx, ids) - - if len(ret) == 0 { - panic("no return value specified for List") - } - - var r0 []models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) ([]models.Policy, error)); ok { - return returnFunc(ctx, tx, ids) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []uuid.UUID) []models.Policy); ok { - r0 = returnFunc(ctx, tx, ids) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]models.Policy) - } - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, []uuid.UUID) error); ok { - r1 = returnFunc(ctx, tx, ids) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' -type PolicyRepository_List_Call struct { - *mock.Call -} - -// List is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - ids []uuid.UUID -func (_e *PolicyRepository_Expecter) List(ctx interface{}, tx interface{}, ids interface{}) *PolicyRepository_List_Call { - return &PolicyRepository_List_Call{Call: _e.mock.On("List", ctx, tx, ids)} -} - -func (_c *PolicyRepository_List_Call) Run(run func(ctx context.Context, tx shared.DB, ids []uuid.UUID)) *PolicyRepository_List_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 []uuid.UUID - if args[2] != nil { - arg2 = args[2].([]uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_List_Call) Return(policys []models.Policy, err error) *PolicyRepository_List_Call { - _c.Call.Return(policys, err) - return _c -} - -func (_c *PolicyRepository_List_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ids []uuid.UUID) ([]models.Policy, error)) *PolicyRepository_List_Call { - _c.Call.Return(run) - return _c -} - -// Read provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Read(ctx context.Context, tx shared.DB, id uuid.UUID) (models.Policy, error) { - ret := _mock.Called(ctx, tx, id) - - if len(ret) == 0 { - panic("no return value specified for Read") - } - - var r0 models.Policy - var r1 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) (models.Policy, error)); ok { - return returnFunc(ctx, tx, id) - } - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID) models.Policy); ok { - r0 = returnFunc(ctx, tx, id) - } else { - r0 = ret.Get(0).(models.Policy) - } - if returnFunc, ok := ret.Get(1).(func(context.Context, shared.DB, uuid.UUID) error); ok { - r1 = returnFunc(ctx, tx, id) - } else { - r1 = ret.Error(1) - } - return r0, r1 -} - -// PolicyRepository_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' -type PolicyRepository_Read_Call struct { - *mock.Call -} - -// Read is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - id uuid.UUID -func (_e *PolicyRepository_Expecter) Read(ctx interface{}, tx interface{}, id interface{}) *PolicyRepository_Read_Call { - return &PolicyRepository_Read_Call{Call: _e.mock.On("Read", ctx, tx, id)} -} - -func (_c *PolicyRepository_Read_Call) Run(run func(ctx context.Context, tx shared.DB, id uuid.UUID)) *PolicyRepository_Read_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_Read_Call) Return(policy models.Policy, err error) *PolicyRepository_Read_Call { - _c.Call.Return(policy, err) - return _c -} - -func (_c *PolicyRepository_Read_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, id uuid.UUID) (models.Policy, error)) *PolicyRepository_Read_Call { - _c.Call.Return(run) - return _c -} - -// Save provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Save(ctx context.Context, tx shared.DB, t *models.Policy) error { - ret := _mock.Called(ctx, tx, t) - - if len(ret) == 0 { - panic("no return value specified for Save") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *models.Policy) error); ok { - r0 = returnFunc(ctx, tx, t) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save' -type PolicyRepository_Save_Call struct { - *mock.Call -} - -// Save is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - t *models.Policy -func (_e *PolicyRepository_Expecter) Save(ctx interface{}, tx interface{}, t interface{}) *PolicyRepository_Save_Call { - return &PolicyRepository_Save_Call{Call: _e.mock.On("Save", ctx, tx, t)} -} - -func (_c *PolicyRepository_Save_Call) Run(run func(ctx context.Context, tx shared.DB, t *models.Policy)) *PolicyRepository_Save_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 *models.Policy - if args[2] != nil { - arg2 = args[2].(*models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_Save_Call) Return(err error) *PolicyRepository_Save_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Save_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *models.Policy) error) *PolicyRepository_Save_Call { - _c.Call.Return(run) - return _c -} - -// SaveBatch provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) SaveBatch(ctx context.Context, tx shared.DB, ts []models.Policy) error { - ret := _mock.Called(ctx, tx, ts) - - if len(ret) == 0 { - panic("no return value specified for SaveBatch") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.Policy) error); ok { - r0 = returnFunc(ctx, tx, ts) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_SaveBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveBatch' -type PolicyRepository_SaveBatch_Call struct { - *mock.Call -} - -// SaveBatch is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - ts []models.Policy -func (_e *PolicyRepository_Expecter) SaveBatch(ctx interface{}, tx interface{}, ts interface{}) *PolicyRepository_SaveBatch_Call { - return &PolicyRepository_SaveBatch_Call{Call: _e.mock.On("SaveBatch", ctx, tx, ts)} -} - -func (_c *PolicyRepository_SaveBatch_Call) Run(run func(ctx context.Context, tx shared.DB, ts []models.Policy)) *PolicyRepository_SaveBatch_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 []models.Policy - if args[2] != nil { - arg2 = args[2].([]models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_SaveBatch_Call) Return(err error) *PolicyRepository_SaveBatch_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_SaveBatch_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ts []models.Policy) error) *PolicyRepository_SaveBatch_Call { - _c.Call.Return(run) - return _c -} - -// SaveBatchBestEffort provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) SaveBatchBestEffort(ctx context.Context, tx shared.DB, ts []models.Policy) error { - ret := _mock.Called(ctx, tx, ts) - - if len(ret) == 0 { - panic("no return value specified for SaveBatchBestEffort") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, []models.Policy) error); ok { - r0 = returnFunc(ctx, tx, ts) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_SaveBatchBestEffort_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveBatchBestEffort' -type PolicyRepository_SaveBatchBestEffort_Call struct { - *mock.Call -} - -// SaveBatchBestEffort is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - ts []models.Policy -func (_e *PolicyRepository_Expecter) SaveBatchBestEffort(ctx interface{}, tx interface{}, ts interface{}) *PolicyRepository_SaveBatchBestEffort_Call { - return &PolicyRepository_SaveBatchBestEffort_Call{Call: _e.mock.On("SaveBatchBestEffort", ctx, tx, ts)} -} - -func (_c *PolicyRepository_SaveBatchBestEffort_Call) Run(run func(ctx context.Context, tx shared.DB, ts []models.Policy)) *PolicyRepository_SaveBatchBestEffort_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 []models.Policy - if args[2] != nil { - arg2 = args[2].([]models.Policy) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *PolicyRepository_SaveBatchBestEffort_Call) Return(err error) *PolicyRepository_SaveBatchBestEffort_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_SaveBatchBestEffort_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, ts []models.Policy) error) *PolicyRepository_SaveBatchBestEffort_Call { - _c.Call.Return(run) - return _c -} - -// Transaction provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Transaction(ctx context.Context, fn func(tx shared.DB) error) error { - ret := _mock.Called(ctx, fn) - - if len(ret) == 0 { - panic("no return value specified for Transaction") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, func(tx shared.DB) error) error); ok { - r0 = returnFunc(ctx, fn) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Transaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Transaction' -type PolicyRepository_Transaction_Call struct { - *mock.Call -} - -// Transaction is a helper method to define mock.On call -// - ctx context.Context -// - fn func(tx shared.DB) error -func (_e *PolicyRepository_Expecter) Transaction(ctx interface{}, fn interface{}) *PolicyRepository_Transaction_Call { - return &PolicyRepository_Transaction_Call{Call: _e.mock.On("Transaction", ctx, fn)} -} - -func (_c *PolicyRepository_Transaction_Call) Run(run func(ctx context.Context, fn func(tx shared.DB) error)) *PolicyRepository_Transaction_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 func(tx shared.DB) error - if args[1] != nil { - arg1 = args[1].(func(tx shared.DB) error) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *PolicyRepository_Transaction_Call) Return(err error) *PolicyRepository_Transaction_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Transaction_Call) RunAndReturn(run func(ctx context.Context, fn func(tx shared.DB) error) error) *PolicyRepository_Transaction_Call { - _c.Call.Return(run) - return _c -} - -// Upsert provides a mock function for the type PolicyRepository -func (_mock *PolicyRepository) Upsert(ctx context.Context, tx shared.DB, t *[]*models.Policy, conflictingColumns []clause.Column, updateOnly []string) error { - ret := _mock.Called(ctx, tx, t, conflictingColumns, updateOnly) - - if len(ret) == 0 { - panic("no return value specified for Upsert") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, *[]*models.Policy, []clause.Column, []string) error); ok { - r0 = returnFunc(ctx, tx, t, conflictingColumns, updateOnly) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// PolicyRepository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' -type PolicyRepository_Upsert_Call struct { - *mock.Call -} - -// Upsert is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - t *[]*models.Policy -// - conflictingColumns []clause.Column -// - updateOnly []string -func (_e *PolicyRepository_Expecter) Upsert(ctx interface{}, tx interface{}, t interface{}, conflictingColumns interface{}, updateOnly interface{}) *PolicyRepository_Upsert_Call { - return &PolicyRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, tx, t, conflictingColumns, updateOnly)} -} - -func (_c *PolicyRepository_Upsert_Call) Run(run func(ctx context.Context, tx shared.DB, t *[]*models.Policy, conflictingColumns []clause.Column, updateOnly []string)) *PolicyRepository_Upsert_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 *[]*models.Policy - if args[2] != nil { - arg2 = args[2].(*[]*models.Policy) - } - var arg3 []clause.Column - if args[3] != nil { - arg3 = args[3].([]clause.Column) - } - var arg4 []string - if args[4] != nil { - arg4 = args[4].([]string) - } - run( - arg0, - arg1, - arg2, - arg3, - arg4, - ) - }) - return _c -} - -func (_c *PolicyRepository_Upsert_Call) Return(err error) *PolicyRepository_Upsert_Call { - _c.Call.Return(err) - return _c -} - -func (_c *PolicyRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, t *[]*models.Policy, conflictingColumns []clause.Column, updateOnly []string) error) *PolicyRepository_Upsert_Call { - _c.Call.Return(run) - return _c -} diff --git a/mocks/mock_ProjectRepository.go b/mocks/mock_ProjectRepository.go index 73ccd679f..131aa4d3f 100644 --- a/mocks/mock_ProjectRepository.go +++ b/mocks/mock_ProjectRepository.go @@ -299,75 +299,6 @@ func (_c *ProjectRepository_Delete_Call) RunAndReturn(run func(ctx context.Conte return _c } -// DisablePolicyForProject provides a mock function for the type ProjectRepository -func (_mock *ProjectRepository) DisablePolicyForProject(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID) error { - ret := _mock.Called(ctx, tx, projectID, policyID) - - if len(ret) == 0 { - panic("no return value specified for DisablePolicyForProject") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, uuid.UUID) error); ok { - r0 = returnFunc(ctx, tx, projectID, policyID) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// ProjectRepository_DisablePolicyForProject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisablePolicyForProject' -type ProjectRepository_DisablePolicyForProject_Call struct { - *mock.Call -} - -// DisablePolicyForProject is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - projectID uuid.UUID -// - policyID uuid.UUID -func (_e *ProjectRepository_Expecter) DisablePolicyForProject(ctx interface{}, tx interface{}, projectID interface{}, policyID interface{}) *ProjectRepository_DisablePolicyForProject_Call { - return &ProjectRepository_DisablePolicyForProject_Call{Call: _e.mock.On("DisablePolicyForProject", ctx, tx, projectID, policyID)} -} - -func (_c *ProjectRepository_DisablePolicyForProject_Call) Run(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID)) *ProjectRepository_DisablePolicyForProject_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - var arg3 uuid.UUID - if args[3] != nil { - arg3 = args[3].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - arg3, - ) - }) - return _c -} - -func (_c *ProjectRepository_DisablePolicyForProject_Call) Return(err error) *ProjectRepository_DisablePolicyForProject_Call { - _c.Call.Return(err) - return _c -} - -func (_c *ProjectRepository_DisablePolicyForProject_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID) error) *ProjectRepository_DisablePolicyForProject_Call { - _c.Call.Return(run) - return _c -} - // EnableCommunityManagedPolicies provides a mock function for the type ProjectRepository func (_mock *ProjectRepository) EnableCommunityManagedPolicies(ctx context.Context, tx shared.DB, projectID uuid.UUID) error { ret := _mock.Called(ctx, tx, projectID) @@ -431,75 +362,6 @@ func (_c *ProjectRepository_EnableCommunityManagedPolicies_Call) RunAndReturn(ru return _c } -// EnablePolicyForProject provides a mock function for the type ProjectRepository -func (_mock *ProjectRepository) EnablePolicyForProject(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID) error { - ret := _mock.Called(ctx, tx, projectID, policyID) - - if len(ret) == 0 { - panic("no return value specified for EnablePolicyForProject") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, shared.DB, uuid.UUID, uuid.UUID) error); ok { - r0 = returnFunc(ctx, tx, projectID, policyID) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// ProjectRepository_EnablePolicyForProject_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnablePolicyForProject' -type ProjectRepository_EnablePolicyForProject_Call struct { - *mock.Call -} - -// EnablePolicyForProject is a helper method to define mock.On call -// - ctx context.Context -// - tx shared.DB -// - projectID uuid.UUID -// - policyID uuid.UUID -func (_e *ProjectRepository_Expecter) EnablePolicyForProject(ctx interface{}, tx interface{}, projectID interface{}, policyID interface{}) *ProjectRepository_EnablePolicyForProject_Call { - return &ProjectRepository_EnablePolicyForProject_Call{Call: _e.mock.On("EnablePolicyForProject", ctx, tx, projectID, policyID)} -} - -func (_c *ProjectRepository_EnablePolicyForProject_Call) Run(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID)) *ProjectRepository_EnablePolicyForProject_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 shared.DB - if args[1] != nil { - arg1 = args[1].(shared.DB) - } - var arg2 uuid.UUID - if args[2] != nil { - arg2 = args[2].(uuid.UUID) - } - var arg3 uuid.UUID - if args[3] != nil { - arg3 = args[3].(uuid.UUID) - } - run( - arg0, - arg1, - arg2, - arg3, - ) - }) - return _c -} - -func (_c *ProjectRepository_EnablePolicyForProject_Call) Return(err error) *ProjectRepository_EnablePolicyForProject_Call { - _c.Call.Return(err) - return _c -} - -func (_c *ProjectRepository_EnablePolicyForProject_Call) RunAndReturn(run func(ctx context.Context, tx shared.DB, projectID uuid.UUID, policyID uuid.UUID) error) *ProjectRepository_EnablePolicyForProject_Call { - _c.Call.Return(run) - return _c -} - // GetByOrgID provides a mock function for the type ProjectRepository func (_mock *ProjectRepository) GetByOrgID(ctx context.Context, tx shared.DB, organizationID uuid.UUID) ([]models.Project, error) { ret := _mock.Called(ctx, tx, organizationID) diff --git a/mocks/mock_ReportingDescriptorReference.go b/mocks/mock_ReportingDescriptorReference.go deleted file mode 100644 index e38e0efb6..000000000 --- a/mocks/mock_ReportingDescriptorReference.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" -) - -// NewReportingDescriptorReference creates a new instance of ReportingDescriptorReference. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewReportingDescriptorReference(t interface { - mock.TestingT - Cleanup(func()) -}) *ReportingDescriptorReference { - mock := &ReportingDescriptorReference{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// ReportingDescriptorReference is an autogenerated mock type for the ReportingDescriptorReference type -type ReportingDescriptorReference struct { - mock.Mock -} - -type ReportingDescriptorReference_Expecter struct { - mock *mock.Mock -} - -func (_m *ReportingDescriptorReference) EXPECT() *ReportingDescriptorReference_Expecter { - return &ReportingDescriptorReference_Expecter{mock: &_m.Mock} -} diff --git a/mocks/mock_VulnDBImportService.go b/mocks/mock_VulnDBImportService.go deleted file mode 100644 index fbc5bbcf9..000000000 --- a/mocks/mock_VulnDBImportService.go +++ /dev/null @@ -1,260 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package mocks - -import ( - "context" - - mock "github.com/stretchr/testify/mock" -) - -// NewVulnDBImportService creates a new instance of VulnDBImportService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewVulnDBImportService(t interface { - mock.TestingT - Cleanup(func()) -}) *VulnDBImportService { - mock := &VulnDBImportService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// VulnDBImportService is an autogenerated mock type for the VulnDBImportService type -type VulnDBImportService struct { - mock.Mock -} - -type VulnDBImportService_Expecter struct { - mock *mock.Mock -} - -func (_m *VulnDBImportService) EXPECT() *VulnDBImportService_Expecter { - return &VulnDBImportService_Expecter{mock: &_m.Mock} -} - -// CleanupOrphanedTables provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) CleanupOrphanedTables(ctx context.Context) error { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for CleanupOrphanedTables") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = returnFunc(ctx) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_CleanupOrphanedTables_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CleanupOrphanedTables' -type VulnDBImportService_CleanupOrphanedTables_Call struct { - *mock.Call -} - -// CleanupOrphanedTables is a helper method to define mock.On call -// - ctx context.Context -func (_e *VulnDBImportService_Expecter) CleanupOrphanedTables(ctx interface{}) *VulnDBImportService_CleanupOrphanedTables_Call { - return &VulnDBImportService_CleanupOrphanedTables_Call{Call: _e.mock.On("CleanupOrphanedTables", ctx)} -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) Run(run func(ctx context.Context)) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) Return(err error) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_CleanupOrphanedTables_Call) RunAndReturn(run func(ctx context.Context) error) *VulnDBImportService_CleanupOrphanedTables_Call { - _c.Call.Return(run) - return _c -} - -// CreateTablesWithSuffix provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) CreateTablesWithSuffix(ctx context.Context, suffix string) error { - ret := _mock.Called(ctx, suffix) - - if len(ret) == 0 { - panic("no return value specified for CreateTablesWithSuffix") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = returnFunc(ctx, suffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_CreateTablesWithSuffix_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTablesWithSuffix' -type VulnDBImportService_CreateTablesWithSuffix_Call struct { - *mock.Call -} - -// CreateTablesWithSuffix is a helper method to define mock.On call -// - ctx context.Context -// - suffix string -func (_e *VulnDBImportService_Expecter) CreateTablesWithSuffix(ctx interface{}, suffix interface{}) *VulnDBImportService_CreateTablesWithSuffix_Call { - return &VulnDBImportService_CreateTablesWithSuffix_Call{Call: _e.mock.On("CreateTablesWithSuffix", ctx, suffix)} -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) Run(run func(ctx context.Context, suffix string)) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) Return(err error) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_CreateTablesWithSuffix_Call) RunAndReturn(run func(ctx context.Context, suffix string) error) *VulnDBImportService_CreateTablesWithSuffix_Call { - _c.Call.Return(run) - return _c -} - -// ExportDiffs provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) ExportDiffs(ctx context.Context, extraTableNameSuffix string) error { - ret := _mock.Called(ctx, extraTableNameSuffix) - - if len(ret) == 0 { - panic("no return value specified for ExportDiffs") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = returnFunc(ctx, extraTableNameSuffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_ExportDiffs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExportDiffs' -type VulnDBImportService_ExportDiffs_Call struct { - *mock.Call -} - -// ExportDiffs is a helper method to define mock.On call -// - ctx context.Context -// - extraTableNameSuffix string -func (_e *VulnDBImportService_Expecter) ExportDiffs(ctx interface{}, extraTableNameSuffix interface{}) *VulnDBImportService_ExportDiffs_Call { - return &VulnDBImportService_ExportDiffs_Call{Call: _e.mock.On("ExportDiffs", ctx, extraTableNameSuffix)} -} - -func (_c *VulnDBImportService_ExportDiffs_Call) Run(run func(ctx context.Context, extraTableNameSuffix string)) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_ExportDiffs_Call) Return(err error) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_ExportDiffs_Call) RunAndReturn(run func(ctx context.Context, extraTableNameSuffix string) error) *VulnDBImportService_ExportDiffs_Call { - _c.Call.Return(run) - return _c -} - -// ImportFromDiff provides a mock function for the type VulnDBImportService -func (_mock *VulnDBImportService) ImportFromDiff(ctx context.Context, extraTableNameSuffix *string) error { - ret := _mock.Called(ctx, extraTableNameSuffix) - - if len(ret) == 0 { - panic("no return value specified for ImportFromDiff") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, *string) error); ok { - r0 = returnFunc(ctx, extraTableNameSuffix) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// VulnDBImportService_ImportFromDiff_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ImportFromDiff' -type VulnDBImportService_ImportFromDiff_Call struct { - *mock.Call -} - -// ImportFromDiff is a helper method to define mock.On call -// - ctx context.Context -// - extraTableNameSuffix *string -func (_e *VulnDBImportService_Expecter) ImportFromDiff(ctx interface{}, extraTableNameSuffix interface{}) *VulnDBImportService_ImportFromDiff_Call { - return &VulnDBImportService_ImportFromDiff_Call{Call: _e.mock.On("ImportFromDiff", ctx, extraTableNameSuffix)} -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) Run(run func(ctx context.Context, extraTableNameSuffix *string)) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 *string - if args[1] != nil { - arg1 = args[1].(*string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) Return(err error) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Return(err) - return _c -} - -func (_c *VulnDBImportService_ImportFromDiff_Call) RunAndReturn(run func(ctx context.Context, extraTableNameSuffix *string) error) *VulnDBImportService_ImportFromDiff_Call { - _c.Call.Return(run) - return _c -} diff --git a/router/artifact_router.go b/router/artifact_router.go index 3b94c3246..05e08afcc 100644 --- a/router/artifact_router.go +++ b/router/artifact_router.go @@ -30,6 +30,7 @@ func NewArtifactRouter( assetVersionGroup AssetVersionRouter, artifactController *controllers.ArtifactController, externalReferenceController *controllers.ExternalReferenceController, + attestationController *controllers.AttestationController, artifactRepository shared.ArtifactRepository, assetRepository shared.AssetRepository, ) ArtifactRouter { @@ -43,6 +44,7 @@ func NewArtifactRouter( artifactRouter.GET("/vex.xml/", artifactController.VEXXML) artifactRouter.GET("/sbom.pdf/", artifactController.BuildPDFFromSBOM) artifactRouter.GET("/vulnerability-report.pdf/", artifactController.BuildVulnerabilityReportPDF) + artifactRouter.GET("/attestations/", attestationController.ListByArtifact) artifactRouter.DELETE("/", artifactController.DeleteArtifact, middlewares.NeededScope([]string{"manage"}), assetScopedRBAC(shared.ObjectAsset, shared.ActionUpdate)) artifactRouter.PUT("/", artifactController.UpdateArtifact, middlewares.NeededScope([]string{"manage"}), assetScopedRBAC(shared.ObjectAsset, shared.ActionUpdate)) diff --git a/router/asset_router.go b/router/asset_router.go index b44b918c3..42b513337 100644 --- a/router/asset_router.go +++ b/router/asset_router.go @@ -32,7 +32,6 @@ func NewAssetRouter( assetController *controllers.AssetController, dependencyProxyController *dependencyfirewall.DependencyProxyController, assetVersionController *controllers.AssetVersionController, - complianceController *controllers.ComplianceController, statisticsController *controllers.StatisticsController, componentController *controllers.ComponentController, intotoController *controllers.InToToController, @@ -49,8 +48,6 @@ func NewAssetRouter( assetRouter := projectGroup.Group.Group("/assets/:assetSlug", assetScopedRBAC(shared.ObjectAsset, shared.ActionRead)) assetRouter.GET("/", assetController.Read) - assetRouter.GET("/compliance/", complianceController.AssetCompliance) - assetRouter.GET("/compliance/:policy/", complianceController.Details) assetRouter.GET("/number-of-exploits/", statisticsController.GetCVESWithKnownExploits) assetRouter.GET("/components/licenses/", componentController.LicenseDistribution) assetRouter.GET("/config-files/:config-file/", assetController.GetConfigFile) diff --git a/router/asset_version_router.go b/router/asset_version_router.go index 6a026ecd8..9abd26a76 100644 --- a/router/asset_version_router.go +++ b/router/asset_version_router.go @@ -30,7 +30,6 @@ func NewAssetVersionRouter( assetGroup AssetRouter, assetVersionController *controllers.AssetVersionController, firstPartyVulnController *controllers.FirstPartyVulnController, - complianceController *controllers.ComplianceController, componentController *controllers.ComponentController, statisticsController *controllers.StatisticsController, attestationController *controllers.AttestationController, @@ -50,8 +49,7 @@ func NewAssetVersionRouter( assetVersionRouter.GET("/vex.json/", assetVersionController.VEXJSON) assetVersionRouter.GET("/sbom.json/", assetVersionController.SBOMJSON) assetVersionRouter.GET("/", assetVersionController.Read) - assetVersionRouter.GET("/compliance/", complianceController.AssetCompliance) - assetVersionRouter.GET("/compliance/:policy/", complianceController.Details) + assetVersionRouter.GET("/metrics/", assetVersionController.Metrics) assetVersionRouter.GET("/components/licenses/", componentController.LicenseDistribution) assetVersionRouter.GET("/affected-components/", assetVersionController.AffectedComponents) diff --git a/router/compliance_risk_router.go b/router/compliance_risk_router.go new file mode 100644 index 000000000..27177e34c --- /dev/null +++ b/router/compliance_risk_router.go @@ -0,0 +1,28 @@ +package router + +import ( + "github.com/l3montree-dev/devguard/controllers" + "github.com/l3montree-dev/devguard/middlewares" + "github.com/labstack/echo/v4" +) + +type ComplianceRiskRouter struct { + *echo.Group +} + +func NewComplianceRiskRouter( + assetVersionGroup AssetVersionRouter, + controller *controllers.ComplianceRiskController, +) ComplianceRiskRouter { + complianceRisksRouter := assetVersionGroup.Group.Group("/compliance-risks") + complianceRisksRouter.GET("/", controller.ListPaged) + complianceRisksRouter.GET("/:complianceRiskID/", controller.Read) + complianceRisksRouter.GET("/:complianceRiskID/evidence/", controller.GetEvidence) + complianceRisksRouter.POST("/:complianceRiskID/", controller.CreateEvent, middlewares.NeededScope([]string{"manage"}), middlewares.DisallowPublicRequests) + complianceRisksRouter.POST("/:complianceRiskID/mitigate/", controller.Mitigate, middlewares.NeededScope([]string{"manage"}), middlewares.DisallowPublicRequests) + + complianceRisksRouter.POST("/evaluate/", controller.RunAttestationEvaluation, middlewares.NeededScope([]string{"manage"}), middlewares.DisallowPublicRequests) + complianceRisksRouter.POST("/upload-zip/", controller.UploadZip, middlewares.NeededScope([]string{"manage"}), middlewares.DisallowPublicRequests) + + return ComplianceRiskRouter{Group: complianceRisksRouter} +} diff --git a/router/org_router.go b/router/org_router.go index eccc375e0..fb2871913 100644 --- a/router/org_router.go +++ b/router/org_router.go @@ -36,7 +36,6 @@ func NewOrgRouter( dependencyProxyController *dependencyfirewall.DependencyProxyController, dependencyVulnController *controllers.DependencyVulnController, firstPartyVulnController *controllers.FirstPartyVulnController, - policyController *controllers.PolicyController, integrationController *controllers.IntegrationController, webhookIntegration *controllers.WebhookController, externalEntityProviderService shared.ExternalEntityProviderService, @@ -75,8 +74,6 @@ func NewOrgRouter( organizationRouter.GET("/content-tree/", orgController.ContentTree) organizationRouter.GET("/dependency-vulns/", dependencyVulnController.ListByOrgPaged) organizationRouter.GET("/first-party-vulns/", firstPartyVulnController.ListByOrgPaged) - organizationRouter.GET("/policies/", policyController.GetOrganizationPolicies) - organizationRouter.GET("/policies/:policyID/", policyController.GetPolicy) organizationRouter.GET("/members/", orgController.Members) organizationRouter.GET("/integrations/finish-installation/", integrationController.FinishInstallation) organizationRouter.GET("/projects/", projectController.List) @@ -89,18 +86,14 @@ func NewOrgRouter( organizationUpdateAccessControlRequired.POST("/integrations/jira/test-and-save/", integrationController.TestAndSaveJiraIntegration) organizationUpdateAccessControlRequired.POST("/integrations/webhook/test-and-save/", webhookIntegration.Save) organizationUpdateAccessControlRequired.POST("/integrations/webhook/test/", webhookIntegration.Test) - organizationUpdateAccessControlRequired.POST("/policies/", policyController.CreatePolicy) - organizationUpdateAccessControlRequired.POST("/integrations/gitlab/test-and-save/", integrationController.TestAndSaveGitlabIntegration) organizationUpdateAccessControlRequired.POST("/projects/", projectController.Create) - organizationUpdateAccessControlRequired.DELETE("/policies/:policyID/", policyController.DeletePolicy) organizationUpdateAccessControlRequired.DELETE("/integrations/gitlab/:gitlab_integration_id/", integrationController.DeleteGitLabAccessToken) organizationUpdateAccessControlRequired.DELETE("/members/:userID/", orgController.RemoveMember) organizationUpdateAccessControlRequired.DELETE("/integrations/jira/:jira_integration_id/", integrationController.DeleteJiraAccessToken) organizationUpdateAccessControlRequired.DELETE("/integrations/webhook/:id/", webhookIntegration.Delete) organizationUpdateAccessControlRequired.PATCH("/", orgController.Update) - organizationUpdateAccessControlRequired.PUT("/policies/:policyID/", policyController.UpdatePolicy) organizationUpdateAccessControlRequired.PUT("/members/:userID/", orgController.ChangeRole) organizationUpdateAccessControlRequired.PUT("/integrations/webhook/:id/", webhookIntegration.Update) diff --git a/router/project_router.go b/router/project_router.go index d3f6ba6b8..86cda60e7 100644 --- a/router/project_router.go +++ b/router/project_router.go @@ -33,7 +33,6 @@ func NewProjectRouter( assetController *controllers.AssetController, dependencyProxyController *dependencyfirewall.DependencyProxyController, dependencyVulnController *controllers.DependencyVulnController, - policyController *controllers.PolicyController, releaseController *controllers.ReleaseController, statisticsController *controllers.StatisticsController, webhookIntegration *controllers.WebhookController, @@ -49,7 +48,6 @@ func NewProjectRouter( projectRouter := organizationGroup.Group.Group("/projects/:projectSlug", projectScopedRBAC(shared.ObjectProject, shared.ActionRead)) projectRouter.GET("/", projectController.Read) projectRouter.GET("/resources/", projectController.ListSubProjectsAndAssets) - projectRouter.GET("/policies/", policyController.GetProjectPolicies) projectRouter.GET("/dependency-vulns/", dependencyVulnController.ListByProjectPaged) projectRouter.GET("/assets/", assetController.List) projectRouter.GET("/members/", projectController.Members) @@ -79,14 +77,12 @@ func NewProjectRouter( projectUpdateAccessControlRequired.POST("/releases/:releaseID/items/", releaseController.AddItem) projectUpdateAccessControlRequired.DELETE("/integrations/webhook/:id/", webhookIntegration.Delete) - projectUpdateAccessControlRequired.DELETE("/policies/:policyID/", policyController.DisablePolicyForProject) projectUpdateAccessControlRequired.DELETE("/", projectController.Delete) projectUpdateAccessControlRequired.DELETE("/members/:userID/", projectController.RemoveMember) projectUpdateAccessControlRequired.DELETE("/releases/:releaseID/", releaseController.Delete) projectUpdateAccessControlRequired.DELETE("/releases/:releaseID/items/:itemID/", releaseController.RemoveItem) projectUpdateAccessControlRequired.PUT("/integrations/webhook/:id/", webhookIntegration.Update) - projectUpdateAccessControlRequired.PUT("/policies/:policyID/", policyController.EnablePolicyForProject) projectUpdateAccessControlRequired.PATCH("/", projectController.Update) projectUpdateAccessControlRequired.PUT("/members/:userID/", projectController.ChangeRole) projectUpdateAccessControlRequired.PATCH("/releases/:releaseID/", releaseController.Update) diff --git a/router/providers.go b/router/providers.go index f2f799529..3a45d6ba5 100644 --- a/router/providers.go +++ b/router/providers.go @@ -10,6 +10,7 @@ var RouterModule = fx.Options( fx.Provide(NewDependencyVulnRouter), fx.Provide(NewFirstPartyVulnRouter), fx.Provide(NewLicenseRiskRouter), + fx.Provide(NewComplianceRiskRouter), fx.Provide(NewOrgRouter), fx.Provide(NewProjectRouter), fx.Provide(NewSessionRouter), diff --git a/router/router_test.go b/router/router_test.go index 7b7251bba..c9356c32e 100644 --- a/router/router_test.go +++ b/router/router_test.go @@ -84,15 +84,15 @@ var intentionallyPublicPaths = map[string]bool{ // Key format: "METHOD /full/echo/path/template/" var memberOnlyPaths = map[string]bool{ // Vuln triage actions — any member who can read the asset version may triage findings. - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/sync/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/batch/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/:dependencyVulnID/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/:dependencyVulnID/mitigate/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/first-party-vulns/:firstPartyVulnID/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/first-party-vulns/:firstPartyVulnID/mitigate/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/:licenseRiskID/": true, - "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/:licenseRiskID/mitigate/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/sync/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/batch/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/:dependencyVulnID/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/dependency-vulns/:dependencyVulnID/mitigate/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/first-party-vulns/:firstPartyVulnID/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/first-party-vulns/:firstPartyVulnID/mitigate/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/:licenseRiskID/": true, + "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/:licenseRiskID/mitigate/": true, "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/license-risks/:licenseRiskID/final-license-decision/": true, // VEX rules — any member may create/edit/delete VEX rules (membership = passed read RBAC). "POST /api/v1/organizations/:organization/projects/:project/assets/:assetSlug/refs/:assetVersionSlug/vex-rules/": true, @@ -219,7 +219,6 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { new(dependencyfirewall.DependencyProxyController), new(controllers.DependencyVulnController), new(controllers.FirstPartyVulnController), - new(controllers.PolicyController), new(controllers.IntegrationController), new(controllers.WebhookController), extEntityService, @@ -235,7 +234,6 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { new(controllers.AssetController), new(dependencyfirewall.DependencyProxyController), new(controllers.DependencyVulnController), - new(controllers.PolicyController), new(controllers.ReleaseController), new(controllers.StatisticsController), new(controllers.WebhookController), @@ -248,7 +246,6 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { new(controllers.AssetController), new(dependencyfirewall.DependencyProxyController), new(controllers.AssetVersionController), - new(controllers.ComplianceController), new(controllers.StatisticsController), new(controllers.ComponentController), new(controllers.InToToController), @@ -261,7 +258,6 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { assetRouter, new(controllers.AssetVersionController), new(controllers.FirstPartyVulnController), - new(controllers.ComplianceController), new(controllers.ComponentController), new(controllers.StatisticsController), new(controllers.AttestationController), @@ -280,7 +276,7 @@ func buildSecurityTestServer(t *testing.T, ac *mocks.AccessControl) *echo.Echo { NewFirstPartyVulnRouter(assetVersionRouter, new(controllers.FirstPartyVulnController), new(controllers.VulnEventController)) NewLicenseRiskRouter(assetVersionRouter, new(controllers.LicenseRiskController)) NewVEXRuleRouter(assetVersionRouter, new(controllers.VEXRuleController)) - NewArtifactRouter(assetVersionRouter, new(controllers.ArtifactController), new(controllers.ExternalReferenceController), artifactRepo, assetRepo) + NewArtifactRouter(assetVersionRouter, new(controllers.ArtifactController), new(controllers.ExternalReferenceController), new(controllers.AttestationController), artifactRepo, assetRepo) NewExternalReferenceRouter(assetVersionRouter, new(controllers.ExternalReferenceController), assetRepo) return e diff --git a/services/attestation_service.go b/services/attestation_service.go new file mode 100644 index 000000000..213a972c7 --- /dev/null +++ b/services/attestation_service.go @@ -0,0 +1,102 @@ +// Copyright (C) 2026 l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package services + +import ( + "context" + "encoding/json" + "time" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/shared" +) + +const secondsPerHour = 3600.0 + +type AttestationService struct { + attestationRepository shared.AttestationRepository + statisticsRepository shared.StatisticsRepository +} + +var _ shared.AttestationService = (*AttestationService)(nil) + +func NewAttestationService(attestationRepository shared.AttestationRepository, statisticsRepository shared.StatisticsRepository) *AttestationService { + return &AttestationService{ + attestationRepository: attestationRepository, + statisticsRepository: statisticsRepository, + } +} + +func (s *AttestationService) GetByAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID) ([]models.Attestation, error) { + return s.attestationRepository.GetByAssetID(ctx, tx, assetID) +} + +func (s *AttestationService) GetByAssetVersionAndAssetID(ctx context.Context, tx shared.DB, assetID uuid.UUID, assetVersion string) ([]models.Attestation, error) { + return s.attestationRepository.GetByAssetVersionAndAssetID(ctx, tx, assetID, assetVersion) +} + +func (s *AttestationService) GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx shared.DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) { + return s.attestationRepository.GetByArtifactAndAssetVersionAndAssetID(ctx, tx, artifactName, assetVersion, assetID) +} + +func (s *AttestationService) Create(ctx context.Context, tx shared.DB, attestation *models.Attestation) error { + return s.attestationRepository.Create(ctx, tx, attestation) +} + +func (s *AttestationService) GenerateAndStoreDevguardAttestation(ctx context.Context, assetID uuid.UUID, assetVersionName string, artifactName string) error { + averages, err := s.statisticsRepository.AverageFixingTimes(ctx, nil, assetVersionName, assetID) + if err != nil { + return err + } + + attestationDTO := dtos.DevguardAssetAttestationDTO{ + Type: dtos.DevguardAssetAttestationPredicateType, + GeneratedAt: time.Now().UTC(), + SchemaVersion: "1.0.0", + MeanTimeToRemediate: dtos.MeanTimeToRemediateDTO{ + RiskLowAvgHours: averages.RiskAvgLow / secondsPerHour, + RiskMediumAvgHours: averages.RiskAvgMedium / secondsPerHour, + RiskHighAvgHours: averages.RiskAvgHigh / secondsPerHour, + RiskCriticalAvgHours: averages.RiskAvgCritical / secondsPerHour, + CVSSLowAvgHours: averages.CVSSAvgLow / secondsPerHour, + CVSSMediumAvgHours: averages.CVSSAvgMedium / secondsPerHour, + CVSSHighAvgHours: averages.CVSSAvgHigh / secondsPerHour, + CVSSCriticalAvgHours: averages.CVSSAvgCritical / secondsPerHour, + }, + } + + content, err := json.Marshal(attestationDTO) + if err != nil { + return err + } + + var contentMap map[string]any + if err := json.Unmarshal(content, &contentMap); err != nil { + return err + } + + attestation := models.Attestation{ + AssetID: assetID, + AssetVersionName: assetVersionName, + ArtifactName: artifactName, + PredicateType: dtos.DevguardAssetAttestationPredicateType, + Content: contentMap, + } + + return s.attestationRepository.Create(ctx, nil, &attestation) +} diff --git a/services/compliance_risk_service.go b/services/compliance_risk_service.go new file mode 100644 index 000000000..be2ea9288 --- /dev/null +++ b/services/compliance_risk_service.go @@ -0,0 +1,428 @@ +package services + +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/dtos/sarif" + "github.com/l3montree-dev/devguard/shared" + "github.com/l3montree-dev/devguard/statemachine" + "github.com/l3montree-dev/devguard/utils" +) + +type ComplianceRiskService struct { + complianceRiskRepository shared.ComplianceRiskRepository + vulnEventRepository shared.VulnEventRepository +} + +var _ shared.ComplianceRiskService = (*ComplianceRiskService)(nil) + +func NewComplianceRiskService(complianceRiskRepository shared.ComplianceRiskRepository, vulnEventRepository shared.VulnEventRepository) *ComplianceRiskService { + return &ComplianceRiskService{ + complianceRiskRepository: complianceRiskRepository, + vulnEventRepository: vulnEventRepository, + } +} + +// HandleArtifactCompliance processes a SARIF compliance report for an artifact and manages the +// lifecycle of compliance risks: new detections, branch-diffing, artifact association, and fixes. +func (s *ComplianceRiskService) HandleArtifactCompliance(ctx context.Context, tx shared.DB, userID string, userAgent *string, assetVersion models.AssetVersion, artifact models.Artifact, sarifDoc sarif.SarifSchema210Json) error { + // fetch all existing compliance risks for this asset version (across all artifacts) + existingRisks, err := s.complianceRiskRepository.GetAllComplianceRisksForAssetVersion(ctx, tx, assetVersion.AssetID, assetVersion.Name) + if err != nil { + return err + } + + foundRisks := sarifToComplianceRisks(sarifDoc, assetVersion) + + // compare found risks with existing ones using hash-based identity + comparison := utils.CompareSlices(foundRisks, existingRisks, func(r models.ComplianceRisk) string { + return r.CalculateHash().String() + }) + + newRisks := comparison.OnlyInA + fixedRisks := comparison.OnlyInB + inBoth := comparison.InBoth + + // get risks from other branches for branch-diffing of new detections + existingRisksOnOtherBranch, err := s.complianceRiskRepository.GetComplianceRisksByOtherAssetVersions(ctx, tx, assetVersion.Name, assetVersion.AssetID) + if err != nil { + slog.Error("could not get existing compliance risks on other branches", "err", err) + return err + } + existingRisksOnOtherBranch = utils.Filter(existingRisksOnOtherBranch, func(r models.ComplianceRisk) bool { + return r.State != dtos.VulnStateFixed + }) + + // branch-diff new risks + branchDiff := statemachine.DiffVulnsBetweenBranches( + utils.Map(newRisks, utils.Ptr), + utils.Map(existingRisksOnOtherBranch, utils.Ptr), + ) + + // determine which "fixed" risks are truly fixed everywhere vs just removed from this artifact + existingNeedsAssoc := make([]models.ComplianceRisk, 0) + for _, r := range inBoth { + alreadyAssoc := utils.Any(r.Artifacts, func(a models.Artifact) bool { + return a.ArtifactName == artifact.ArtifactName + }) + if !alreadyAssoc { + existingNeedsAssoc = append(existingNeedsAssoc, r) + } + } + + existingNeedsDissoc := make([]models.ComplianceRisk, 0) + finallyFixed := make([]models.ComplianceRisk, 0) + for _, r := range fixedRisks { + if len(r.Artifacts) > 1 { + existingNeedsDissoc = append(existingNeedsDissoc, r) + } else if len(r.Artifacts) == 1 && r.Artifacts[0].ArtifactName != artifact.ArtifactName { + existingNeedsDissoc = append(existingNeedsDissoc, r) + } else { + finallyFixed = append(finallyFixed, r) + } + } + + return s.complianceRiskRepository.Transaction(ctx, func(db shared.DB) error { + // update policy/evidence metadata + if err := s.complianceRiskRepository.SaveBatch(ctx, db, inBoth); err != nil { + return err + } + + // risks that exist on other branches: copy event history + if err := s.UserDetectedExistingComplianceRiskOnDifferentBranch(ctx, db, artifact.ArtifactName, branchDiff.ExistingOnOtherBranches, assetVersion); err != nil { + slog.Error("error processing existing compliance risk on different branch", "err", err) + return err + } + + // brand-new risks never seen before + newToAllBranches := utils.Map(utils.DereferenceSlice(branchDiff.NewToAllBranches), func(r models.ComplianceRisk) models.ComplianceRisk { return r }) + if err := s.UserDetectedComplianceRisks(ctx, db, userID, userAgent, assetVersion.Name, artifact.ArtifactName, newToAllBranches); err != nil { + return err + } + + // risks now fixed everywhere + if err := s.UserFixedComplianceRisks(ctx, db, userID, userAgent, finallyFixed); err != nil { + return err + } + + // risks seen in this artifact for the first time (already exist in other artifacts) + if err := s.UserDetectedComplianceRiskInAnotherArtifact(ctx, db, existingNeedsAssoc, artifact.ArtifactName); err != nil { + return err + } + + // risks no longer seen in this artifact (still exists in others) + if err := s.UserDidNotDetectComplianceRiskInArtifactAnymore(ctx, db, existingNeedsDissoc, artifact.ArtifactName); err != nil { + return err + } + + return nil + }) +} + +func (s *ComplianceRiskService) UserDetectedExistingComplianceRiskOnDifferentBranch(ctx context.Context, tx shared.DB, artifactName string, matches []statemachine.BranchVulnMatch[*models.ComplianceRisk], assetVersion models.AssetVersion) error { + if len(matches) == 0 { + return nil + } + + risks := utils.Map(matches, func(m statemachine.BranchVulnMatch[*models.ComplianceRisk]) models.ComplianceRisk { + r := *m.CurrentBranchVuln + r.Artifacts = append(r.Artifacts, models.Artifact{ + ArtifactName: artifactName, + AssetVersionName: assetVersion.Name, + AssetID: assetVersion.AssetID, + }) + return r + }) + events := utils.Map(matches, func(m statemachine.BranchVulnMatch[*models.ComplianceRisk]) []models.VulnEvent { + return m.EventsToCopy + }) + + if err := s.complianceRiskRepository.SaveBatch(ctx, tx, risks); err != nil { + return err + } + return s.vulnEventRepository.SaveBatch(ctx, tx, utils.Flat(events)) +} + +func (s *ComplianceRiskService) UserDetectedComplianceRisks(ctx context.Context, tx shared.DB, userID string, userAgent *string, assetVersionName, artifactName string, risks []models.ComplianceRisk) error { + if len(risks) == 0 { + return nil + } + events := make([]models.VulnEvent, len(risks)) + for i := range risks { + risks[i].Artifacts = append(risks[i].Artifacts, models.Artifact{ + ArtifactName: artifactName, + AssetVersionName: assetVersionName, + AssetID: risks[i].AssetID, + }) + ev := models.NewDetectedEvent(risks[i].CalculateHash(), dtos.VulnTypeComplianceRisk, userID, dtos.RiskCalculationReport{}, artifactName, false, userAgent) + statemachine.Apply(&risks[i], ev) + events[i] = ev + } + if err := s.complianceRiskRepository.SaveBatch(ctx, tx, risks); err != nil { + return err + } + return s.vulnEventRepository.SaveBatch(ctx, tx, events) +} + +func (s *ComplianceRiskService) UserFixedComplianceRisks(ctx context.Context, tx shared.DB, userID string, userAgent *string, risks []models.ComplianceRisk) error { + if len(risks) == 0 { + return nil + } + events := make([]models.VulnEvent, len(risks)) + for i := range risks { + ev := models.NewFixedEvent(risks[i].CalculateHash(), dtos.VulnTypeComplianceRisk, userID, "", false, userAgent) + statemachine.Apply(&risks[i], ev) + events[i] = ev + } + if err := s.complianceRiskRepository.SaveBatch(ctx, tx, risks); err != nil { + return err + } + return s.vulnEventRepository.SaveBatch(ctx, tx, events) +} + +func (s *ComplianceRiskService) UserDetectedComplianceRiskInAnotherArtifact(ctx context.Context, tx shared.DB, risks []models.ComplianceRisk, artifactName string) error { + if len(risks) == 0 { + return nil + } + for i := range risks { + if err := tx.Exec( + "INSERT INTO artifact_compliance_risks (artifact_artifact_name, artifact_asset_version_name, artifact_asset_id, compliance_risk_id) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING", + artifactName, risks[i].AssetVersionName, risks[i].AssetID, risks[i].CalculateHash(), + ).Error; err != nil { + return err + } + } + return nil +} + +func (s *ComplianceRiskService) UserDidNotDetectComplianceRiskInArtifactAnymore(ctx context.Context, tx shared.DB, risks []models.ComplianceRisk, artifactName string) error { + if len(risks) == 0 { + return nil + } + for i := range risks { + if err := tx.Exec( + "DELETE FROM artifact_compliance_risks WHERE artifact_artifact_name = ? AND artifact_asset_version_name = ? AND artifact_asset_id = ? AND compliance_risk_id = ?", + artifactName, risks[i].AssetVersionName, risks[i].AssetID, risks[i].ID, + ).Error; err != nil { + return err + } + } + return nil +} + +func (s *ComplianceRiskService) UpdateComplianceRiskState(ctx context.Context, tx shared.DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string) (models.VulnEvent, error) { + if tx == nil { + var ev models.VulnEvent + var err error + err = s.complianceRiskRepository.Transaction(ctx, func(d shared.DB) error { + ev, err = s.updateComplianceRiskState(ctx, d, userID, risk, statusType, justification, mechanicalJustification, userAgent) + return err + }) + return ev, err + } + return s.updateComplianceRiskState(ctx, tx, userID, risk, statusType, justification, mechanicalJustification, userAgent) +} + +func (s *ComplianceRiskService) updateComplianceRiskState(ctx context.Context, tx shared.DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string) (models.VulnEvent, error) { + var ev models.VulnEvent + switch dtos.VulnEventType(statusType) { + case dtos.EventTypeAccepted: + ev = models.NewAcceptedEvent(risk.CalculateHash(), dtos.VulnTypeComplianceRisk, userID, justification, false, userAgent) + case dtos.EventTypeFalsePositive: + ev = models.NewFalsePositiveEvent(risk.CalculateHash(), dtos.VulnTypeComplianceRisk, userID, justification, mechanicalJustification, risk.GetArtifactNames(), false, userAgent) + case dtos.EventTypeReopened: + ev = models.NewReopenedEvent(risk.CalculateHash(), dtos.VulnTypeComplianceRisk, userID, justification, false, userAgent) + case dtos.EventTypeComment: + ev = models.NewCommentEvent(risk.CalculateHash(), dtos.VulnTypeComplianceRisk, userID, justification, false, userAgent) + default: + return models.VulnEvent{}, fmt.Errorf("unsupported event type: %s", statusType) + } + err := s.complianceRiskRepository.ApplyAndSave(ctx, tx, risk, &ev) + return ev, err +} + +// sarifToComplianceRisks converts a SARIF document into ComplianceRisk models for the given asset version. +// Each SARIF rule becomes one risk; its state is derived from the result kinds (pass/fail/open). +func sarifToComplianceRisks(sarifDoc sarif.SarifSchema210Json, assetVersion models.AssetVersion) []models.ComplianceRisk { + if len(sarifDoc.Runs) == 0 { + return nil + } + + risks := make([]models.ComplianceRisk, 0) + for _, run := range sarifDoc.Runs { + + type ruleInfo struct { + title string + description *string + relatedResources []string + tags []string + priority int + policyFrameworks []models.PolicyFrameworks + } + ruleMap := make(map[string]ruleInfo, len(run.Tool.Driver.Rules)) + for _, rule := range run.Tool.Driver.Rules { + var desc *string + if rule.FullDescription != nil && rule.FullDescription.Text != "" { + d := rule.FullDescription.Text + desc = &d + } + + title := rule.ID + if rule.ShortDescription != nil { + title = rule.ShortDescription.Text + } + + var tags []string + if rule.Properties != nil { + tags = rule.Properties.Tags + } + + relatedResources := make([]string, 0) + if rule.Properties != nil { + if rr, ok := rule.Properties.AdditionalProperties["relatedResources"].([]string); ok { + relatedResources = rr + } else if rr, ok := rule.Properties.AdditionalProperties["relatedResources"].([]any); ok { + for _, r := range rr { + if s, ok := r.(string); ok { + relatedResources = append(relatedResources, s) + } + } + } + } + + var policyFrameworks []models.PolicyFrameworks + if rule.Properties != nil { + if direct, ok := rule.Properties.AdditionalProperties["policyFrameworks"].([]models.PolicyFrameworks); ok { + policyFrameworks = direct + } else if cf, ok := rule.Properties.AdditionalProperties["policyFrameworks"].([]any); ok { + for _, c := range cf { + if cMap, ok := c.(map[string]any); ok { + pc := models.PolicyFrameworks{} + if fw, ok := cMap["framework"].(string); ok { + pc.Framework = fw + } + if ctls, ok := cMap["controls"].([]any); ok { + for _, ctl := range ctls { + if s, ok := ctl.(string); ok { + pc.Controls = append(pc.Controls, s) + } + } + } + policyFrameworks = append(policyFrameworks, pc) + } + } + } + } + + var priority int + if rule.Properties != nil { + if p, ok := rule.Properties.AdditionalProperties["priority"].(int); ok { + priority = p + } else if pFloat, ok := rule.Properties.AdditionalProperties["priority"].(float64); ok { + priority = int(pFloat) + } + } + + ruleMap[rule.ID] = ruleInfo{title: title, description: desc, relatedResources: relatedResources, tags: tags, priority: priority, policyFrameworks: policyFrameworks} + } + + type policyResult struct { + kind sarif.ResultKind + message sarif.Message + violations []string + evidenceContent []byte + evidenceType string + } + resultMap := make(map[string]*policyResult, len(ruleMap)) + + for _, result := range run.Results { + if result.RuleID == nil { + continue + } + ruleID := *result.RuleID + pr := resultMap[ruleID] + if pr == nil { + pr = &policyResult{} + resultMap[ruleID] = pr + } + + pr.message = result.Message + + if result.Properties != nil { + if ac, ok := result.Properties.AdditionalProperties["evidenceContent"].(string); ok && pr.evidenceContent == nil { + pr.evidenceContent = []byte(ac) + } + if et, ok := result.Properties.AdditionalProperties["evidenceType"].(string); ok { + pr.evidenceType = et + } + if v, ok := result.Properties.AdditionalProperties["violations"].([]string); ok { + pr.violations = v + } + } + + switch result.Kind { + case sarif.ResultKindFail: + pr.kind = sarif.ResultKindFail + case sarif.ResultKindOpen: + if pr.kind != sarif.ResultKindFail { + pr.kind = sarif.ResultKindOpen + } + case sarif.ResultKindPass: + if pr.kind == "" { + pr.kind = sarif.ResultKindPass + } + } + + } + + for ruleID, info := range ruleMap { + state := dtos.VulnStateOpen + var violations []string + var evidenceContent []byte + var evidenceType string + var message string + + if pr := resultMap[ruleID]; pr != nil { + evidenceContent = pr.evidenceContent + switch pr.kind { + case sarif.ResultKindPass: + state = dtos.VulnStateFixed + case sarif.ResultKindFail: + state = dtos.VulnStateOpen + violations = pr.violations + } + evidenceType = pr.evidenceType + message = pr.message.Text + + } + + risks = append(risks, models.ComplianceRisk{ + Vulnerability: models.Vulnerability{ + AssetVersionName: assetVersion.Name, + AssetID: assetVersion.AssetID, + AssetVersion: assetVersion, + State: state, + LastDetected: time.Now(), + }, + PolicyID: ruleID, + PolicyTitle: info.title, + PolicyDescription: info.description, + PolicyRelatedResources: info.relatedResources, + PolicyTags: info.tags, + PolicyPriority: info.priority, + PolicyFrameworks: info.policyFrameworks, + EvidenceType: evidenceType, + Violations: violations, + EvidenceContent: evidenceContent, + Message: message, + }) + } + } + + return risks +} diff --git a/services/compliance_service.go b/services/compliance_service.go new file mode 100644 index 000000000..41f4cbe86 --- /dev/null +++ b/services/compliance_service.go @@ -0,0 +1,63 @@ +// Copyright (C) 2025 l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +package services + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/compliance" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos/sarif" + "github.com/l3montree-dev/devguard/shared" +) + +type ComplianceService struct { + attestationRepository shared.AttestationRepository +} + +func NewComplianceService(attestationRepository shared.AttestationRepository) *ComplianceService { + return &ComplianceService{ + attestationRepository: attestationRepository, + } +} + +func (s *ComplianceService) EvaluateArtifactAttestations(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion, artifact models.Artifact) (sarif.SarifSchema210Json, error) { + attestations, err := s.attestationRepository.GetByArtifactAndAssetVersionAndAssetID(ctx, nil, artifact.ArtifactName, assetVersion.Name, assetVersion.AssetID) + if err != nil { + return sarif.SarifSchema210Json{}, err + } + + policies, err := compliance.GetPoliciesFromFS("attestation-compliance-policies/policies") + if err != nil { + return sarif.SarifSchema210Json{}, err + } + + evals := make([]compliance.PolicyEvaluation, 0, len(policies)) +foundMatch: + for _, policy := range policies { + for _, attestation := range attestations { + if attestation.PredicateType != policy.PredicateType { + continue + } + eval := compliance.Eval(policy, attestation.Content) + evals = append(evals, eval) + continue foundMatch + } + evals = append(evals, compliance.Eval(policy, nil)) + } + + return compliance.BuildSarifFromPoliciesEvaluations("", evals), nil +} diff --git a/services/dependency_vuln_service.go b/services/dependency_vuln_service.go index af679e783..9caf35de2 100644 --- a/services/dependency_vuln_service.go +++ b/services/dependency_vuln_service.go @@ -340,6 +340,8 @@ func (s *DependencyVulnService) createVulnEventAndApply(ctx context.Context, tx ev = models.NewCommentEvent(dependencyVuln.CalculateHash(), dtos.VulnTypeDependencyVuln, userID, justification, false, userAgent) case dtos.EventTypeFixed: ev = models.NewFixedEvent(dependencyVuln.CalculateHash(), dtos.VulnTypeDependencyVuln, userID, dependencyVuln.GetScannerIDsOrArtifactNames(), false, userAgent) + default: + return models.VulnEvent{}, fmt.Errorf("unsupported event type: %s", vulnEventType) } // Apply the event to the original vuln diff --git a/services/external_entity_provider_service.go b/services/external_entity_provider_service.go index 986dc98f8..aff440b0a 100644 --- a/services/external_entity_provider_service.go +++ b/services/external_entity_provider_service.go @@ -136,10 +136,6 @@ func (s externalEntityProviderService) RefreshExternalEntityProviderProjects(ctx return nil, err } - if err := s.enableCommunityPoliciesForNewProjects(ctx.Request().Context(), created); err != nil { - return nil, err - } - projectsMap := s.createProjectsMap(ctx.Request().Context(), created, updated) assets, err := s.syncProjectsAndAssets(ctx, domainRBAC, user, projects, roles, append(created, updated...)) @@ -199,16 +195,6 @@ func (s externalEntityProviderService) upsertProjects(ctx context.Context, org m return created, updated, nil } -func (s externalEntityProviderService) enableCommunityPoliciesForNewProjects(ctx context.Context, created []models.Project) error { - for _, project := range created { - if err := s.projectRepository.EnableCommunityManagedPolicies(ctx, nil, project.ID); err != nil { - return fmt.Errorf("could not enable community managed policies for project %s: %w", project.Slug, err) - } - slog.Info("enabled community managed policies for project", "projectSlug", project.Slug, "projectID", project.ID) - } - return nil -} - func (s externalEntityProviderService) createProjectsMap(ctx context.Context, created, updated []models.Project) map[string]struct{} { projectsMap := make(map[string]struct{}, len(created)+len(updated)) for _, project := range append(created, updated...) { diff --git a/services/external_entity_provider_service_test.go b/services/external_entity_provider_service_test.go index 0d21b65d4..e2a48d83b 100644 --- a/services/external_entity_provider_service_test.go +++ b/services/external_entity_provider_service_test.go @@ -191,41 +191,6 @@ func TestUpsertProjects(t *testing.T) { }) } -func TestEnableCommunityPoliciesForNewProjects(t *testing.T) { - t.Run("successful enable", func(t *testing.T) { - projectRepo := mocks.NewProjectRepository(t) - service := createTestServiceWithRepo(t, projectRepo) - - projects := []models.Project{ - {Model: models.Model{ID: uuid.New()}, Slug: "project1"}, - {Model: models.Model{ID: uuid.New()}, Slug: "project2"}, - } - - for _, project := range projects { - projectRepo.On("EnableCommunityManagedPolicies", mock.Anything, mock.Anything, project.ID).Return(nil) - } - - err := service.enableCommunityPoliciesForNewProjects(context.Background(), projects) - - assert.NoError(t, err) - projectRepo.AssertExpectations(t) - }) - - t.Run("repository error", func(t *testing.T) { - projectRepo := mocks.NewProjectRepository(t) - service := createTestServiceWithRepo(t, projectRepo) - - projects := []models.Project{{Model: models.Model{ID: uuid.New()}, Slug: "project1"}} - - projectRepo.On("EnableCommunityManagedPolicies", mock.Anything, mock.Anything, projects[0].ID).Return(errors.New("policy error")) - - err := service.enableCommunityPoliciesForNewProjects(context.Background(), projects) - - assert.Error(t, err) - assert.Contains(t, err.Error(), "could not enable community managed policies for project project1") - }) -} - func TestSyncOrgs(t *testing.T) { t.Run("successful sync with new organizations", func(t *testing.T) { ctx := createTestContext() diff --git a/services/first_party_vuln_service.go b/services/first_party_vuln_service.go index 6d4ac7c32..8138abe24 100644 --- a/services/first_party_vuln_service.go +++ b/services/first_party_vuln_service.go @@ -120,6 +120,8 @@ func (s *firstPartyVulnService) updateFirstPartyVulnState(ctx context.Context, t ev = models.NewReopenedEvent(firstPartyVuln.CalculateHash(), dtos.VulnTypeFirstPartyVuln, userID, justification, false, userAgent) case dtos.EventTypeComment: ev = models.NewCommentEvent(firstPartyVuln.CalculateHash(), dtos.VulnTypeFirstPartyVuln, userID, justification, false, userAgent) + default: + return models.VulnEvent{}, fmt.Errorf("unsupported event type: %s", statusType) } return s.applyAndSave(ctx, tx, firstPartyVuln, &ev) diff --git a/services/license_risk_service.go b/services/license_risk_service.go index a8435b008..98fa98710 100644 --- a/services/license_risk_service.go +++ b/services/license_risk_service.go @@ -2,6 +2,7 @@ package services import ( "context" + "fmt" "log/slog" "slices" "strings" @@ -403,6 +404,8 @@ func (s *LicenseRiskService) updateLicenseRiskState(ctx context.Context, tx shar ev = models.NewReopenedEvent(licenseRisk.CalculateHash(), dtos.VulnTypeLicenseRisk, userID, justification, false, userAgent) case dtos.EventTypeComment: ev = models.NewCommentEvent(licenseRisk.CalculateHash(), dtos.VulnTypeLicenseRisk, userID, justification, false, userAgent) + default: + return models.VulnEvent{}, fmt.Errorf("unsupported event type: %s", statusType) } err := s.licenseRiskRepository.ApplyAndSave(ctx, tx, licenseRisk, &ev) diff --git a/services/project_service.go b/services/project_service.go index be2575273..4fbfbc4bf 100644 --- a/services/project_service.go +++ b/services/project_service.go @@ -61,9 +61,7 @@ func (s *projectService) CreateProject(ctx shared.Context, project *models.Proje return echo.NewHTTPError(500, "could not create project").WithInternal(err) } } - - // enable the default community policies - return s.projectRepository.EnableCommunityManagedPolicies(ctx.Request().Context(), tx, newProject.ID) + return nil }) if err != nil { slog.Error("could not create project", "err", err, "projectSlug", project.Slug, "projectID", project.ID) diff --git a/services/providers.go b/services/providers.go index 479d5824e..79117efb5 100644 --- a/services/providers.go +++ b/services/providers.go @@ -18,6 +18,8 @@ var ServiceModule = fx.Options( fx.Provide(fx.Annotate(NewConfigService, fx.As(new(shared.ConfigService)))), fx.Provide(fx.Annotate(NewFirstPartyVulnService, fx.As(new(shared.FirstPartyVulnService)))), fx.Provide(fx.Annotate(NewLicenseRiskService, fx.As(new(shared.LicenseRiskService)))), + fx.Provide(fx.Annotate(NewComplianceService, fx.As(new(shared.ComplianceService)))), + fx.Provide(fx.Annotate(NewComplianceRiskService, fx.As(new(shared.ComplianceRiskService)))), fx.Provide(fx.Annotate(NewProjectService, fx.As(new(shared.ProjectService)))), fx.Provide(fx.Annotate(NewAssetService, fx.As(new(shared.AssetService)))), fx.Provide(fx.Annotate(NewComponentService, fx.As(new(shared.ComponentService)))), @@ -25,6 +27,7 @@ var ServiceModule = fx.Options( fx.Provide(func() http.Client { return utils.EgressClient }), fx.Provide(fx.Annotate(NewCSAFService, fx.As(new(shared.CSAFService)))), fx.Provide(fx.Annotate(NewArtifactService, fx.As(new(shared.ArtifactService)))), + fx.Provide(fx.Annotate(NewAttestationService, fx.As(new(shared.AttestationService)))), fx.Provide(fx.Annotate(NewStatisticsService, fx.As(new(shared.StatisticsService)))), fx.Provide(fx.Annotate(NewInTotoService, fx.As(new(shared.InTotoVerifierService)))), fx.Provide(fx.Annotate(NewOrgService, fx.As(new(shared.OrgService)))), diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index 72a531708..53b16c601 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -44,7 +44,6 @@ type DaemonRunner interface { UpdateFixedVersions(ctx context.Context) error UpdateVulnDB(ctx context.Context) error UpdateOpenSourceInsightInformation(ctx context.Context) error - Start(ctx context.Context) } @@ -98,10 +97,7 @@ type ProjectRepository interface { GetByProjectIDs(ctx context.Context, tx DB, projectIDs []uuid.UUID) ([]models.Project, error) List(ctx context.Context, tx DB, idSlice []uuid.UUID, parentID *uuid.UUID, organizationID uuid.UUID) ([]models.Project, error) ListPaged(ctx context.Context, tx DB, projectIDs []uuid.UUID, parentID *uuid.UUID, orgID uuid.UUID, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[models.Project], error) - EnablePolicyForProject(ctx context.Context, tx DB, projectID uuid.UUID, policyID uuid.UUID) error - DisablePolicyForProject(ctx context.Context, tx DB, projectID uuid.UUID, policyID uuid.UUID) error Upsert(ctx context.Context, tx DB, projects *[]*models.Project, conflictingColumns []clause.Column, toUpdate []string) error - EnableCommunityManagedPolicies(ctx context.Context, tx DB, projectID uuid.UUID) error UpsertSplit(ctx context.Context, tx DB, externalProviderID string, projects []*models.Project) ([]*models.Project, []*models.Project, error) ListSubProjectsAndAssets(ctx context.Context, tx DB, allowedAssetIDs []string, allowedProjectIDs []uuid.UUID, parentID *uuid.UUID, orgID uuid.UUID, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[dtos.ProjectAssetDTO], error) SearchProjectsWithSubProjectsAndAssetsPaged(ctx context.Context, tx DB, allowedAssetIDs []string, allowedProjectIDs []string, parentID *uuid.UUID, orgID uuid.UUID, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[dtos.ProjectDTO], error) @@ -112,13 +108,6 @@ type Verifier interface { VerifyRequestSignature(ctx context.Context, req *http.Request) (string, string, error) } -type PolicyRepository interface { - utils.Repository[uuid.UUID, models.Policy, DB] - FindByProjectID(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.Policy, error) - FindByOrganizationID(ctx context.Context, tx DB, organizationID uuid.UUID) ([]models.Policy, error) - FindCommunityManagedPolicies(ctx context.Context, tx DB) ([]models.Policy, error) -} - type DependencyProxySecretRepository interface { utils.Repository[uuid.UUID, models.DependencyProxySecret, DB] GetOrCreateByOrgID(ctx context.Context, tx DB, orgID uuid.UUID) (models.DependencyProxySecret, error) @@ -157,6 +146,16 @@ type AttestationRepository interface { utils.Repository[string, models.Attestation, DB] GetByAssetID(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.Attestation, error) GetByAssetVersionAndAssetID(ctx context.Context, tx DB, assetID uuid.UUID, assetVersion string) ([]models.Attestation, error) + GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) + Create(ctx context.Context, tx DB, attestation *models.Attestation) error +} + +type AttestationService interface { + GetByAssetID(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.Attestation, error) + GetByAssetVersionAndAssetID(ctx context.Context, tx DB, assetID uuid.UUID, assetVersion string) ([]models.Attestation, error) + GetByArtifactAndAssetVersionAndAssetID(ctx context.Context, tx DB, artifactName string, assetVersion string, assetID uuid.UUID) ([]models.Attestation, error) + Create(ctx context.Context, tx DB, attestation *models.Attestation) error + GenerateAndStoreDevguardAttestation(ctx context.Context, assetID uuid.UUID, assetVersionName string, artifactName string) error } type ArtifactRepository interface { @@ -169,6 +168,10 @@ type ArtifactRepository interface { CleanupOrphanedRecords(ctx context.Context) error } +type ComplianceService interface { + EvaluateArtifactAttestations(ctx context.Context, projectID uuid.UUID, assetVersion models.AssetVersion, artifact models.Artifact) (sarif.SarifSchema210Json, error) +} + type ReleaseRepository interface { utils.Repository[uuid.UUID, models.Release, DB] GetByProjectID(ctx context.Context, tx DB, projectID uuid.UUID) ([]models.Release, error) @@ -284,6 +287,22 @@ type LicenseRiskRepository interface { ApplyAndSave(ctx context.Context, tx DB, licenseRisk *models.LicenseRisk, vulnEvent *models.VulnEvent) error } +type ComplianceRiskRepository interface { + utils.Repository[uuid.UUID, models.ComplianceRisk, DB] + GetAllComplianceRisksForAssetVersion(ctx context.Context, tx DB, assetID uuid.UUID, assetVersionName string) ([]models.ComplianceRisk, error) + GetAllComplianceRisksForAssetVersionPaged(ctx context.Context, tx DB, assetID uuid.UUID, assetVersionName string, pageInfo PageInfo, search string, filter []FilterQuery, sort []SortQuery) (Paged[models.ComplianceRisk], error) + GetComplianceRisksByOtherAssetVersions(ctx context.Context, tx DB, assetVersionName string, assetID uuid.UUID) ([]models.ComplianceRisk, error) + Read(ctx context.Context, tx DB, id uuid.UUID) (models.ComplianceRisk, error) + ApplyAndSave(ctx context.Context, tx DB, risk *models.ComplianceRisk, ev *models.VulnEvent) error + GetDistinctFrameworksForAssetVersion(ctx context.Context, tx DB, assetID uuid.UUID, assetVersionName string) ([]string, error) + SaveBatch(ctx context.Context, tx DB, risks []models.ComplianceRisk) error +} + +type ComplianceRiskService interface { + HandleArtifactCompliance(ctx context.Context, tx DB, userID string, userAgent *string, assetVersion models.AssetVersion, artifact models.Artifact, sarifDoc sarif.SarifSchema210Json) error + UpdateComplianceRiskState(ctx context.Context, tx DB, userID string, risk *models.ComplianceRisk, statusType string, justification string, mechanicalJustification dtos.MechanicalJustificationType, userAgent *string) (models.VulnEvent, error) +} + type InTotoLinkRepository interface { utils.Repository[uuid.UUID, models.InTotoLink, DB] FindByAssetAndSupplyChainID(ctx context.Context, tx DB, assetID uuid.UUID, supplyChainID string) ([]models.InTotoLink, error) diff --git a/shared/context_utils.go b/shared/context_utils.go index fc4807de5..72ce65560 100644 --- a/shared/context_utils.go +++ b/shared/context_utils.go @@ -168,6 +168,23 @@ func GetVulnID(ctx Context) (uuid.UUID, dtos.VulnType, error) { return id, dtos.VulnTypeLicenseRisk, nil } + ComplianceRiskID := ctx.Param("complianceRiskID") + if ComplianceRiskID != "" { + id, err := uuid.Parse(ComplianceRiskID) + if err != nil { + return uuid.Nil, "", fmt.Errorf("invalid compliance risk id: %w", err) + } + return id, dtos.VulnTypeComplianceRisk, nil + } + ComplianceRiskIDFromGet, ok := ctx.Get("complianceRiskID").(string) + if ok && ComplianceRiskIDFromGet != "" { + id, err := uuid.Parse(ComplianceRiskIDFromGet) + if err != nil { + return uuid.Nil, "", fmt.Errorf("invalid compliance risk id: %w", err) + } + return id, dtos.VulnTypeComplianceRisk, nil + } + return uuid.Nil, "", fmt.Errorf("could not get vuln id") } @@ -557,6 +574,11 @@ func (f FilterQuery) SQL() string { return field + " ILIKE ?" case "any": return "? = ANY(string_to_array(" + field + ", ' '))" + case "frameworkContains": + // Matches a JSONB array-of-objects column (e.g. policyFrameworks) where any + // element's "framework" key equals the value. Used by the compliance-risks + // framework filter. + return "EXISTS (SELECT 1 FROM jsonb_array_elements(" + field + ") AS e WHERE e->>'framework' = ?)" default: // default do an equals return field + " = ?" diff --git a/tests/fx_test_app.go b/tests/fx_test_app.go index 3dbceda13..f2e1054b1 100644 --- a/tests/fx_test_app.go +++ b/tests/fx_test_app.go @@ -96,6 +96,11 @@ type TestApp struct { VulnEventRepository shared.VulnEventRepository ComponentProjectRepository shared.ComponentProjectRepository StatisticsRepository shared.StatisticsRepository + AttestationRepository shared.AttestationRepository + AttestationService shared.AttestationService + ComplianceService shared.ComplianceService + ComplianceRiskService shared.ComplianceRiskService + ComplianceRiskRepository shared.ComplianceRiskRepository LicenseRiskRepository shared.LicenseRiskRepository GitLabOauth2TokenRepository shared.GitLabOauth2TokenRepository GitlabIntegrationRepository shared.GitlabIntegrationRepository diff --git a/tests/fx_test_helpers.go b/tests/fx_test_helpers.go index 5f8e4d2ed..074c6ba94 100644 --- a/tests/fx_test_helpers.go +++ b/tests/fx_test_helpers.go @@ -180,6 +180,10 @@ func (f *TestFixture) CreateDaemonRunner() *daemons.DaemonRunner { f.App.VulnDBService, f.App.VexRuleService, f.App.FixedVersionResolver, + f.App.AttestationService, + f.App.StatisticsRepository, + f.App.ComplianceService, + f.App.ComplianceRiskService, ) } diff --git a/tests/project_controller_test.go b/tests/project_controller_test.go index cc4355fc0..74e47fe5a 100644 --- a/tests/project_controller_test.go +++ b/tests/project_controller_test.go @@ -22,68 +22,6 @@ func TestProjectCreation(t *testing.T) { WithTestApp(t, "../initdb.sql", func(f *TestFixture) { org, project, _, _ := f.CreateOrgProjectAssetAndVersion() - t.Run("should enable all community policies by default", func(t *testing.T) { - e := echo.New() - rec := httptest.NewRecorder() - // create a community policy - communityPolicy := models.Policy{ - Title: "Community Policy 1", - Description: "This is a community policy", - OrganizationID: nil, // nil means it's a community policy - } - - assert.Nil(t, f.DB.Create(&communityPolicy).Error) - - requestBody := map[string]string{ - "name": "new-project", - "description": "This is a new project", - } - - b, err := json.Marshal(requestBody) - assert.Nil(t, err) - - req := httptest.NewRequest("POST", "/projects", bytes.NewBuffer(b)) - req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) - ctx := e.NewContext(req, rec) - shared.SetOrg(ctx, org) - session := mocks.NewAuthSession(t) - shared.SetSession(ctx, session) - rbac := mocks.NewAccessControl(t) - rbac.On("LinkDomainAndProjectRole", mock.Anything, shared.RoleAdmin, shared.RoleAdmin, mock.Anything).Return(nil) - rbac.On("InheritProjectRole", mock.Anything, shared.RoleAdmin, shared.RoleMember, mock.Anything).Return(nil) - rbac.On("AllowRoleInProject", mock.Anything, mock.Anything, shared.RoleAdmin, shared.ObjectUser, []shared.Action{ - shared.ActionCreate, - shared.ActionDelete, - shared.ActionUpdate, - }).Return(nil) - rbac.On("AllowRoleInProject", mock.Anything, mock.Anything, shared.RoleAdmin, shared.ObjectAsset, []shared.Action{ - shared.ActionCreate, - shared.ActionDelete, - shared.ActionUpdate, - }).Return(nil) - rbac.On("AllowRoleInProject", mock.Anything, mock.Anything, shared.RoleAdmin, shared.ObjectProject, []shared.Action{ - shared.ActionDelete, - shared.ActionUpdate, - }).Return(nil) - rbac.On("AllowRoleInProject", mock.Anything, mock.Anything, shared.RoleMember, shared.ObjectProject, []shared.Action{ - shared.ActionRead, - }).Return(nil) - rbac.On("AllowRoleInProject", mock.Anything, mock.Anything, shared.RoleMember, shared.ObjectAsset, []shared.Action{ - shared.ActionRead, - }).Return(nil) - - shared.SetRBAC(ctx, rbac) - - err = f.App.ProjectController.Create(ctx) - assert.Nil(t, err) - - var createdProject models.Project - err = f.DB.Preload("EnabledPolicies").First(&createdProject, "slug = ?", requestBody["name"]).Error - - assert.Nil(t, err) - assert.Len(t, createdProject.EnabledPolicies, 1) - }) - t.Run("should generate a unique slug", func(t *testing.T) { // create a new project with the same name and slug as the existing project e := echo.New() diff --git a/transformer/compliance_risk_transformer.go b/transformer/compliance_risk_transformer.go new file mode 100644 index 000000000..c8b1f59a0 --- /dev/null +++ b/transformer/compliance_risk_transformer.go @@ -0,0 +1,47 @@ +package transformer + +import ( + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" +) + +func ComplianceRiskToDTO(r models.ComplianceRisk) dtos.ComplianceRiskDTO { + artifacts := make([]dtos.ArtifactDTO, len(r.Artifacts)) + for i, a := range r.Artifacts { + artifacts[i] = dtos.ArtifactDTO{ + ArtifactName: a.ArtifactName, + AssetVersionName: a.AssetVersionName, + AssetID: a.AssetID, + } + } + + policyFrameworks := make([]dtos.PolicyFrameworks, len(r.PolicyFrameworks)) + for i, pf := range r.PolicyFrameworks { + policyFrameworks[i] = dtos.PolicyFrameworks{ + Framework: pf.Framework, + Controls: pf.Controls, + } + } + + return dtos.ComplianceRiskDTO{ + ID: r.ID, + AssetVersionName: r.AssetVersionName, + AssetID: r.AssetID.String(), + State: r.State, + CreatedAt: r.CreatedAt, + TicketID: r.TicketID, + TicketURL: r.TicketURL, + ManualTicketCreation: r.ManualTicketCreation, + PolicyID: r.PolicyID, + PolicyTitle: r.PolicyTitle, + PolicyDescription: r.PolicyDescription, + PolicyRelatedResources: r.PolicyRelatedResources, + PolicyTags: r.PolicyTags, + PolicyPriority: r.PolicyPriority, + PolicyFrameworks: policyFrameworks, + EvidenceType: r.EvidenceType, + Violations: r.Violations, + Artifacts: artifacts, + Message: r.Message, + } +}