diff --git a/internal/eval.go b/internal/eval.go index 9fe74b2..31ac5bf 100644 --- a/internal/eval.go +++ b/internal/eval.go @@ -2,11 +2,8 @@ package internal import ( "context" - "encoding/json" "errors" "fmt" - "os" - "path/filepath" "strings" policyManager "github.com/compliance-framework/agent/policy-manager" @@ -152,41 +149,6 @@ func certificateBaseLabels() map[string]string { } } -// LoadBundleRootData reads data.json from the OPA bundle root and merges it -// with overrides. When the agent downloads a policy OCI artifact it returns the -// policies/ subdirectory as policyPath; the bundle's data.json lives one level -// up in the bundle root. For local source trees the data.json lives inside the -// policies/ directory itself, so both locations are checked. overrides win on -// conflict, so operator-supplied policy_data takes precedence over bundle defaults. -func LoadBundleRootData(policyPath string, overrides map[string]interface{}) (map[string]interface{}, error) { - candidates := []string{ - filepath.Join(policyPath, "data.json"), - filepath.Join(filepath.Dir(policyPath), "data.json"), - } - for _, p := range candidates { - raw, err := os.ReadFile(p) - if errors.Is(err, os.ErrNotExist) { - continue - } - if err != nil { - return nil, fmt.Errorf("reading bundle data %s: %w", p, err) - } - var bundleData map[string]interface{} - if err := json.Unmarshal(raw, &bundleData); err != nil { - return nil, fmt.Errorf("parsing bundle data %s: %w", p, err) - } - merged := make(map[string]interface{}, len(bundleData)+len(overrides)) - for k, v := range bundleData { - merged[k] = v - } - for k, v := range overrides { - merged[k] = v - } - return merged, nil - } - return overrides, nil -} - // arnCertID extracts the certificate UUID from an ACM ARN. // ARN format: arn:aws:acm:::certificate/ func arnCertID(arn string) string { diff --git a/internal/eval_test.go b/internal/eval_test.go deleted file mode 100644 index 1edadb8..0000000 --- a/internal/eval_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package internal - -import ( - "os" - "path/filepath" - "testing" -) - -func TestLoadBundleRootData_OverridesWinOverBundleDefaults(t *testing.T) { - dir := t.TempDir() - bundleJSON := `{"expiry_warning_days":30,"required_certificate_tags":["Environment"]}` - if err := os.WriteFile(filepath.Join(dir, "data.json"), []byte(bundleJSON), 0644); err != nil { - t.Fatal(err) - } - - overrides := map[string]interface{}{ - "expiry_warning_days": float64(90), - } - - result, err := LoadBundleRootData(dir, overrides) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - // Operator override (90) must win over bundle default (30). - if got := result["expiry_warning_days"]; got != float64(90) { - t.Errorf("expiry_warning_days: got %v, want 90", got) - } - - // Bundle key absent from overrides must still be present. - if _, ok := result["required_certificate_tags"]; !ok { - t.Error("required_certificate_tags from bundle missing from merged result") - } -} - -func TestLoadBundleRootData_NoBundleDataJsonReturnsOverrides(t *testing.T) { - dir := t.TempDir() // no data.json written - - overrides := map[string]interface{}{ - "expiry_warning_days": float64(60), - } - - result, err := LoadBundleRootData(dir, overrides) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if got := result["expiry_warning_days"]; got != float64(60) { - t.Errorf("expiry_warning_days: got %v, want 60", got) - } -} - -func TestLoadBundleRootData_PolicyPathDataJsonWinsOverParent(t *testing.T) { - root := t.TempDir() - parentJSON := `{"expiry_warning_days":30,"source":"parent"}` - if err := os.WriteFile(filepath.Join(root, "data.json"), []byte(parentJSON), 0644); err != nil { - t.Fatal(err) - } - policiesDir := filepath.Join(root, "policies") - if err := os.Mkdir(policiesDir, 0755); err != nil { - t.Fatal(err) - } - policiesJSON := `{"expiry_warning_days":60,"source":"policyPath"}` - if err := os.WriteFile(filepath.Join(policiesDir, "data.json"), []byte(policiesJSON), 0644); err != nil { - t.Fatal(err) - } - - // When both data.json locations exist, policyPath/data.json must win. - result, err := LoadBundleRootData(policiesDir, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if got := result["source"]; got != "policyPath" { - t.Errorf("source: got %v, want policyPath", got) - } - if got := result["expiry_warning_days"]; got != float64(60) { - t.Errorf("expiry_warning_days: got %v, want 60", got) - } -} - -func TestLoadBundleRootData_FindsDataJsonOneDirectoryUp(t *testing.T) { - root := t.TempDir() - bundleJSON := `{"expiry_warning_days":30}` - if err := os.WriteFile(filepath.Join(root, "data.json"), []byte(bundleJSON), 0644); err != nil { - t.Fatal(err) - } - policiesDir := filepath.Join(root, "policies") - if err := os.Mkdir(policiesDir, 0755); err != nil { - t.Fatal(err) - } - - // policyPath is the policies/ subdirectory; data.json is one level up. - result, err := LoadBundleRootData(policiesDir, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if got := result["expiry_warning_days"]; got != float64(30) { - t.Errorf("expiry_warning_days: got %v, want 30", got) - } -} diff --git a/main.go b/main.go index eda4ddb..7842485 100644 --- a/main.go +++ b/main.go @@ -72,23 +72,12 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api }, fmt.Errorf("failed to fetch data: %w", err) } - // Load bundle data.json defaults and merge with operator overrides once per - // evaluation cycle. All policy paths share the same bundle so one load suffices. - policyData := l.policyData - if paths := request.GetPolicyPaths(); len(paths) > 0 { - merged, err := internal.LoadBundleRootData(paths[0], l.policyData) - if err != nil { - return &proto.EvalResponse{Status: proto.ExecutionStatus_FAILURE}, fmt.Errorf("loading bundle data for %s: %w", paths[0], err) - } - policyData = merged - } - policyEvaluator := internal.NewPolicyEvaluator(ctx, l.logger, activities) var allEvidences []*proto.Evidence var evalErrors error for _, cert := range certs { - certEvidences, err := policyEvaluator.Eval(ctx, cert, request.GetPolicyPaths(), policyData, l.config.PolicyLabels) + certEvidences, err := policyEvaluator.Eval(ctx, cert, request.GetPolicyPaths(), l.policyData, l.config.PolicyLabels) allEvidences = append(allEvidences, certEvidences...) if err != nil { evalErrors = errors.Join(evalErrors, fmt.Errorf("evaluating cert %s: %w", cert.CertificateArn, err))