From 13422a9cec87f848d8bea5ff819655e262360eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 18 Sep 2026 06:54:21 +0200 Subject: [PATCH 1/5] chore: replace interface{} with any Pure alias rename; any has been the idiomatic spelling since Go 1.18. The struct tag realignment is gofmt output from the shorter name. --- cmd/kosli/apiKey.go | 2 +- cmd/kosli/assertArtifact.go | 32 +- cmd/kosli/assertSnapshot.go | 2 +- cmd/kosli/attestCustom.go | 4 +- cmd/kosli/attestation.go | 10 +- cmd/kosli/attestation_test.go | 12 +- cmd/kosli/beginTrail.go | 2 +- cmd/kosli/beginTrailPayload_test.go | 14 +- cmd/kosli/cli_utils.go | 8 +- cmd/kosli/cli_utils_test.go | 2 +- cmd/kosli/createAttestationType.go | 4 +- cmd/kosli/createFlow.go | 2 +- cmd/kosli/createPolicy.go | 2 +- cmd/kosli/evaluateHelpers.go | 28 +- cmd/kosli/evaluateInput.go | 8 +- cmd/kosli/evaluateInput_test.go | 2 +- cmd/kosli/evaluatePolicy_test.go | 40 +-- cmd/kosli/evaluateServerSide_test.go | 42 +-- cmd/kosli/evaluateTrail.go | 2 +- cmd/kosli/evaluateTrails.go | 4 +- cmd/kosli/getArtifact.go | 16 +- cmd/kosli/getAttestationType.go | 16 +- cmd/kosli/getControl.go | 8 +- cmd/kosli/getEnvironment.go | 4 +- cmd/kosli/getFlow.go | 4 +- cmd/kosli/getPolicy.go | 8 +- cmd/kosli/getSnapshot_test.go | 2 +- cmd/kosli/getTrail.go | 48 +-- cmd/kosli/listAttestationTypes.go | 4 +- cmd/kosli/listControls.go | 8 +- cmd/kosli/listEnvironments.go | 20 +- cmd/kosli/listFlows.go | 8 +- cmd/kosli/listPolicies.go | 6 +- cmd/kosli/listSnapshots.go | 4 +- cmd/kosli/openapiContract_test.go | 4 +- cmd/kosli/reportArtifact.go | 2 +- cmd/kosli/search.go | 24 +- cmd/kosli/snapshotAutoEnvironment.go | 2 +- cmd/kosli/testHelpers.go | 4 +- cmd/kosli/updateControl.go | 2 +- cmd/kosli/updateServiceAccount.go | 2 +- internal/azure/azure_apps.go | 2 +- internal/evaluate/opa_contract_test.go | 50 +-- internal/evaluate/rego.go | 10 +- internal/evaluate/rego_test.go | 28 +- internal/evaluate/transform.go | 58 ++-- internal/evaluate/transform_test.go | 460 ++++++++++++------------- internal/evaluations/client.go | 12 +- internal/evaluations/client_test.go | 30 +- internal/github/github.go | 4 +- internal/gitview/gitView.go | 14 +- internal/logger/logger.go | 10 +- internal/requests/requests.go | 10 +- internal/requests/requests_test.go | 6 +- internal/snyk/snyk.go | 2 +- internal/utils/utils.go | 2 +- 56 files changed, 558 insertions(+), 558 deletions(-) diff --git a/cmd/kosli/apiKey.go b/cmd/kosli/apiKey.go index 9e48d851d..0dfd62274 100644 --- a/cmd/kosli/apiKey.go +++ b/cmd/kosli/apiKey.go @@ -132,7 +132,7 @@ func printApiKeyMetadataAsTable(raw string, out io.Writer, page int) error { // optionalTimestamp formats an epoch timestamp, returning "N/A" when it is // unset (nil, or a zero value meaning "never"/"not set"). -func optionalTimestamp(epoch interface{}) (string, error) { +func optionalTimestamp(epoch any) (string, error) { switch v := epoch.(type) { case nil: return "N/A", nil diff --git a/cmd/kosli/assertArtifact.go b/cmd/kosli/assertArtifact.go index b39b57700..854737834 100644 --- a/cmd/kosli/assertArtifact.go +++ b/cmd/kosli/assertArtifact.go @@ -168,7 +168,7 @@ func (o *assertArtifactOptions) run(out io.Writer, args []string) error { return err } - var evaluationResult map[string]interface{} + var evaluationResult map[string]any err = json.Unmarshal([]byte(response.Body), &evaluationResult) if err != nil { return err @@ -182,7 +182,7 @@ func (o *assertArtifactOptions) run(out io.Writer, args []string) error { } func printAssertAsTable(raw string, out io.Writer, page int) error { - var evaluationResult map[string]interface{} + var evaluationResult map[string]any err := json.Unmarshal([]byte(raw), &evaluationResult) if err != nil { return err @@ -201,29 +201,29 @@ func printAssertAsTable(raw string, out io.Writer, page int) error { logger.Info("Environment: %v", evaluationResult["environment"].(string)) } logger.Info("%-32v %-30v", "Policy-name", "status") - policyEvaluations := evaluationResult["policy_evaluations"].([]interface{}) + policyEvaluations := evaluationResult["policy_evaluations"].([]any) for _, item := range policyEvaluations { - policyEvaluation := item.(map[string]interface{}) + policyEvaluation := item.(map[string]any) policyName := policyEvaluation["policy_name"] policyStatus := policyEvaluation["status"] logger.Info(" %-32v %-30v", policyName, policyStatus) if policyStatus != "COMPLIANT" { - ruleEvaluations := policyEvaluation["rule_evaluations"].([]interface{}) + ruleEvaluations := policyEvaluation["rule_evaluations"].([]any) var failures []string for _, item2 := range ruleEvaluations { - ruleEvaluation := item2.(map[string]interface{}) + ruleEvaluation := item2.(map[string]any) ignored := ruleEvaluation["ignored"].(bool) satisfied, _ := ruleEvaluation["satisfied"].(bool) if !ignored && !satisfied { - rule := ruleEvaluation["rule"].(map[string]interface{}) - resolutions := ruleEvaluation["resolutions"].([]interface{}) + rule := ruleEvaluation["rule"].(map[string]any) + resolutions := ruleEvaluation["resolutions"].([]any) for _, item3 := range resolutions { - resolution := item3.(map[string]interface{}) + resolution := item3.(map[string]any) resolutionType := resolution["type"].(string) - ruleDefinition := rule["definition"].(map[string]interface{}) + ruleDefinition := rule["definition"].(map[string]any) attestationName := ruleDefinition["name"] attestationType := ruleDefinition["type"] - context, _ := resolution["context"].(map[string]interface{}) + context, _ := resolution["context"].(map[string]any) forControl, _ := context["for_control"].(string) switch resolutionType { case "legacy_flow": @@ -254,19 +254,19 @@ func printAssertAsTable(raw string, out io.Writer, page int) error { logger.Info("") } - flows := evaluationResult["flows"].([]interface{}) + flows := evaluationResult["flows"].([]any) for _, item := range flows { - item := item.(map[string]interface{}) + item := item.(map[string]any) flow := item["flow"].(string) trail, _ := item["trail"].(string) - complianceStatus, _ := item["compliance_status"].(map[string]interface{}) - attestationsStatuses, _ := complianceStatus["attestations_statuses"].([]interface{}) + complianceStatus, _ := item["compliance_status"].(map[string]any) + attestationsStatuses, _ := complianceStatus["attestations_statuses"].([]any) logger.Info("Flow: %v\n Trail: %v", flow, trail) logger.Info(" %-32v %-30v %-15v %-10v", "Attestation-name", "type", "status", "compliant") for _, item := range attestationsStatuses { - attestation := item.(map[string]interface{}) + attestation := item.(map[string]any) name := attestation["attestation_name"] attType := attestation["attestation_type"] status := attestation["status"] diff --git a/cmd/kosli/assertSnapshot.go b/cmd/kosli/assertSnapshot.go index c521d39a3..74698e36b 100644 --- a/cmd/kosli/assertSnapshot.go +++ b/cmd/kosli/assertSnapshot.go @@ -78,7 +78,7 @@ func run(out io.Writer, args []string) error { return err } - var environmentData map[string]interface{} + var environmentData map[string]any err = json.Unmarshal([]byte(response.Body), &environmentData) if err != nil { return err diff --git a/cmd/kosli/attestCustom.go b/cmd/kosli/attestCustom.go index 534de235e..d8f644245 100644 --- a/cmd/kosli/attestCustom.go +++ b/cmd/kosli/attestCustom.go @@ -13,8 +13,8 @@ import ( type CustomAttestationPayload struct { *CommonAttestationPayload - TypeName string `json:"type_name"` - AttestationData interface{} `json:"attestation_data"` + TypeName string `json:"type_name"` + AttestationData any `json:"attestation_data"` } type attestCustomOptions struct { diff --git a/cmd/kosli/attestation.go b/cmd/kosli/attestation.go index e0d747156..92ca25262 100644 --- a/cmd/kosli/attestation.go +++ b/cmd/kosli/attestation.go @@ -32,7 +32,7 @@ type CommonAttestationPayload struct { TargetArtifacts []string `json:"target_artifacts,omitempty"` ExternalURLs map[string]*URLInfo `json:"external_urls,omitempty"` OriginURL string `json:"origin_url,omitempty"` - UserData interface{} `json:"user_data,omitempty"` + UserData any `json:"user_data,omitempty"` Description string `json:"description,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` } @@ -212,7 +212,7 @@ func processExternalURLs(externalURLs, externalFingerprints map[string]string) ( return processedExternalURLs, nil } -func prepareAttestationForm(payload interface{}, evidencePaths []string) ([]requests.FormItem, bool, string, error) { +func prepareAttestationForm(payload any, evidencePaths []string) ([]requests.FormItem, bool, string, error) { form, cleanupNeeded, evidencePath, err := newAttestationForm(payload, evidencePaths) if err != nil { return []requests.FormItem{}, cleanupNeeded, evidencePath, err @@ -235,7 +235,7 @@ func parseAttestationNameTemplate(template string) (string, string, error) { // newAttestationForm constructs a list of FormItems for an attestation // form submission. -func newAttestationForm(payload interface{}, attachments []string) ( +func newAttestationForm(payload any, attachments []string) ( []requests.FormItem, bool, string, error, ) { form := []requests.FormItem{ @@ -314,9 +314,9 @@ func getGitRepoInfoFromBitbucket() *gitview.GitRepoInfo { workspace = "" } - var additionalInfo map[string]interface{} + var additionalInfo map[string]any if projectKey := os.Getenv("BITBUCKET_PROJECT_KEY"); projectKey != "" { - additionalInfo = map[string]interface{}{"project_key": projectKey} + additionalInfo = map[string]any{"project_key": projectKey} } return &gitview.GitRepoInfo{ diff --git a/cmd/kosli/attestation_test.go b/cmd/kosli/attestation_test.go index bf74b591f..63a57c6b2 100644 --- a/cmd/kosli/attestation_test.go +++ b/cmd/kosli/attestation_test.go @@ -23,7 +23,7 @@ func TestMergeGitRepoInfo(t *testing.T) { wantURL string wantProvider string wantNamespacePath []string - wantAdditionalInfo map[string]interface{} + wantAdditionalInfo map[string]any }{ { name: "nil when both ID and Name are empty", @@ -115,7 +115,7 @@ func TestMergeGitRepoInfo(t *testing.T) { name: "explicit --repository override clears stale CI-detected NamespacePath/AdditionalInfo", base: &gitview.GitRepoInfo{ ID: "repo-id", Name: "MyOrg/Payment/my-repo", URL: "https://dev.azure.com/MyOrg/Payment/_git/my-repo", - NamespacePath: []string{"MyOrg", "Payment"}, AdditionalInfo: map[string]interface{}{"project_key": "PAY"}, + NamespacePath: []string{"MyOrg", "Payment"}, AdditionalInfo: map[string]any{"project_key": "PAY"}, }, repoName: "my-fork/repo", repoProvider: "github", @@ -130,7 +130,7 @@ func TestMergeGitRepoInfo(t *testing.T) { name: "CI-detected NamespacePath/AdditionalInfo are preserved when --repository is not set explicitly", base: &gitview.GitRepoInfo{ ID: "repo-id", Name: "MyOrg/Payment/my-repo", URL: "https://dev.azure.com/MyOrg/Payment/_git/my-repo", - NamespacePath: []string{"MyOrg", "Payment"}, AdditionalInfo: map[string]interface{}{"project_key": "PAY"}, + NamespacePath: []string{"MyOrg", "Payment"}, AdditionalInfo: map[string]any{"project_key": "PAY"}, }, repoNameExplicit: false, wantNil: false, @@ -138,7 +138,7 @@ func TestMergeGitRepoInfo(t *testing.T) { wantName: "MyOrg/Payment/my-repo", wantURL: "https://dev.azure.com/MyOrg/Payment/_git/my-repo", wantNamespacePath: []string{"MyOrg", "Payment"}, - wantAdditionalInfo: map[string]interface{}{"project_key": "PAY"}, + wantAdditionalInfo: map[string]any{"project_key": "PAY"}, }, { name: "flag name applied when base has no name even if not explicit", @@ -440,14 +440,14 @@ func TestGetGitRepoInfoFromBitbucket(t *testing.T) { bitbucketRepoFullName string bitbucketProjectKey string wantNamespacePath []string - wantAdditionalInfo map[string]interface{} + wantAdditionalInfo map[string]any }{ { name: "with project key", bitbucketRepoFullName: "myteam/my-repo", bitbucketProjectKey: "PROJ", wantNamespacePath: []string{"myteam"}, - wantAdditionalInfo: map[string]interface{}{"project_key": "PROJ"}, + wantAdditionalInfo: map[string]any{"project_key": "PROJ"}, }, { name: "without project key", diff --git a/cmd/kosli/beginTrail.go b/cmd/kosli/beginTrail.go index 8a796b34f..418c13bae 100644 --- a/cmd/kosli/beginTrail.go +++ b/cmd/kosli/beginTrail.go @@ -55,7 +55,7 @@ type beginTrailOptions struct { type TrailPayload struct { Name string `json:"name"` Description string `json:"description,omitempty"` - UserData interface{} `json:"user_data,omitempty"` + UserData any `json:"user_data,omitempty"` Commit *gitview.BasicCommitInfo `json:"git_commit_info,omitempty"` GitRepoInfo *gitview.GitRepoInfo `json:"repo_info,omitempty"` ExternalURLs map[string]*URLInfo `json:"external_urls,omitempty"` diff --git a/cmd/kosli/beginTrailPayload_test.go b/cmd/kosli/beginTrailPayload_test.go index 732d63745..57241413d 100644 --- a/cmd/kosli/beginTrailPayload_test.go +++ b/cmd/kosli/beginTrailPayload_test.go @@ -14,7 +14,7 @@ func TestTrailPayloadOmitsUnsetDescription(t *testing.T) { body, err := json.Marshal(TrailPayload{Name: "test-123"}) require.NoError(t, err) - var got map[string]interface{} + var got map[string]any require.NoError(t, json.Unmarshal(body, &got)) require.NotContains(t, got, "description") } @@ -24,7 +24,7 @@ func TestTrailPayloadOmitsUnsetUserData(t *testing.T) { body, err := json.Marshal(TrailPayload{Name: "test-123"}) require.NoError(t, err) - var got map[string]interface{} + var got map[string]any require.NoError(t, json.Unmarshal(body, &got)) require.NotContains(t, got, "user_data") } @@ -40,9 +40,9 @@ func TestTrailPayloadKeepsAnExplicitlyEmptyUserData(t *testing.T) { body, err := json.Marshal(TrailPayload{Name: "test-123", UserData: userData}) require.NoError(t, err) - var got map[string]interface{} + var got map[string]any require.NoError(t, json.Unmarshal(body, &got)) - require.Equal(t, map[string]interface{}{}, got["user_data"]) + require.Equal(t, map[string]any{}, got["user_data"]) } // omitempty must not swallow a value the user did give, so a payload carrying @@ -51,14 +51,14 @@ func TestTrailPayloadKeepsSetDescriptionAndUserData(t *testing.T) { payload := TrailPayload{ Name: "test-123", Description: "the release trail", - UserData: map[string]interface{}{"release": "2.11.21"}, + UserData: map[string]any{"release": "2.11.21"}, } body, err := json.Marshal(payload) require.NoError(t, err) - var got map[string]interface{} + var got map[string]any require.NoError(t, json.Unmarshal(body, &got)) require.Equal(t, "the release trail", got["description"]) - require.Equal(t, map[string]interface{}{"release": "2.11.21"}, got["user_data"]) + require.Equal(t, map[string]any{"release": "2.11.21"}, got["user_data"]) } diff --git a/cmd/kosli/cli_utils.go b/cmd/kosli/cli_utils.go index e7b38e9e1..a5c58ddcc 100644 --- a/cmd/kosli/cli_utils.go +++ b/cmd/kosli/cli_utils.go @@ -436,9 +436,9 @@ func GetSha256Digest(artifactName string, o *fingerprintOptions, logger *log.Log } // LoadJsonData loads json data from a file -func LoadJsonData(filepath string) (interface{}, error) { +func LoadJsonData(filepath string) (any, error) { var err error - var result interface{} + var result any content := `{}` if filepath != "" { content, err = utils.LoadFileContent(filepath) @@ -459,7 +459,7 @@ func LoadJsonData(filepath string) (interface{}, error) { // LoadOptionalJsonData loads json data from a file, and returns nil when no // file was given so that an omitempty field stays out of the payload. -func LoadOptionalJsonData(filepath string) (interface{}, error) { +func LoadOptionalJsonData(filepath string) (any, error) { if filepath == "" { return nil, nil } @@ -578,7 +578,7 @@ func tabFormattedPrint(out io.Writer, header []string, rows []string) { // formattedTimestamp formats a float timestamp into something like "Mon, 22 Aug 2022 11:34:59 CEST • 10 days ago" // time is formatted using RFC1123 -func formattedTimestamp(timestamp interface{}, short bool) (string, error) { +func formattedTimestamp(timestamp any, short bool) (string, error) { var intTimestamp int64 var shortFormat string var unixTime time.Time diff --git a/cmd/kosli/cli_utils_test.go b/cmd/kosli/cli_utils_test.go index 63ed7fba9..cf50a27e9 100644 --- a/cmd/kosli/cli_utils_test.go +++ b/cmd/kosli/cli_utils_test.go @@ -859,7 +859,7 @@ func (suite *CliUtilsTestSuite) TestConditionallyRequiredFlags() { func (suite *CliUtilsTestSuite) TestFormattedTimestamp() { tests := []struct { name string - timestamp interface{} + timestamp any short bool expected string wantErr bool diff --git a/cmd/kosli/createAttestationType.go b/cmd/kosli/createAttestationType.go index 643db2b22..86c8cd011 100644 --- a/cmd/kosli/createAttestationType.go +++ b/cmd/kosli/createAttestationType.go @@ -243,7 +243,7 @@ func (o *createAttestationTypeOptions) run(args []string) error { return err } -func prepareAttestationTypeForm(payload interface{}, schemaFilePath string) ([]requests.FormItem, error) { +func prepareAttestationTypeForm(payload any, schemaFilePath string) ([]requests.FormItem, error) { form, err := newAttestationTypeForm(payload, schemaFilePath) if err != nil { return []requests.FormItem{}, err @@ -253,7 +253,7 @@ func prepareAttestationTypeForm(payload interface{}, schemaFilePath string) ([]r // newAttestationTypeForm constructs a list of FormItems for an attestation-type // form submission. -func newAttestationTypeForm(payload interface{}, schemaFilePath string) ( +func newAttestationTypeForm(payload any, schemaFilePath string) ( []requests.FormItem, error, ) { form := []requests.FormItem{ diff --git a/cmd/kosli/createFlow.go b/cmd/kosli/createFlow.go index 6581a8c4a..4ecbf926b 100644 --- a/cmd/kosli/createFlow.go +++ b/cmd/kosli/createFlow.go @@ -189,7 +189,7 @@ func injectArtifactIntoTemplateIfNotExisting(template []string) []string { // newFlowForm constructs a list of FormItems for a flow with a template file // form submission. -func newFlowForm(payload interface{}, templateFile string, templateRequired bool) ([]requests.FormItem, error) { +func newFlowForm(payload any, templateFile string, templateRequired bool) ([]requests.FormItem, error) { if templateFile == "" && templateRequired { return []requests.FormItem{}, fmt.Errorf("cannot create a flow form without a template file") } diff --git a/cmd/kosli/createPolicy.go b/cmd/kosli/createPolicy.go index 707742e11..432d870f8 100644 --- a/cmd/kosli/createPolicy.go +++ b/cmd/kosli/createPolicy.go @@ -108,7 +108,7 @@ func (o *createPolicyOptions) run(args []string) error { // newPolicyForm constructs a list of FormItems for a policy with a policy file // form submission. -func newPolicyForm(payload interface{}, policyFile string) ([]requests.FormItem, error) { +func newPolicyForm(payload any, policyFile string) ([]requests.FormItem, error) { if policyFile == "" { return []requests.FormItem{}, fmt.Errorf("cannot create a policy form without a policy file") } diff --git a/cmd/kosli/evaluateHelpers.go b/cmd/kosli/evaluateHelpers.go index 975ef796e..f1b332c2f 100644 --- a/cmd/kosli/evaluateHelpers.go +++ b/cmd/kosli/evaluateHelpers.go @@ -97,7 +97,7 @@ func (o *commonEvaluateOptions) assertOnDeny() bool { return !o.noAssert } -func fetchAndEnrichTrail(flowName, trailName string, attestations []string) (interface{}, error) { +func fetchAndEnrichTrail(flowName, trailName string, attestations []string) (any, error) { trailURL, err := url.JoinPath(global.Host, "api/v2/trails", global.Org, flowName, trailName) if err != nil { return nil, err @@ -113,7 +113,7 @@ func fetchAndEnrichTrail(flowName, trailName string, attestations []string) (int return nil, err } - var trailData interface{} + var trailData any err = json.Unmarshal([]byte(response.Body), &trailData) if err != nil { return nil, fmt.Errorf("failed to parse trail response: %v", err) @@ -124,7 +124,7 @@ func fetchAndEnrichTrail(flowName, trailName string, attestations []string) (int ids := evaluate.CollectAttestationIDs(trailData) if len(ids) > 0 { - details := make(map[string]interface{}) + details := make(map[string]any) for _, id := range ids { detailURL, err := url.JoinPath(global.Host, "api/v2/attestations", global.Org) if err != nil { @@ -141,12 +141,12 @@ func fetchAndEnrichTrail(flowName, trailName string, attestations []string) (int if err != nil { return nil, fmt.Errorf("failed to fetch attestation detail for %s: %w", id, err) } - var wrapper map[string]interface{} + var wrapper map[string]any if err := json.Unmarshal([]byte(detailResp.Body), &wrapper); err != nil { return nil, fmt.Errorf("failed to parse attestation detail for %s: %w", id, err) } - if data, ok := wrapper["data"].([]interface{}); ok && len(data) > 0 { - if entry, ok := data[0].(map[string]interface{}); ok { + if data, ok := wrapper["data"].([]any); ok && len(data) > 0 { + if entry, ok := data[0].(map[string]any); ok { details[id] = entry } } @@ -231,7 +231,7 @@ func sameHostRedirectPolicy(req *http.Request, via []*http.Request) error { return nil } -func parseParams(raw string) (map[string]interface{}, error) { +func parseParams(raw string) (map[string]any, error) { if raw == "" { return nil, nil } @@ -247,14 +247,14 @@ func parseParams(raw string) (map[string]interface{}, error) { jsonBytes = []byte(raw) } - var params map[string]interface{} + var params map[string]any if err := json.Unmarshal(jsonBytes, ¶ms); err != nil { return nil, fmt.Errorf("failed to parse --params: %w", err) } return params, nil } -func evaluateAndPrintResult(out io.Writer, policyRef string, input map[string]interface{}, outputFormat string, showInput bool, params map[string]interface{}, assertOnDeny bool) error { +func evaluateAndPrintResult(out io.Writer, policyRef string, input map[string]any, outputFormat string, showInput bool, params map[string]any, assertOnDeny bool) error { policySource, err := loadPolicy(policyRef) if err != nil { return err @@ -533,8 +533,8 @@ func policyBundleKey(ref string) string { // printEvaluateResult renders a verdict, whatever produced it, so that every // evaluation path prints the same bytes for the same verdict. -func printEvaluateResult(out io.Writer, result *evaluate.Result, input map[string]interface{}, outputFormat string, showInput bool, params map[string]interface{}, assertOnDeny bool, decisionID string) error { - auditResult := map[string]interface{}{ +func printEvaluateResult(out io.Writer, result *evaluate.Result, input map[string]any, outputFormat string, showInput bool, params map[string]any, assertOnDeny bool, decisionID string) error { + auditResult := map[string]any{ "allow": result.Allow, "violations": result.Violations, } @@ -568,7 +568,7 @@ func printEvaluateResultAsJsonFn(assertOnDeny bool) output.FormatOutputFunc { return err } - var result map[string]interface{} + var result map[string]any if err := json.Unmarshal([]byte(raw), &result); err != nil { return err } @@ -581,7 +581,7 @@ func printEvaluateResultAsJsonFn(assertOnDeny bool) output.FormatOutputFunc { func printEvaluateResultAsTableFn(assertOnDeny bool) output.FormatOutputFunc { return func(raw string, out io.Writer, _ int) error { - var result map[string]interface{} + var result map[string]any if err := json.Unmarshal([]byte(raw), &result); err != nil { return err } @@ -601,7 +601,7 @@ func printEvaluateResultAsTableFn(assertOnDeny bool) output.FormatOutputFunc { rows = append(rows, "RESULT:\tDENIED") - if violations, ok := result["violations"].([]interface{}); ok && len(violations) > 0 { + if violations, ok := result["violations"].([]any); ok && len(violations) > 0 { for i, v := range violations { if i == 0 { rows = append(rows, fmt.Sprintf("VIOLATIONS:\t%s", v)) diff --git a/cmd/kosli/evaluateInput.go b/cmd/kosli/evaluateInput.go index 55fc3674c..ca1c96deb 100644 --- a/cmd/kosli/evaluateInput.go +++ b/cmd/kosli/evaluateInput.go @@ -108,7 +108,7 @@ func newEvaluateInputCmd(out io.Writer) *cobra.Command { } func (o *evaluateInputOptions) run(out io.Writer, in io.Reader) error { - var input map[string]interface{} + var input map[string]any var err error if o.inputFile == "" { @@ -131,7 +131,7 @@ func (o *evaluateInputOptions) run(out io.Writer, in io.Reader) error { return evaluateAndPrintResult(out, o.policyRef, input, o.output, o.showInput, params, o.assertOnDeny()) } -func loadInputFromFile(filePath string) (result map[string]interface{}, err error) { +func loadInputFromFile(filePath string) (result map[string]any, err error) { f, err := os.Open(filePath) if err != nil { return nil, fmt.Errorf("failed to read input file: %w", err) @@ -144,8 +144,8 @@ func loadInputFromFile(filePath string) (result map[string]interface{}, err erro return loadInput(f) } -func loadInput(r io.Reader) (map[string]interface{}, error) { - var input map[string]interface{} +func loadInput(r io.Reader) (map[string]any, error) { + var input map[string]any if err := json.NewDecoder(r).Decode(&input); err != nil { return nil, fmt.Errorf("failed to parse input: %w", err) } diff --git a/cmd/kosli/evaluateInput_test.go b/cmd/kosli/evaluateInput_test.go index 76580b8d4..077fe636a 100644 --- a/cmd/kosli/evaluateInput_test.go +++ b/cmd/kosli/evaluateInput_test.go @@ -252,7 +252,7 @@ func TestLoadInput(t *testing.T) { reader := strings.NewReader(`{"trail": {"name": "from-reader"}}`) input, err := loadInput(reader) require.NoError(t, err) - trail, ok := input["trail"].(map[string]interface{}) + trail, ok := input["trail"].(map[string]any) require.True(t, ok) require.Equal(t, "from-reader", trail["name"]) } diff --git a/cmd/kosli/evaluatePolicy_test.go b/cmd/kosli/evaluatePolicy_test.go index 11d58c01a..8503df235 100644 --- a/cmd/kosli/evaluatePolicy_test.go +++ b/cmd/kosli/evaluatePolicy_test.go @@ -87,12 +87,12 @@ func (suite *EvaluatePolicyCommandTestSuite) TestItSendsThePolicyAndTheTrailAndP require.Equal(suite.T(), 0, fake.unexpected) created := fake.created[0] - context := created["context"].(map[string]interface{}) - require.Equal(suite.T(), []interface{}{ - map[string]interface{}{"flow": "my-flow", "trail": "my-trail"}, + context := created["context"].(map[string]any) + require.Equal(suite.T(), []any{ + map[string]any{"flow": "my-flow", "trail": "my-trail"}, }, context["trails"]) - files := created["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := created["policy"].(map[string]any)["files"].(map[string]any) require.Len(suite.T(), files, 1) require.Contains(suite.T(), files, "allow-all.rego", "the policy travels under its own name") require.Contains(suite.T(), files["allow-all.rego"], "package policy") @@ -113,10 +113,10 @@ func (suite *EvaluatePolicyCommandTestSuite) TestItPassesParamsOnUnchanged() { for _, test := range []struct { name string flag string - want map[string]interface{} + want map[string]any }{ - {"inline json", `--params '{"min_approvers":2}'`, map[string]interface{}{"min_approvers": float64(2)}}, - {"a file", "--params @testdata/evaluate/params-low-threshold.json", map[string]interface{}{"threshold": float64(3)}}, + {"inline json", `--params '{"min_approvers":2}'`, map[string]any{"min_approvers": float64(2)}}, + {"a file", "--params @testdata/evaluate/params-low-threshold.json", map[string]any{"threshold": float64(3)}}, } { suite.Run(test.name, func() { server, fake := newFakeEvaluations(suite.T(), verdictAllowed) @@ -136,7 +136,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestNoParamsTravelAsAnEmptyObject() _, _, _, _, err := executeCommandC(suite.cmd(server.URL, "")) require.NoError(suite.T(), err) - require.Equal(suite.T(), map[string]interface{}{}, fake.created[0]["params"]) + require.Equal(suite.T(), map[string]any{}, fake.created[0]["params"]) } // A caller moving here from `evaluate trail` must not have to re-parse. @@ -230,11 +230,11 @@ func (suite *EvaluatePolicyCommandTestSuite) TestEveryContextGoesInOneEvaluation require.NoError(suite.T(), err) require.Len(suite.T(), fake.created, 1, "one evaluation, however many trails") - context := fake.created[0]["context"].(map[string]interface{}) - require.Equal(suite.T(), []interface{}{ - map[string]interface{}{"flow": "my-flow", "trail": "my-trail"}, - map[string]interface{}{"flow": "other-flow", "trail": "second"}, - map[string]interface{}{"flow": "my-flow", "trail": "third"}, + context := fake.created[0]["context"].(map[string]any) + require.Equal(suite.T(), []any{ + map[string]any{"flow": "my-flow", "trail": "my-trail"}, + map[string]any{"flow": "other-flow", "trail": "second"}, + map[string]any{"flow": "my-flow", "trail": "third"}, }, context["trails"], "named in the order given") } @@ -246,7 +246,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestARepeatedContextIsSentAsGiven() _, _, _, _, err := executeCommandC(suite.cmd(server.URL, "--context trail=my-flow/my-trail")) require.NoError(suite.T(), err) - context := fake.created[0]["context"].(map[string]interface{}) + context := fake.created[0]["context"].(map[string]any) require.Len(suite.T(), context["trails"], 2) } @@ -306,7 +306,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestControlRecordsADecisionWhereItI "--fingerprint b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c")) require.NoError(suite.T(), err) - require.Equal(suite.T(), map[string]interface{}{ + require.Equal(suite.T(), map[string]any{ "control": "SDLC-CTRL-0007", "name": "SDLC-CTRL-0007-decision", "flow": "release", @@ -324,7 +324,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestTheDecisionNameDefaultsToTheCon "--control SDLC-CTRL-0007 --flow release --trail my-trail")) require.NoError(suite.T(), err) - decision := fake.created[0]["decision"].(map[string]interface{}) + decision := fake.created[0]["decision"].(map[string]any) require.Equal(suite.T(), "SDLC-CTRL-0007-decision", decision["name"]) require.NotContains(suite.T(), decision, "fingerprint", "a decision about the trail carries no fingerprint") @@ -337,7 +337,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestAGivenNameIsSentAsGiven() { "--control SDLC-CTRL-0007 --flow release --trail my-trail --name code-review-decision")) require.NoError(suite.T(), err) - decision := fake.created[0]["decision"].(map[string]interface{}) + decision := fake.created[0]["decision"].(map[string]any) require.Equal(suite.T(), "code-review-decision", decision["name"]) } @@ -411,7 +411,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestTheDestinationCanComeFromTheEnv _, _, _, _, err := executeCommandC(suite.cmd(server.URL, "--control SDLC-CTRL-0007")) require.NoError(suite.T(), err) - decision := fake.created[0]["decision"].(map[string]interface{}) + decision := fake.created[0]["decision"].(map[string]any) require.Equal(suite.T(), "release", decision["flow"]) require.Equal(suite.T(), "my-trail", decision["trail"]) } @@ -504,7 +504,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestADirectoryTravelsAsOneBundle() "--host %s --org test-org --api-token test-token --max-api-retries 0", server.URL)) require.NoError(suite.T(), err) - files := fake.created[0]["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := fake.created[0]["policy"].(map[string]any)["files"].(map[string]any) require.Equal(suite.T(), []string{"README.md", "lib/helpers.rego", "policy.rego"}, sortedKeys(files), "keyed by path relative to the directory, and nothing left out by name") require.Contains(suite.T(), files["policy.rego"], "package policy") @@ -596,7 +596,7 @@ func (suite *EvaluatePolicyCommandTestSuite) TestADryRunSendsNothing() { require.NotContains(suite.T(), combined, "RESULT") } -func sortedKeys(files map[string]interface{}) []string { +func sortedKeys(files map[string]any) []string { keys := make([]string, 0, len(files)) for key := range files { keys = append(keys, key) diff --git a/cmd/kosli/evaluateServerSide_test.go b/cmd/kosli/evaluateServerSide_test.go index ef37e4698..b8c8d4698 100644 --- a/cmd/kosli/evaluateServerSide_test.go +++ b/cmd/kosli/evaluateServerSide_test.go @@ -22,7 +22,7 @@ import ( // database and object storage, with no queue, no worker and no evaluator, so // an evaluation started there can never reach a verdict. type fakeEvaluations struct { - created []map[string]interface{} + created []map[string]any reads int trailReads int unexpected int @@ -61,7 +61,7 @@ func newFakeEvaluations(t *testing.T, verdict string) (*httptest.Server, *fakeEv case r.Method == http.MethodPost && strings.HasPrefix(r.URL.Path, "/api/v2/evaluations/"): raw, err := io.ReadAll(r.Body) require.NoError(t, err) - var body map[string]interface{} + var body map[string]any require.NoError(t, json.Unmarshal(raw, &body)) fake.created = append(fake.created, body) w.WriteHeader(http.StatusCreated) @@ -146,12 +146,12 @@ func (suite *EvaluateServerSideTestSuite) TestItSendsThePolicyAndTheTrailAndPrin require.Equal(suite.T(), 0, fake.unexpected) created := fake.created[0] - context := created["context"].(map[string]interface{}) - require.Equal(suite.T(), []interface{}{ - map[string]interface{}{"flow": "my-flow", "trail": "my-trail"}, + context := created["context"].(map[string]any) + require.Equal(suite.T(), []any{ + map[string]any{"flow": "my-flow", "trail": "my-trail"}, }, context["trails"]) - files := created["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := created["policy"].(map[string]any)["files"].(map[string]any) require.Len(suite.T(), files, 1) require.Contains(suite.T(), files, "allow-all.rego", "the policy travels under its own name") require.Contains(suite.T(), files["allow-all.rego"], "package policy") @@ -252,7 +252,7 @@ func (suite *EvaluateServerSideTestSuite) TestItUploadsAPolicyTheLocalPathWouldR require.NoError(suite.T(), err, "the local package rule must not be applied under the flag") require.Regexp(suite.T(), `RESULT:\s+ALLOWED`, combined) - files := fake.created[0]["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := fake.created[0]["policy"].(map[string]any)["files"].(map[string]any) require.Contains(suite.T(), files, "no-package-policy.rego") } @@ -272,11 +272,11 @@ func (suite *EvaluateServerSideTestSuite) TestEveryTrailGoesInOneEvaluation() { require.Len(suite.T(), fake.created, 1, "one evaluation, however many trails") require.Equal(suite.T(), 0, fake.trailReads) - context := fake.created[0]["context"].(map[string]interface{}) - require.Equal(suite.T(), []interface{}{ - map[string]interface{}{"flow": "my-flow", "trail": "first"}, - map[string]interface{}{"flow": "my-flow", "trail": "second"}, - map[string]interface{}{"flow": "my-flow", "trail": "third"}, + context := fake.created[0]["context"].(map[string]any) + require.Equal(suite.T(), []any{ + map[string]any{"flow": "my-flow", "trail": "first"}, + map[string]any{"flow": "my-flow", "trail": "second"}, + map[string]any{"flow": "my-flow", "trail": "third"}, }, context["trails"], "named in the order given") } @@ -316,7 +316,7 @@ func (suite *EvaluateServerSideTestSuite) TestARepeatedTrailIsSentAsGiven() { "--host %s --org test-org --api-token test-token --max-api-retries 0", server.URL)) require.NoError(suite.T(), err) - context := fake.created[0]["context"].(map[string]interface{}) + context := fake.created[0]["context"].(map[string]any) require.Len(suite.T(), context["trails"], 2) } @@ -331,7 +331,7 @@ func (suite *EvaluateServerSideTestSuite) TestTheCeilingIsAHundredTrails() { trailNames(100), server.URL)) require.NoError(suite.T(), err) - require.Len(suite.T(), fake.created[0]["context"].(map[string]interface{})["trails"], 100) + require.Len(suite.T(), fake.created[0]["context"].(map[string]any)["trails"], 100) }) suite.Run("a hundred and one are refused before anything is sent", func() { @@ -403,22 +403,22 @@ func (suite *EvaluateServerSideTestSuite) TestPolicyParametersTravelUnchanged() for _, test := range []struct { name string extra string - want map[string]interface{} + want map[string]any }{ { name: "given inline", extra: `--params '{"threshold":2}'`, - want: map[string]interface{}{"threshold": float64(2)}, + want: map[string]any{"threshold": float64(2)}, }, { name: "read from a file", extra: "--params @testdata/evaluate/params-low-threshold.json", - want: map[string]interface{}{"threshold": float64(3)}, + want: map[string]any{"threshold": float64(3)}, }, { name: "not given at all", extra: "", - want: map[string]interface{}{}, + want: map[string]any{}, }, } { suite.Run(test.name, func() { @@ -588,7 +588,7 @@ func (suite *EvaluateServerSideTestSuite) TestARemotePolicyIsFetchedAndItsSource require.NoError(suite.T(), err) - files := fake.created[0]["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := fake.created[0]["policy"].(map[string]any)["files"].(map[string]any) require.Len(suite.T(), files, 1) require.Contains(suite.T(), files, "pr.rego", "named after the file, not the host") require.Equal(suite.T(), "package policy\n\nallow := true\n", files["pr.rego"]) @@ -624,7 +624,7 @@ func (suite *EvaluateServerSideTestSuite) TestThePolicyIsNamedByItsFileAlone() { test.policy, server.URL)) require.NoError(suite.T(), err) - files := fake.created[0]["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := fake.created[0]["policy"].(map[string]any)["files"].(map[string]any) require.Contains(suite.T(), files, test.want) for name := range files { require.NotContains(suite.T(), name, "..", "a bundle path never climbs out") @@ -646,7 +646,7 @@ func (suite *EvaluateServerSideTestSuite) TestAPolicyUrlNamingNoFileStillGetsANa policyServer.URL, server.URL)) require.NoError(suite.T(), err) - files := fake.created[0]["policy"].(map[string]interface{})["files"].(map[string]interface{}) + files := fake.created[0]["policy"].(map[string]any)["files"].(map[string]any) require.Contains(suite.T(), files, "policy.rego") } diff --git a/cmd/kosli/evaluateTrail.go b/cmd/kosli/evaluateTrail.go index 752024d4d..d84bcb19a 100644 --- a/cmd/kosli/evaluateTrail.go +++ b/cmd/kosli/evaluateTrail.go @@ -124,7 +124,7 @@ func (o *evaluateTrailOptions) run(out io.Writer, args []string) error { return err } - input := map[string]interface{}{ + input := map[string]any{ "trail": trailData, } diff --git a/cmd/kosli/evaluateTrails.go b/cmd/kosli/evaluateTrails.go index 3d9b67f12..88e910ce9 100644 --- a/cmd/kosli/evaluateTrails.go +++ b/cmd/kosli/evaluateTrails.go @@ -110,7 +110,7 @@ func (o *evaluateTrailsOptions) run(out io.Writer, args []string) error { return evaluateServerSide(out, &o.commonEvaluateOptions, refs) } - var trails []interface{} + var trails []any for _, trailName := range args { trailData, err := fetchAndEnrichTrail(o.flowName, trailName, o.attestations) if err != nil { @@ -124,7 +124,7 @@ func (o *evaluateTrailsOptions) run(out io.Writer, args []string) error { return err } - input := map[string]interface{}{ + input := map[string]any{ "trails": trails, } diff --git a/cmd/kosli/getArtifact.go b/cmd/kosli/getArtifact.go index a96f2301c..c00f5a83a 100644 --- a/cmd/kosli/getArtifact.go +++ b/cmd/kosli/getArtifact.go @@ -138,7 +138,7 @@ func printArtifactAsTableWrapper(artifactRaw string, out io.Writer, pageNumber i } func printArtifactsAsTable(artifactRaw string, out io.Writer, pageNumber int) error { - var artifacts []map[string]interface{} + var artifacts []map[string]any err := json.Unmarshal([]byte(artifactRaw), &artifacts) if err != nil { return err @@ -146,7 +146,7 @@ func printArtifactsAsTable(artifactRaw string, out io.Writer, pageNumber int) er return printArtifactsJsonAsTable(artifacts, out, pageNumber) } -func printArtifactsJsonAsTable(artifacts []map[string]interface{}, out io.Writer, pageNumber int) error { +func printArtifactsJsonAsTable(artifacts []map[string]any, out io.Writer, pageNumber int) error { separator := "" for _, artifact := range artifacts { rows := []string{} @@ -171,11 +171,11 @@ func printArtifactsJsonAsTable(artifacts []map[string]interface{}, out io.Writer rows = append(rows, fmt.Sprintf("State:\t%s", artifact["state"].(string))) - runningInEnvs := artifact["running"].([]interface{}) + runningInEnvs := artifact["running"].([]any) if len(runningInEnvs) > 0 { runningInEnvNames := []string{} for _, envDataInterface := range runningInEnvs { - envData := envDataInterface.(map[string]interface{}) + envData := envDataInterface.(map[string]any) runningInEnvNames = append(runningInEnvNames, fmt.Sprintf("%s#%.0f", envData["environment_name"].(string), envData["snapshot_index"].(float64))) } @@ -183,22 +183,22 @@ func printArtifactsJsonAsTable(artifacts []map[string]interface{}, out io.Writer rows = append(rows, fmt.Sprintf("Running in environments:\t%s", strings.Join(runningInEnvNames, ", "))) } - exitedInEnvs := artifact["exited"].([]interface{}) + exitedInEnvs := artifact["exited"].([]any) if len(exitedInEnvs) > 0 { exitedInEnvNames := []string{} for _, envDataInterface := range exitedInEnvs { - envData := envDataInterface.(map[string]interface{}) + envData := envDataInterface.(map[string]any) exitedInEnvNames = append(exitedInEnvNames, fmt.Sprintf("%s#%.0f", envData["environment_name"].(string), envData["snapshot_index"].(float64))) } rows = append(rows, fmt.Sprintf("Exited from environments:\t%s", strings.Join(exitedInEnvNames, ", "))) } - history := artifact["history"].([]interface{}) + history := artifact["history"].([]any) if len(history) > 0 { rows = append(rows, "History:") for _, rawHistory := range history { - event := rawHistory.(map[string]interface{}) + event := rawHistory.(map[string]any) eventString := event["event"] eventTimestamp, err := formattedTimestamp(event["timestamp"], true) if err != nil { diff --git a/cmd/kosli/getAttestationType.go b/cmd/kosli/getAttestationType.go index fbac38279..55fa87241 100644 --- a/cmd/kosli/getAttestationType.go +++ b/cmd/kosli/getAttestationType.go @@ -102,7 +102,7 @@ func (o *getAttestationTypeOptions) run(out io.Writer, args []string) error { } func printAttestationTypeAsTable(raw string, out io.Writer, page int) error { - var attestationType map[string]interface{} + var attestationType map[string]any err := json.Unmarshal([]byte(raw), &attestationType) if err != nil { return err @@ -141,8 +141,8 @@ func printAttestationTypeAsTable(raw string, out io.Writer, page int) error { } rows = append(rows, "Versions:\t") - for _, version := range attestationType["versions"].([]interface{}) { - versionMap := version.(map[string]interface{}) + for _, version := range attestationType["versions"].([]any) { + versionMap := version.(map[string]any) rows, err = printVersionedAttestationTypeAsTable(versionMap, rows) if err != nil { return err @@ -154,7 +154,7 @@ func printAttestationTypeAsTable(raw string, out io.Writer, page int) error { return nil } -func printVersionedAttestationTypeAsTable(raw map[string]interface{}, rows []string) ([]string, error) { +func printVersionedAttestationTypeAsTable(raw map[string]any, rows []string) ([]string, error) { attestationType := raw timestamp, err := formattedTimestamp(attestationType["timestamp"], false) @@ -179,10 +179,10 @@ func printVersionedAttestationTypeAsTable(raw map[string]interface{}, rows []str rows = append(rows, fmt.Sprintf(" Type schema:\t%s", string(typeSchemaJSON))) } - if evaluator, ok := attestationType["evaluator"].(map[string]interface{}); ok { + if evaluator, ok := attestationType["evaluator"].(map[string]any); ok { rows = append(rows, " Evaluator:\t") rows = append(rows, fmt.Sprintf(" Content Type:\t%s", evaluator["content_type"])) - if rules, ok := evaluator["rules"].([]interface{}); ok { + if rules, ok := evaluator["rules"].([]any); ok { rows = append(rows, " Rules:") for _, rule := range rules { rows = append(rows, fmt.Sprintf(" \t\t%s", rule)) @@ -192,10 +192,10 @@ func printVersionedAttestationTypeAsTable(raw map[string]interface{}, rows []str // Types created without a summary have a null "summary", which fails this // type assertion and prints nothing, leaving their output unchanged. - if summary, ok := attestationType["summary"].([]interface{}); ok && len(summary) > 0 { + if summary, ok := attestationType["summary"].([]any); ok && len(summary) > 0 { rows = append(rows, " Summary:\t") for _, entry := range summary { - entryMap, ok := entry.(map[string]interface{}) + entryMap, ok := entry.(map[string]any) if !ok { continue } diff --git a/cmd/kosli/getControl.go b/cmd/kosli/getControl.go index aa3e2e23a..bce6f47c2 100644 --- a/cmd/kosli/getControl.go +++ b/cmd/kosli/getControl.go @@ -77,7 +77,7 @@ func (o *getControlOptions) run(out io.Writer, args []string) error { } func printControlAsTable(raw string, out io.Writer, page int) error { - var control map[string]interface{} + var control map[string]any if err := json.Unmarshal([]byte(raw), &control); err != nil { return err } @@ -105,7 +105,7 @@ func printControlAsTable(raw string, out io.Writer, page int) error { rows = append(rows, fmt.Sprintf("Created at:\t%s", createdAtFormatted)) } - if tags, ok := control["tags"].(map[string]interface{}); ok && len(tags) > 0 { + if tags, ok := control["tags"].(map[string]any); ok && len(tags) > 0 { tagKeys := make([]string, 0, len(tags)) for key := range tags { tagKeys = append(tagKeys, key) @@ -118,7 +118,7 @@ func printControlAsTable(raw string, out io.Writer, page int) error { rows = append(rows, fmt.Sprintf("Tags:\t%s", strings.Join(tagPairs, ", "))) } - if links, ok := control["links"].(map[string]interface{}); ok && len(links) > 0 { + if links, ok := control["links"].(map[string]any); ok && len(links) > 0 { rows = append(rows, "Links:\t") linkNames := make([]string, 0, len(links)) for name := range links { @@ -130,7 +130,7 @@ func printControlAsTable(raw string, out io.Writer, page int) error { } } - if policies, ok := control["policies_referencing"].([]interface{}); ok && len(policies) > 0 { + if policies, ok := control["policies_referencing"].([]any); ok && len(policies) > 0 { policyNames := make([]string, 0, len(policies)) for _, p := range policies { policyNames = append(policyNames, fmt.Sprintf("%s", p)) diff --git a/cmd/kosli/getEnvironment.go b/cmd/kosli/getEnvironment.go index 365dccd97..b381600d7 100644 --- a/cmd/kosli/getEnvironment.go +++ b/cmd/kosli/getEnvironment.go @@ -68,7 +68,7 @@ func (o *getEnvironmentOptions) run(out io.Writer, args []string) error { } func printEnvironmentAsTable(raw string, out io.Writer, page int) error { - var env map[string]interface{} + var env map[string]any err := json.Unmarshal([]byte(raw), &env) if err != nil { return err @@ -86,7 +86,7 @@ func printEnvironmentAsTable(raw string, out io.Writer, page int) error { state = "NON-COMPLIANT" } - tags := env["tags"].(map[string]interface{}) + tags := env["tags"].(map[string]any) tagsOutput := "" for key, value := range tags { tagsOutput += fmt.Sprintf("[%s=%s], ", key, value) diff --git a/cmd/kosli/getFlow.go b/cmd/kosli/getFlow.go index 1db35090a..a8e5867ae 100644 --- a/cmd/kosli/getFlow.go +++ b/cmd/kosli/getFlow.go @@ -67,7 +67,7 @@ func (o *getFlowOptions) run(out io.Writer, args []string) error { } func printFlowAsTable(raw string, out io.Writer, page int) error { - var flow map[string]interface{} + var flow map[string]any err := json.Unmarshal([]byte(raw), &flow) if err != nil { return err @@ -95,7 +95,7 @@ func printFlowAsTable(raw string, out io.Writer, page int) error { tagsOutput := "" if flow["tags"] != nil { - tags := flow["tags"].(map[string]interface{}) + tags := flow["tags"].(map[string]any) for key, value := range tags { tagsOutput += fmt.Sprintf("[%s=%s], ", key, value) } diff --git a/cmd/kosli/getPolicy.go b/cmd/kosli/getPolicy.go index 5fb438605..a9b7cf8da 100644 --- a/cmd/kosli/getPolicy.go +++ b/cmd/kosli/getPolicy.go @@ -67,7 +67,7 @@ func (o *getPolicyOptions) run(out io.Writer, args []string) error { } func printPolicyAsTable(raw string, out io.Writer, page int) error { - var policy map[string]interface{} + var policy map[string]any err := json.Unmarshal([]byte(raw), &policy) if err != nil { return err @@ -78,11 +78,11 @@ func printPolicyAsTable(raw string, out io.Writer, page int) error { return err } - consumingEnvs := policy["consuming_envs"].([]interface{}) + consumingEnvs := policy["consuming_envs"].([]any) - versions := policy["versions"].([]interface{}) + versions := policy["versions"].([]any) - latestVersion := versions[len(versions)-1].(map[string]interface{}) + latestVersion := versions[len(versions)-1].(map[string]any) policyYaml := latestVersion["policy_yaml"].(string) policyYamlIndented := "\t" + strings.ReplaceAll(policyYaml, "\n", "\n\t") diff --git a/cmd/kosli/getSnapshot_test.go b/cmd/kosli/getSnapshot_test.go index 286beb416..9f69b478e 100644 --- a/cmd/kosli/getSnapshot_test.go +++ b/cmd/kosli/getSnapshot_test.go @@ -35,7 +35,7 @@ func (suite *GetSnapshotCommandTestSuite) SetupTest() { CreateEnv(global.Org, suite.emptyEnvName, "server", suite.T()) } -// TODO: Add test for a snappish of the environemnt name +// TODO: Add test for a snappish of the environment name func (suite *GetSnapshotCommandTestSuite) TestGetSnapshotCmd() { tests := []cmdTestCase{ { diff --git a/cmd/kosli/getTrail.go b/cmd/kosli/getTrail.go index d73f38ad5..ec3b2e322 100644 --- a/cmd/kosli/getTrail.go +++ b/cmd/kosli/getTrail.go @@ -80,7 +80,7 @@ func (o *getTrailOptions) run(out io.Writer, args []string) error { // summary.md artifact). It is a method so the trail heading can link to the // trail page in the Kosli app, which needs the flow name. func (o *getTrailOptions) printTrailAsMarkdown(raw string, out io.Writer, page int) error { - var trail map[string]interface{} + var trail map[string]any err := json.Unmarshal([]byte(raw), &trail) if err != nil { return err @@ -111,7 +111,7 @@ func (o *getTrailOptions) printTrailAsMarkdown(raw string, out io.Writer, page i fmt.Fprintf(&b, "| Origin | %s |\n", mdCell(originURL)) } - if commitInfo, ok := trail["git_commit_info"].(map[string]interface{}); ok { + if commitInfo, ok := trail["git_commit_info"].(map[string]any); ok { commitTimestamp, err := formattedTimestamp(commitInfo["timestamp"], false) if err != nil { return err @@ -132,7 +132,7 @@ func (o *getTrailOptions) printTrailAsMarkdown(raw string, out io.Writer, page i writeAttestationStatuses(&b, trail["compliance_status"], trailURL) b.WriteString("\n### Events\n\n") - if events, ok := trail["events"].([]interface{}); ok && len(events) > 0 { + if events, ok := trail["events"].([]any); ok && len(events) > 0 { b.WriteString("| Time | Description | Git commit | Compliance |\n") b.WriteString("| --- | --- | --- | --- |\n") for _, event := range events { @@ -159,7 +159,7 @@ func (o *getTrailOptions) printTrailAsMarkdown(raw string, out io.Writer, page i // that would otherwise break the table layout or be swallowed as HTML (e.g. // "" in a commit author). CR and CRLF count as line endings in // CommonMark, so they must be normalized along with LF. -func mdCell(v interface{}) string { +func mdCell(v any) string { if v == nil { return "" } @@ -176,7 +176,7 @@ func mdCell(v interface{}) string { // firstLine returns the first line of a multi-line value, e.g. a git commit // message subject. A full commit message would dominate a CI summary table. -func firstLine(v interface{}) string { +func firstLine(v any) string { if v == nil { return "" } @@ -190,7 +190,7 @@ func firstLine(v interface{}) string { // mdComplianceState prefixes a trail or artifact compliance state with a // glanceable emoji. Values come from the server: COMPLIANT / NON-COMPLIANT / // INCOMPLETE for trails, plus MISSING for artifacts. -func mdComplianceState(v interface{}) string { +func mdComplianceState(v any) string { s := mdCell(v) switch s { case "COMPLIANT": @@ -209,13 +209,13 @@ func mdComplianceState(v interface{}) string { // the trail and by each artifact. The attestation name links to the attestation // on the trail page when an attestation_id is present. The section is omitted // when the trail has no attestation statuses. -func writeAttestationStatuses(b *strings.Builder, complianceStatus interface{}, trailURL string) { - cs, ok := complianceStatus.(map[string]interface{}) +func writeAttestationStatuses(b *strings.Builder, complianceStatus any, trailURL string) { + cs, ok := complianceStatus.(map[string]any) if !ok { return } - trailAtts, _ := cs["attestations_statuses"].([]interface{}) - artifactsStatuses, _ := cs["artifacts_statuses"].(map[string]interface{}) + trailAtts, _ := cs["attestations_statuses"].([]any) + artifactsStatuses, _ := cs["artifacts_statuses"].(map[string]any) artifactNames := make([]string, 0, len(artifactsStatuses)) for name := range artifactsStatuses { @@ -225,8 +225,8 @@ func writeAttestationStatuses(b *strings.Builder, complianceStatus interface{}, total := len(trailAtts) for _, name := range artifactNames { - if artifact, ok := artifactsStatuses[name].(map[string]interface{}); ok { - if atts, ok := artifact["attestations_statuses"].([]interface{}); ok { + if artifact, ok := artifactsStatuses[name].(map[string]any); ok { + if atts, ok := artifact["attestations_statuses"].([]any); ok { total += len(atts) } } @@ -243,11 +243,11 @@ func writeAttestationStatuses(b *strings.Builder, complianceStatus interface{}, } for _, name := range artifactNames { - artifact, ok := artifactsStatuses[name].(map[string]interface{}) + artifact, ok := artifactsStatuses[name].(map[string]any) if !ok { continue } - atts, _ := artifact["attestations_statuses"].([]interface{}) + atts, _ := artifact["attestations_statuses"].([]any) if len(atts) == 0 { continue } @@ -258,11 +258,11 @@ func writeAttestationStatuses(b *strings.Builder, complianceStatus interface{}, // writeAttestationTable writes a headerless two-column table of attestation // name (linked when possible) and compliance status. -func writeAttestationTable(b *strings.Builder, attestations []interface{}, trailURL string) { +func writeAttestationTable(b *strings.Builder, attestations []any, trailURL string) { b.WriteString("| | |\n") b.WriteString("| --- | --- |\n") for _, a := range attestations { - att, ok := a.(map[string]interface{}) + att, ok := a.(map[string]any) if !ok { continue } @@ -280,7 +280,7 @@ func writeAttestationTable(b *strings.Builder, attestations []interface{}, trail // label, covering every status the server produces: MISSING (not yet reported), // COMPLETE with is_compliant true/false, and the unexpected flag (reported but // not expected by the template). -func mdAttestationCompliance(status string, isCompliant interface{}, unexpected bool) string { +func mdAttestationCompliance(status string, isCompliant any, unexpected bool) string { var label string switch status { case "MISSING": @@ -348,7 +348,7 @@ func mdEventCompliance(compliance string) string { } func printTrailAsTable(raw string, out io.Writer, page int) error { - var trail map[string]interface{} + var trail map[string]any err := json.Unmarshal([]byte(raw), &trail) if err != nil { return err @@ -366,7 +366,7 @@ func printTrailAsTable(raw string, out io.Writer, page int) error { rows = append(rows, fmt.Sprintf("Description:\t%s", trail["description"])) rows = append(rows, fmt.Sprintf("Compliance:\t%s", trail["compliance_state"])) rows = append(rows, fmt.Sprintf("Last modified at:\t%s", lastModifiedAt)) - if commitInfo, ok := trail["git_commit_info"].(map[string]interface{}); ok { + if commitInfo, ok := trail["git_commit_info"].(map[string]any); ok { rows = append(rows, "Git commit:\t") rows = append(rows, fmt.Sprintf(" Sha1:\t%s", commitInfo["sha1"].(string))) rows = append(rows, fmt.Sprintf(" Author:\t%s", commitInfo["author"].(string))) @@ -384,7 +384,7 @@ func printTrailAsTable(raw string, out io.Writer, page int) error { tabFormattedPrint(out, header, rows) - if events, ok := trail["events"].([]interface{}); ok { + if events, ok := trail["events"].([]any); ok { eventsHeader := []string{"\tTIME", "DESCRIPTION", "GIT-COMMIT", "COMPLIANCE"} eventsRows := []string{} for _, event := range events { @@ -400,7 +400,7 @@ func printTrailAsTable(raw string, out io.Writer, page int) error { return nil } -func eventRow(event interface{}) (string, error) { +func eventRow(event any) (string, error) { e, err := eventFields(event) if err != nil { return "", err @@ -422,8 +422,8 @@ type trailEventFields struct { attestationRef string // the attestation reference as it appears in the description, e.g. "artifact.snyk-scan" } -func eventFields(event interface{}) (trailEventFields, error) { - eventMap := event.(map[string]interface{}) +func eventFields(event any) (trailEventFields, error) { + eventMap := event.(map[string]any) eventTimestamp, err := formattedTimestamp(eventMap["timestamp"].(float64), true) if err != nil { return trailEventFields{}, err @@ -441,7 +441,7 @@ func eventFields(event interface{}) (trailEventFields, error) { eventCommit := "" eventCommitURL := "" - if commitInfo, ok := eventMap["git_commit_info"].(map[string]interface{}); ok { + if commitInfo, ok := eventMap["git_commit_info"].(map[string]any); ok { if sha1, ok := commitInfo["sha1"].(string); ok && len(sha1) >= 7 { eventCommit = sha1[0:7] } diff --git a/cmd/kosli/listAttestationTypes.go b/cmd/kosli/listAttestationTypes.go index 1ef16d89e..e68bd4154 100644 --- a/cmd/kosli/listAttestationTypes.go +++ b/cmd/kosli/listAttestationTypes.go @@ -66,7 +66,7 @@ func (o *listAttestationTypesOptions) run(out io.Writer, args []string) error { } func printAttestationTypesListAsTable(raw string, out io.Writer, page int) error { - var attestationTypes []map[string]interface{} + var attestationTypes []map[string]any err := json.Unmarshal([]byte(raw), &attestationTypes) if err != nil { return err @@ -85,7 +85,7 @@ func printAttestationTypesListAsTable(raw string, out io.Writer, page int) error if description == nil { description = "" } - latestVersion := len(attestationType["versions"].([]interface{})) + latestVersion := len(attestationType["versions"].([]any)) rows = append(rows, fmt.Sprintf("%s\t%s\t%d", attestationType["name"], description, latestVersion)) } diff --git a/cmd/kosli/listControls.go b/cmd/kosli/listControls.go index 45789da68..8e6c54f14 100644 --- a/cmd/kosli/listControls.go +++ b/cmd/kosli/listControls.go @@ -66,10 +66,10 @@ type listControlsOptions struct { } type listControlsResponse struct { - Controls []map[string]interface{} `json:"controls"` - Page int `json:"page"` - TotalPages int `json:"total_pages"` - TotalCount int `json:"total_count"` + Controls []map[string]any `json:"controls"` + Page int `json:"page"` + TotalPages int `json:"total_pages"` + TotalCount int `json:"total_count"` } func newListControlsCmd(out io.Writer) *cobra.Command { diff --git a/cmd/kosli/listEnvironments.go b/cmd/kosli/listEnvironments.go index 3f485a4d1..f2b1350f2 100644 --- a/cmd/kosli/listEnvironments.go +++ b/cmd/kosli/listEnvironments.go @@ -72,11 +72,11 @@ type environmentLsOptions struct { } type paginatedEnvsResponse struct { - Page int64 `json:"page"` - PerPage int64 `json:"per_page"` - TotalPages int64 `json:"total_pages"` - TotalCount int64 `json:"total_count"` - Environments []map[string]interface{} `json:"environments"` + Page int64 `json:"page"` + PerPage int64 `json:"per_page"` + TotalPages int64 `json:"total_pages"` + TotalCount int64 `json:"total_count"` + Environments []map[string]any `json:"environments"` } func newListEnvironmentsCmd(out io.Writer) *cobra.Command { @@ -169,7 +169,7 @@ func (o *environmentLsOptions) run(out io.Writer, args []string) error { func printEnvListAsTable(raw string, out io.Writer, page int) error { // the API returns a plain array when no pagination params are sent, // and a wrapped object with pagination metadata when they are - var envs []map[string]interface{} + var envs []map[string]any var paginated *paginatedEnvsResponse if err := json.Unmarshal([]byte(raw), &envs); err != nil { paginated = &paginatedEnvsResponse{} @@ -203,18 +203,18 @@ func printEnvListAsTable(raw string, out io.Writer, page int) error { } tagsOutput := "" - if tags, ok := env["tags"].(map[string]interface{}); ok { + if tags, ok := env["tags"].(map[string]any); ok { for key, value := range tags { tagsOutput += fmt.Sprintf("[%s=%s], ", key, value) } tagsOutput = strings.TrimSuffix(tagsOutput, ", ") } - var policies []interface{} + var policies []any if env["policies"] != nil { - policies = env["policies"].([]interface{}) + policies = env["policies"].([]any) } else { - policies = []interface{}{} + policies = []any{} } row := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s", env["name"], env["type"], last_reported_str, last_modified_str, tagsOutput, policies) diff --git a/cmd/kosli/listFlows.go b/cmd/kosli/listFlows.go index c1076ae9d..e85d7796c 100644 --- a/cmd/kosli/listFlows.go +++ b/cmd/kosli/listFlows.go @@ -129,12 +129,12 @@ func (o *listFlowsOptions) run(out io.Writer) error { } type listFlowsResponse struct { - Data []map[string]interface{} `json:"data"` - Pagination Pagination `json:"pagination"` + Data []map[string]any `json:"data"` + Pagination Pagination `json:"pagination"` } func printFlowsListAsTable(raw string, out io.Writer, page int) error { - var flows []map[string]interface{} + var flows []map[string]any var pagination *Pagination // The endpoint returns a plain array when unpaginated and a {data, pagination} @@ -165,7 +165,7 @@ func printFlowsListAsTable(raw string, out io.Writer, page int) error { rows := []string{} for _, flow := range flows { tagsOutput := "" - if tags, ok := flow["tags"].(map[string]interface{}); ok { + if tags, ok := flow["tags"].(map[string]any); ok { for key, value := range tags { tagsOutput += fmt.Sprintf("[%s=%s], ", key, value) } diff --git a/cmd/kosli/listPolicies.go b/cmd/kosli/listPolicies.go index f77b9a44f..4815fa344 100644 --- a/cmd/kosli/listPolicies.go +++ b/cmd/kosli/listPolicies.go @@ -66,7 +66,7 @@ func (o *policiesLsOptions) run(out io.Writer, args []string) error { } func printPolicyListAsTable(raw string, out io.Writer, page int) error { - var policies []map[string]interface{} + var policies []map[string]any err := json.Unmarshal([]byte(raw), &policies) if err != nil { return err @@ -85,9 +85,9 @@ func printPolicyListAsTable(raw string, out io.Writer, page int) error { return err } - versions := policy["versions"].([]interface{}) + versions := policy["versions"].([]any) - usedByEnvs := policy["consuming_envs"].([]interface{}) + usedByEnvs := policy["consuming_envs"].([]any) row := fmt.Sprintf("%s\t%s\t%s\t%d\t%s", policy["name"], policy["description"], createdAt, len(versions), usedByEnvs) rows = append(rows, row) diff --git a/cmd/kosli/listSnapshots.go b/cmd/kosli/listSnapshots.go index 2392c6227..4dce5d7a5 100644 --- a/cmd/kosli/listSnapshots.go +++ b/cmd/kosli/listSnapshots.go @@ -121,7 +121,7 @@ func (o *listSnapshotsOptions) getSnapshotsList(out io.Writer, envName, interval } func printSnapshotsListAsTable(raw string, out io.Writer, page int) error { - var snapshots []map[string]interface{} + var snapshots []map[string]any err := json.Unmarshal([]byte(raw), &snapshots) if err != nil { return err @@ -166,7 +166,7 @@ func printSnapshotsListAsTable(raw string, out io.Writer, page int) error { } func printEnvironmentEventsLogAsTable(raw string, out io.Writer, page int) error { - var events []map[string]interface{} + var events []map[string]any err := json.Unmarshal([]byte(raw), &events) if err != nil { return err diff --git a/cmd/kosli/openapiContract_test.go b/cmd/kosli/openapiContract_test.go index 639c51ad1..fc4a272d9 100644 --- a/cmd/kosli/openapiContract_test.go +++ b/cmd/kosli/openapiContract_test.go @@ -46,7 +46,7 @@ type componentSchema struct { // driftCase maps a CLI payload struct to the OpenAPI component it must match. type driftCase struct { name string - payload interface{} + payload any component string // ignore lists json field names to skip on both sides, for deliberate // CLI/API divergences (none needed yet). @@ -110,7 +110,7 @@ func (suite *OpenAPIContractTestSuite) TestPayloadsMatchSchema() { } // jsonFieldNames returns the wire names from a struct's json tags. -func jsonFieldNames(v interface{}) []string { +func jsonFieldNames(v any) []string { t := reflect.TypeOf(v) names := []string{} for i := 0; i < t.NumField(); i++ { diff --git a/cmd/kosli/reportArtifact.go b/cmd/kosli/reportArtifact.go index 60bbccad2..7f442b4e9 100644 --- a/cmd/kosli/reportArtifact.go +++ b/cmd/kosli/reportArtifact.go @@ -187,7 +187,7 @@ func (o *reportArtifactOptions) latestCommit(branchName string) (string, error) return "", err } - var latestCommitResponse map[string]interface{} + var latestCommitResponse map[string]any err = json.Unmarshal([]byte(response.Body), &latestCommitResponse) if err != nil { return "", err diff --git a/cmd/kosli/search.go b/cmd/kosli/search.go index 1ed5198fb..c95bb0e90 100644 --- a/cmd/kosli/search.go +++ b/cmd/kosli/search.go @@ -23,18 +23,18 @@ type SearchResponse struct { } type SearchArtifact struct { - Fingerprint string `json:"fingerprint"` - Name string `json:"name"` - Flow string `json:"flow"` - Commit string `json:"git_commit"` - HasProvenance bool `json:"has_provenance"` - CommitURL string `json:"commit_url"` - BuildURL string `json:"build_url"` - ArtifactURL string `json:"html_url"` - ComplianceState string `json:"compliance_state"` - RunningIn []string `json:"running_in"` - ExitedFrom []string `json:"exited_from"` - History []map[string]interface{} `json:"history"` + Fingerprint string `json:"fingerprint"` + Name string `json:"name"` + Flow string `json:"flow"` + Commit string `json:"git_commit"` + HasProvenance bool `json:"has_provenance"` + CommitURL string `json:"commit_url"` + BuildURL string `json:"build_url"` + ArtifactURL string `json:"html_url"` + ComplianceState string `json:"compliance_state"` + RunningIn []string `json:"running_in"` + ExitedFrom []string `json:"exited_from"` + History []map[string]any `json:"history"` } type ResolvedToBody struct { diff --git a/cmd/kosli/snapshotAutoEnvironment.go b/cmd/kosli/snapshotAutoEnvironment.go index fdcb4df2e..59dafd5e4 100644 --- a/cmd/kosli/snapshotAutoEnvironment.go +++ b/cmd/kosli/snapshotAutoEnvironment.go @@ -134,7 +134,7 @@ func getEnvironmentTypeIfExists(envName string) (bool, string, error) { return false, "", err } - var env map[string]interface{} + var env map[string]any if err := json.Unmarshal([]byte(response.Body), &env); err != nil { return false, "", err } diff --git a/cmd/kosli/testHelpers.go b/cmd/kosli/testHelpers.go index 8da6f50a1..a92a0e89b 100644 --- a/cmd/kosli/testHelpers.go +++ b/cmd/kosli/testHelpers.go @@ -40,7 +40,7 @@ type cmdTestCase struct { goldenStderr string // expected stderr only (exact match, ignored when empty) stdin string // fed to the command's stdin (empty means an immediate EOF) wantError bool - additionalConfig interface{} + additionalConfig any } // executeCommandC executes a command as a user would, with an empty stdin (any @@ -709,7 +709,7 @@ func GetAttestationId(flowName, trailName, attestationName string, t *testing.T) err := o.run(buffer, []string{attestationName}) require.NoError(t, err, "attestation should be retrieved without error") - var data []map[string]interface{} + var data []map[string]any err = json.Unmarshal(buffer.Bytes(), &data) require.NoError(t, err, "failed to parse attestation JSON: %s", buffer.String()) require.Greater(t, len(data), 0, "expected at least one attestation") diff --git a/cmd/kosli/updateControl.go b/cmd/kosli/updateControl.go index 0cb03e477..d0ffdf5a6 100644 --- a/cmd/kosli/updateControl.go +++ b/cmd/kosli/updateControl.go @@ -75,7 +75,7 @@ func (o *updateControlOptions) run(cmd *cobra.Command, args []string) error { // Only send the fields the user explicitly set, so unset flags leave the // corresponding values unchanged (the server treats an omitted field as // "no change"). - payload := map[string]interface{}{} + payload := map[string]any{} if cmd.Flags().Changed("name") { payload["name"] = o.name } diff --git a/cmd/kosli/updateServiceAccount.go b/cmd/kosli/updateServiceAccount.go index b3b941a83..c6dbd8cc6 100644 --- a/cmd/kosli/updateServiceAccount.go +++ b/cmd/kosli/updateServiceAccount.go @@ -70,7 +70,7 @@ func (o *updateServiceAccountOptions) run(cmd *cobra.Command, args []string) err // Only send the fields the user explicitly set, so unset flags leave the // corresponding values unchanged (the server treats an omitted field as // "no change"). - payload := map[string]interface{}{} + payload := map[string]any{} if cmd.Flags().Changed("description") { payload["description"] = o.description } diff --git a/internal/azure/azure_apps.go b/internal/azure/azure_apps.go index 5276dfc91..b40909d8b 100644 --- a/internal/azure/azure_apps.go +++ b/internal/azure/azure_apps.go @@ -202,7 +202,7 @@ func (azureClient *AzureClient) getBearerToken(logger *logger.Logger) (string, e if err != nil { return "", err } - var oauthResp map[string]interface{} + var oauthResp map[string]any err = json.Unmarshal(body, &oauthResp) if err != nil { return "", err diff --git a/internal/evaluate/opa_contract_test.go b/internal/evaluate/opa_contract_test.go index 4a61b45c8..c75a2fa22 100644 --- a/internal/evaluate/opa_contract_test.go +++ b/internal/evaluate/opa_contract_test.go @@ -31,25 +31,25 @@ import ( // realisticTrailInput mirrors the shape produced by TransformTrail + // RehydrateTrail: attestations keyed by name, each carrying the fields the // Kosli API returns. -func realisticTrailInput() map[string]interface{} { - return map[string]interface{}{ - "trail": map[string]interface{}{ +func realisticTrailInput() map[string]any { + return map[string]any{ + "trail": map[string]any{ "name": "release-42", - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "pull-request": map[string]interface{}{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "pull-request": map[string]any{ "attestation_name": "pull-request", "compliant": true, "html_url": "https://app.kosli.com/kosli/flows/cli/trails/release-42", "created_at": "2026-01-14T09:30:00Z", }, - "unit-tests": map[string]interface{}{ + "unit-tests": map[string]any{ "attestation_name": "unit-tests", "compliant": true, "html_url": "https://app.kosli.com/kosli/flows/cli/trails/release-42", "created_at": "2026-01-14T09:35:00Z", }, - "snyk-scan": map[string]interface{}{ + "snyk-scan": map[string]any{ "attestation_name": "snyk-scan", "compliant": false, "html_url": "https://app.kosli.com/kosli/flows/cli/trails/release-42", @@ -87,8 +87,8 @@ func TestOPAContract_RealisticPolicyAllows(t *testing.T) { func TestOPAContract_RealisticPolicyDeniesOnDefaultRequirements(t *testing.T) { input := realisticTrailInput() - attestations := input["trail"].(map[string]interface{})["compliance_status"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) - attestations["unit-tests"].(map[string]interface{})["compliant"] = false + attestations := input["trail"].(map[string]any)["compliance_status"].(map[string]any)["attestations_statuses"].(map[string]any) + attestations["unit-tests"].(map[string]any)["compliant"] = false result, err := Evaluate(realisticPolicy, input, nil) require.NoError(t, err) @@ -97,8 +97,8 @@ func TestOPAContract_RealisticPolicyDeniesOnDefaultRequirements(t *testing.T) { } func TestOPAContract_RealisticPolicyDeniesWithViolations(t *testing.T) { - params := map[string]interface{}{ - "required_attestations": []interface{}{"pull-request", "snyk-scan", "sbom"}, + params := map[string]any{ + "required_attestations": []any{"pull-request", "snyk-scan", "sbom"}, } result, err := Evaluate(realisticPolicy, realisticTrailInput(), params) @@ -151,7 +151,7 @@ allow { } ` - _, err := Evaluate(policy, map[string]interface{}{"score": 5}, nil) + _, err := Evaluate(policy, map[string]any{"score": 5}, nil) require.Error(t, err) require.Contains(t, err.Error(), "failed to parse policy") require.Contains(t, err.Error(), "`if` keyword is required before rule body") @@ -173,7 +173,7 @@ allow if { } ` - result, err := Evaluate(policy, map[string]interface{}{"score": 5}, nil) + result, err := Evaluate(policy, map[string]any{"score": 5}, nil) require.NoError(t, err) require.True(t, result.Allow) } @@ -194,7 +194,7 @@ allow if { } ` - _, err := Evaluate(policy, map[string]interface{}{}, nil) + _, err := Evaluate(policy, map[string]any{}, nil) require.Error(t, err) require.Contains(t, err.Error(), "rego_unsafe_var_error") require.Contains(t, err.Error(), "policy.rego:6") @@ -219,7 +219,7 @@ allow if { } ` - _, err := Evaluate(policy, map[string]interface{}{}, nil) + _, err := Evaluate(policy, map[string]any{}, nil) require.Error(t, err) require.Contains(t, err.Error(), "rego_unsafe_var_error") require.Contains(t, err.Error(), "var y is unsafe") @@ -241,7 +241,7 @@ allow if { } ` - input := map[string]interface{}{"score": 5, "checks": []interface{}{"a", "b", "c"}} + input := map[string]any{"score": 5, "checks": []any{"a", "b", "c"}} result, err := Evaluate(policy, input, nil) require.NoError(t, err) @@ -262,7 +262,7 @@ violations contains msg if { } ` - result, err := Evaluate(policy, map[string]interface{}{}, nil) + result, err := Evaluate(policy, map[string]any{}, nil) require.Error(t, err) require.Nil(t, result) require.Contains(t, err.Error(), "rego_unsafe_var_error") @@ -278,7 +278,7 @@ func TestOPAContract_UndefinedAllowIsAnError(t *testing.T) { for _, tc := range []struct { name string policy string - input map[string]interface{} + input map[string]any }{ { name: "no default and the body does not hold", @@ -288,7 +288,7 @@ allow if { input.score > 3 } `, - input: map[string]interface{}{"score": 1}, + input: map[string]any{"score": 1}, }, { name: "expression is undefined at evaluation time", @@ -299,7 +299,7 @@ allow if { ratio > 0.5 } `, - input: map[string]interface{}{"passed": 3, "total": 0}, + input: map[string]any{"passed": 3, "total": 0}, }, } { t.Run(tc.name, func(t *testing.T) { @@ -340,7 +340,7 @@ violations contains msg if { t.Run(tc.name, func(t *testing.T) { policy := "package policy\n\ndefault allow := false\n\n" + tc.rules + "\n" - result, err := Evaluate(policy, map[string]interface{}{}, nil) + result, err := Evaluate(policy, map[string]any{}, nil) require.NoError(t, err) require.False(t, result.Allow) require.Equal(t, tc.expect, result.Violations) @@ -404,7 +404,7 @@ allow if { } ` - result, err := Evaluate(policy, map[string]interface{}{}, map[string]interface{}{"threshold": 7}) + result, err := Evaluate(policy, map[string]any{}, map[string]any{"threshold": 7}) require.NoError(t, err) require.True(t, result.Allow) } @@ -431,9 +431,9 @@ allow if { } ` - input := map[string]interface{}{"score": 5} + input := map[string]any{"score": 5} - withParams, err := Evaluate(policy, input, map[string]interface{}{"unrelated": true}) + withParams, err := Evaluate(policy, input, map[string]any{"unrelated": true}) require.NoError(t, err) require.True(t, withParams.Allow, "with a store installed, object.get returns the fallback of 3") diff --git a/internal/evaluate/rego.go b/internal/evaluate/rego.go index 82d7f12c5..db50a9230 100644 --- a/internal/evaluate/rego.go +++ b/internal/evaluate/rego.go @@ -18,7 +18,7 @@ type Result struct { // Evaluate evaluates a Rego policy against the given input. // The policy must use `package policy` and declare an `allow` rule. // An optional params map can be provided to populate data.params in the policy. -func Evaluate(policySource string, input interface{}, params map[string]interface{}) (*Result, error) { +func Evaluate(policySource string, input any, params map[string]any) (*Result, error) { if err := validatePolicy(policySource); err != nil { return nil, err } @@ -31,7 +31,7 @@ func Evaluate(policySource string, input interface{}, params map[string]interfac rego.Input(input), } if params != nil { - store := inmem.NewFromObject(map[string]interface{}{"params": params}) + store := inmem.NewFromObject(map[string]any{"params": params}) opts = append(opts, rego.Store(store)) } @@ -89,14 +89,14 @@ func validatePolicy(policySource string) error { return nil } -func collectViolations(ctx context.Context, policySource string, input interface{}, params map[string]interface{}) ([]string, error) { +func collectViolations(ctx context.Context, policySource string, input any, params map[string]any) ([]string, error) { opts := []func(*rego.Rego){ rego.Query("data.policy.violations"), rego.Module("policy.rego", policySource), rego.Input(input), } if params != nil { - store := inmem.NewFromObject(map[string]interface{}{"params": params}) + store := inmem.NewFromObject(map[string]any{"params": params}) opts = append(opts, rego.Store(store)) } @@ -109,7 +109,7 @@ func collectViolations(ctx context.Context, policySource string, input interface var violations []string if len(rs) > 0 && len(rs[0].Expressions) > 0 { - if vs, ok := rs[0].Expressions[0].Value.([]interface{}); ok { + if vs, ok := rs[0].Expressions[0].Value.([]any); ok { for _, v := range vs { if s, ok := v.(string); ok { violations = append(violations, s) diff --git a/internal/evaluate/rego_test.go b/internal/evaluate/rego_test.go index ec19ff196..d5d047812 100644 --- a/internal/evaluate/rego_test.go +++ b/internal/evaluate/rego_test.go @@ -11,8 +11,8 @@ func TestEvaluate_AllowAllPolicy(t *testing.T) { allow = true ` - input := map[string]interface{}{ - "trail": map[string]interface{}{ + input := map[string]any{ + "trail": map[string]any{ "name": "test-trail", }, } @@ -32,8 +32,8 @@ violations contains msg if { msg := "always denied" } ` - input := map[string]interface{}{ - "trail": map[string]interface{}{ + input := map[string]any{ + "trail": map[string]any{ "name": "test-trail", }, } @@ -49,7 +49,7 @@ func TestEvaluate_MissingPackagePolicy(t *testing.T) { allow = true ` - input := map[string]interface{}{} + input := map[string]any{} _, err := Evaluate(policy, input, nil) require.Error(t, err) @@ -63,7 +63,7 @@ violations contains msg if { msg := "no allow rule" } ` - input := map[string]interface{}{} + input := map[string]any{} _, err := Evaluate(policy, input, nil) require.Error(t, err) @@ -75,7 +75,7 @@ func TestEvaluate_NoViolationsRule(t *testing.T) { allow = false ` - input := map[string]interface{}{} + input := map[string]any{} result, err := Evaluate(policy, input, nil) require.NoError(t, err) @@ -88,7 +88,7 @@ func TestEvaluate_NonBooleanAllow(t *testing.T) { allow = "yes" ` - input := map[string]interface{}{} + input := map[string]any{} _, err := Evaluate(policy, input, nil) require.Error(t, err) @@ -100,7 +100,7 @@ func TestEvaluate_SyntaxError(t *testing.T) { allow = {{{ ` - input := map[string]interface{}{} + input := map[string]any{} _, err := Evaluate(policy, input, nil) require.Error(t, err) @@ -125,10 +125,10 @@ violations contains msg if { msg := sprintf("score %d is below threshold %d", [input.score, threshold]) } ` - input := map[string]interface{}{ + input := map[string]any{ "score": 5, } - params := map[string]interface{}{ + params := map[string]any{ "threshold": 3, } @@ -155,7 +155,7 @@ violations contains msg if { msg := sprintf("score %d is below threshold %d", [input.score, threshold]) } ` - input := map[string]interface{}{ + input := map[string]any{ "score": 5, } @@ -172,8 +172,8 @@ func TestEvaluate_ParamsIgnoredByPolicy(t *testing.T) { allow = true ` - input := map[string]interface{}{} - params := map[string]interface{}{ + input := map[string]any{} + params := map[string]any{ "unused_key": "unused_value", } diff --git a/internal/evaluate/transform.go b/internal/evaluate/transform.go index b3bdc0b33..0d86f30e3 100644 --- a/internal/evaluate/transform.go +++ b/internal/evaluate/transform.go @@ -4,31 +4,31 @@ import "strings" // TransformTrail converts attestations_statuses arrays in trail data // to maps keyed by attestation_name for easier Rego policy access. -func TransformTrail(trailData interface{}) interface{} { +func TransformTrail(trailData any) any { if trailData == nil { return nil } - trailMap, ok := trailData.(map[string]interface{}) + trailMap, ok := trailData.(map[string]any) if !ok { return trailData } - cs, ok := trailMap["compliance_status"].(map[string]interface{}) + cs, ok := trailMap["compliance_status"].(map[string]any) if !ok { return trailData } - if arr, ok := cs["attestations_statuses"].([]interface{}); ok { + if arr, ok := cs["attestations_statuses"].([]any); ok { cs["attestations_statuses"] = attestationsArrayToMap(arr) } - if artifacts, ok := cs["artifacts_statuses"].(map[string]interface{}); ok { + if artifacts, ok := cs["artifacts_statuses"].(map[string]any); ok { for _, artData := range artifacts { - artMap, ok := artData.(map[string]interface{}) + artMap, ok := artData.(map[string]any) if !ok { continue } - if arr, ok := artMap["attestations_statuses"].([]interface{}); ok { + if arr, ok := artMap["attestations_statuses"].([]any); ok { artMap["attestations_statuses"] = attestationsArrayToMap(arr) } } @@ -39,10 +39,10 @@ func TransformTrail(trailData interface{}) interface{} { // CollectAttestationIDs extracts all non-null attestation_id values // from the already-transformed (map-keyed) trail data. -func CollectAttestationIDs(trailData interface{}) []string { +func CollectAttestationIDs(trailData any) []string { var ids []string seen := make(map[string]bool) - walkTrailAttestations(trailData, func(_ string, as map[string]interface{}) { + walkTrailAttestations(trailData, func(_ string, as map[string]any) { for _, id := range collectIDsFromAttestationMap(as) { if !seen[id] { seen[id] = true @@ -55,11 +55,11 @@ func CollectAttestationIDs(trailData interface{}) []string { // RehydrateTrail merges attestation detail data into the already-transformed // trail data. Fields from details are added where the key doesn't already exist. -func RehydrateTrail(trailData interface{}, details map[string]interface{}) interface{} { +func RehydrateTrail(trailData any, details map[string]any) any { if len(details) == 0 { return trailData } - walkTrailAttestations(trailData, func(_ string, as map[string]interface{}) { + walkTrailAttestations(trailData, func(_ string, as map[string]any) { rehydrateAttestationMap(as, details) }) return trailData @@ -69,14 +69,14 @@ func RehydrateTrail(trailData interface{}, details map[string]interface{}) inter // When filters is nil or empty, all attestations are included unchanged. // Plain names (e.g. "pull-request") filter trail-level attestations. // Dot-qualified names (e.g. "cli.unit-test") filter artifact-level attestations. -func FilterAttestations(trailData interface{}, filters []string) interface{} { +func FilterAttestations(trailData any, filters []string) any { if len(filters) == 0 { return trailData } trailFilters, artifactFilters := parseAttestationFilters(filters) - walkTrailAttestations(trailData, func(artifactName string, as map[string]interface{}) { + walkTrailAttestations(trailData, func(artifactName string, as map[string]any) { if artifactName == "" { filterMap(as, trailFilters) } else if allowed, exists := artifactFilters[artifactName]; exists { @@ -94,27 +94,27 @@ func FilterAttestations(trailData interface{}, filters []string) interface{} { // walkTrailAttestations navigates the trail data structure and calls fn // for each attestations_statuses map found. The artifactName is "" for // trail-level attestations, or the artifact name for artifact-level ones. -func walkTrailAttestations(trailData interface{}, fn func(artifactName string, as map[string]interface{})) { - trailMap, ok := trailData.(map[string]interface{}) +func walkTrailAttestations(trailData any, fn func(artifactName string, as map[string]any)) { + trailMap, ok := trailData.(map[string]any) if !ok { return } - cs, ok := trailMap["compliance_status"].(map[string]interface{}) + cs, ok := trailMap["compliance_status"].(map[string]any) if !ok { return } - if as, ok := cs["attestations_statuses"].(map[string]interface{}); ok { + if as, ok := cs["attestations_statuses"].(map[string]any); ok { fn("", as) } - if artifacts, ok := cs["artifacts_statuses"].(map[string]interface{}); ok { + if artifacts, ok := cs["artifacts_statuses"].(map[string]any); ok { for artName, artData := range artifacts { - artMap, ok := artData.(map[string]interface{}) + artMap, ok := artData.(map[string]any) if !ok { continue } - if as, ok := artMap["attestations_statuses"].(map[string]interface{}); ok { + if as, ok := artMap["attestations_statuses"].(map[string]any); ok { fn(artName, as) } } @@ -145,7 +145,7 @@ func parseAttestationFilters(filters []string) (trailFilters map[string]bool, ar return } -func filterMap(m map[string]interface{}, keep map[string]bool) { +func filterMap(m map[string]any, keep map[string]bool) { for k := range m { if !keep[k] { delete(m, k) @@ -153,9 +153,9 @@ func filterMap(m map[string]interface{}, keep map[string]bool) { } } -func rehydrateAttestationMap(attestations map[string]interface{}, details map[string]interface{}) { +func rehydrateAttestationMap(attestations map[string]any, details map[string]any) { for _, v := range attestations { - entry, ok := v.(map[string]interface{}) + entry, ok := v.(map[string]any) if !ok { continue } @@ -163,7 +163,7 @@ func rehydrateAttestationMap(attestations map[string]interface{}, details map[st if !ok { continue } - detail, ok := details[id].(map[string]interface{}) + detail, ok := details[id].(map[string]any) if !ok { continue } @@ -175,10 +175,10 @@ func rehydrateAttestationMap(attestations map[string]interface{}, details map[st } } -func collectIDsFromAttestationMap(m map[string]interface{}) []string { +func collectIDsFromAttestationMap(m map[string]any) []string { var ids []string for _, v := range m { - entry, ok := v.(map[string]interface{}) + entry, ok := v.(map[string]any) if !ok { continue } @@ -189,10 +189,10 @@ func collectIDsFromAttestationMap(m map[string]interface{}) []string { return ids } -func attestationsArrayToMap(arr []interface{}) map[string]interface{} { - result := make(map[string]interface{}) +func attestationsArrayToMap(arr []any) map[string]any { + result := make(map[string]any) for _, entry := range arr { - entryMap, ok := entry.(map[string]interface{}) + entryMap, ok := entry.(map[string]any) if !ok { continue } diff --git a/internal/evaluate/transform_test.go b/internal/evaluate/transform_test.go index f03c2eb8f..693e3b1c9 100644 --- a/internal/evaluate/transform_test.go +++ b/internal/evaluate/transform_test.go @@ -20,34 +20,34 @@ func TestTransformTrail(t *testing.T) { }) t.Run("trail with no compliance_status passes through", func(t *testing.T) { - input := map[string]interface{}{ + input := map[string]any{ "name": "my-trail", } result := TransformTrail(input) - resultMap := result.(map[string]interface{}) + resultMap := result.(map[string]any) assert.Equal(t, "my-trail", resultMap["name"]) assert.Nil(t, resultMap["compliance_status"]) }) t.Run("empty attestations_statuses array becomes empty map", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": []interface{}{}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": []any{}, }, } result := TransformTrail(input) - resultMap := result.(map[string]interface{}) - cs := resultMap["compliance_status"].(map[string]interface{}) + resultMap := result.(map[string]any) + cs := resultMap["compliance_status"].(map[string]any) as := cs["attestations_statuses"] - require.IsType(t, map[string]interface{}{}, as) + require.IsType(t, map[string]any{}, as) assert.Empty(t, as) }) t.Run("single trail-level attestation becomes map entry keyed by name", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": []any{ + map[string]any{ "attestation_name": "bar", "is_compliant": true, }, @@ -55,27 +55,27 @@ func TestTransformTrail(t *testing.T) { }, } result := TransformTrail(input) - resultMap := result.(map[string]interface{}) - cs := resultMap["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) - bar := as["bar"].(map[string]interface{}) + resultMap := result.(map[string]any) + cs := resultMap["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) + bar := as["bar"].(map[string]any) assert.Equal(t, "bar", bar["attestation_name"]) assert.Equal(t, true, bar["is_compliant"]) }) t.Run("multiple trail-level attestations all present in map", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "alpha"}, - map[string]interface{}{"attestation_name": "beta"}, - map[string]interface{}{"attestation_name": "gamma"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "alpha"}, + map[string]any{"attestation_name": "beta"}, + map[string]any{"attestation_name": "gamma"}, }, }, } result := TransformTrail(input) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Len(t, as, 3) assert.Contains(t, as, "alpha") assert.Contains(t, as, "beta") @@ -83,88 +83,88 @@ func TestTransformTrail(t *testing.T) { }) t.Run("artifact-level attestations_statuses array becomes map", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "foo", "is_compliant": true}, + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "foo", "is_compliant": true}, }, }, }, }, } result := TransformTrail(input) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - cli := cs["artifacts_statuses"].(map[string]interface{})["cli"].(map[string]interface{}) - as := cli["attestations_statuses"].(map[string]interface{}) - foo := as["foo"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + cli := cs["artifacts_statuses"].(map[string]any)["cli"].(map[string]any) + as := cli["attestations_statuses"].(map[string]any) + foo := as["foo"].(map[string]any) assert.Equal(t, "foo", foo["attestation_name"]) assert.Equal(t, true, foo["is_compliant"]) }) t.Run("both trail-level and artifact-level transform in one call", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "trail-att"}, - }, - "artifacts_statuses": map[string]interface{}{ - "art1": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "art-att"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "trail-att"}, + }, + "artifacts_statuses": map[string]any{ + "art1": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "art-att"}, }, }, }, }, } result := TransformTrail(input) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - trailAs := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + trailAs := cs["attestations_statuses"].(map[string]any) assert.Contains(t, trailAs, "trail-att") - art1 := cs["artifacts_statuses"].(map[string]interface{})["art1"].(map[string]interface{}) - artAs := art1["attestations_statuses"].(map[string]interface{}) + art1 := cs["artifacts_statuses"].(map[string]any)["art1"].(map[string]any) + artAs := art1["attestations_statuses"].(map[string]any) assert.Contains(t, artAs, "art-att") }) t.Run("multiple artifacts each get their attestations transformed", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "art1": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "a1"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "art1": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "a1"}, }, }, - "art2": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "a2"}, + "art2": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "a2"}, }, }, }, }, } result := TransformTrail(input) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - arts := cs["artifacts_statuses"].(map[string]interface{}) - art1As := arts["art1"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + arts := cs["artifacts_statuses"].(map[string]any) + art1As := arts["art1"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Contains(t, art1As, "a1") - art2As := arts["art2"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + art2As := arts["art2"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Contains(t, art2As, "a2") }) t.Run("entry without attestation_name is skipped", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": []interface{}{ - map[string]interface{}{"attestation_name": "good"}, - map[string]interface{}{"something_else": "no name"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": []any{ + map[string]any{"attestation_name": "good"}, + map[string]any{"something_else": "no name"}, }, }, } result := TransformTrail(input) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Len(t, as, 1) assert.Contains(t, as, "good") }) @@ -177,7 +177,7 @@ func TestCollectAttestationIDs(t *testing.T) { }) t.Run("trail with no compliance_status returns empty slice", func(t *testing.T) { - input := map[string]interface{}{ + input := map[string]any{ "name": "my-trail", } ids := CollectAttestationIDs(input) @@ -185,10 +185,10 @@ func TestCollectAttestationIDs(t *testing.T) { }) t.Run("collects ID from trail-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", }, @@ -200,18 +200,18 @@ func TestCollectAttestationIDs(t *testing.T) { }) t.Run("skips entries with null or missing attestation_id", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "has-id": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "has-id": map[string]any{ "attestation_name": "has-id", "attestation_id": "att-uuid-001", }, - "null-id": map[string]interface{}{ + "null-id": map[string]any{ "attestation_name": "null-id", "attestation_id": nil, }, - "missing-id": map[string]interface{}{ + "missing-id": map[string]any{ "attestation_name": "missing-id", }, }, @@ -222,12 +222,12 @@ func TestCollectAttestationIDs(t *testing.T) { }) t.Run("collects IDs from artifact-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{ "attestation_name": "foo", "attestation_id": "att-uuid-002", }, @@ -241,18 +241,18 @@ func TestCollectAttestationIDs(t *testing.T) { }) t.Run("collects from both trail-level and artifact-level", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "trail-att": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "trail-att": map[string]any{ "attestation_name": "trail-att", "attestation_id": "att-trail-001", }, }, - "artifacts_statuses": map[string]interface{}{ - "art1": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "art-att": map[string]interface{}{ + "artifacts_statuses": map[string]any{ + "art1": map[string]any{ + "attestations_statuses": map[string]any{ + "art-att": map[string]any{ "attestation_name": "art-att", "attestation_id": "att-art-001", }, @@ -270,18 +270,18 @@ func TestCollectAttestationIDs(t *testing.T) { func TestCollectAttestationIDs_Deduplication(t *testing.T) { t.Run("duplicate IDs across trail and artifact level are deduplicated", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "trail-att": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "trail-att": map[string]any{ "attestation_name": "trail-att", "attestation_id": "att-uuid-001", }, }, - "artifacts_statuses": map[string]interface{}{ - "art1": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "art-att": map[string]interface{}{ + "artifacts_statuses": map[string]any{ + "art1": map[string]any{ + "attestations_statuses": map[string]any{ + "art-att": map[string]any{ "attestation_name": "art-att", "attestation_id": "att-uuid-001", }, @@ -297,185 +297,185 @@ func TestCollectAttestationIDs_Deduplication(t *testing.T) { func TestFilterAttestations(t *testing.T) { t.Run("nil filters returns trail unchanged", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", }, }, }, } result := FilterAttestations(input, nil) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Contains(t, as, "bar") }) t.Run("empty filters slice returns trail unchanged", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", }, }, }, } result := FilterAttestations(input, []string{}) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Contains(t, as, "bar") }) t.Run("plain name keeps only matching trail-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", }, - "baz": map[string]interface{}{ + "baz": map[string]any{ "attestation_name": "baz", }, }, }, } result := FilterAttestations(input, []string{"bar"}) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Contains(t, as, "bar") assert.NotContains(t, as, "baz") }) t.Run("plain name removes non-matching trail-level attestations", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "alpha": map[string]interface{}{"attestation_name": "alpha"}, - "beta": map[string]interface{}{"attestation_name": "beta"}, - "gamma": map[string]interface{}{"attestation_name": "gamma"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "alpha": map[string]any{"attestation_name": "alpha"}, + "beta": map[string]any{"attestation_name": "beta"}, + "gamma": map[string]any{"attestation_name": "gamma"}, }, }, } result := FilterAttestations(input, []string{"beta"}) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - as := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + as := cs["attestations_statuses"].(map[string]any) assert.Len(t, as, 1) assert.Contains(t, as, "beta") }) t.Run("dot-qualified name keeps only matching artifact-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{"attestation_name": "foo"}, - "bar": map[string]interface{}{"attestation_name": "bar"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{"attestation_name": "foo"}, + "bar": map[string]any{"attestation_name": "bar"}, }, }, }, }, } result := FilterAttestations(input, []string{"cli.foo"}) - cli := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["artifacts_statuses"].(map[string]interface{})["cli"].(map[string]interface{}) - as := cli["attestations_statuses"].(map[string]interface{}) + cli := result.(map[string]any)["compliance_status"].(map[string]any)["artifacts_statuses"].(map[string]any)["cli"].(map[string]any) + as := cli["attestations_statuses"].(map[string]any) assert.Len(t, as, 1) assert.Contains(t, as, "foo") }) t.Run("dot-qualified name: unmentioned artifact gets empty attestations_statuses", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{"attestation_name": "foo"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{"attestation_name": "foo"}, }, }, - "server": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{"attestation_name": "bar"}, + "server": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{"attestation_name": "bar"}, }, }, }, }, } result := FilterAttestations(input, []string{"cli.foo"}) - arts := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["artifacts_statuses"].(map[string]interface{}) + arts := result.(map[string]any)["compliance_status"].(map[string]any)["artifacts_statuses"].(map[string]any) // cli keeps foo - cliAs := arts["cli"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + cliAs := arts["cli"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Len(t, cliAs, 1) assert.Contains(t, cliAs, "foo") // server gets empty attestations_statuses - serverAs := arts["server"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + serverAs := arts["server"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Empty(t, serverAs) }) t.Run("mixed filters: trail-level and artifact-level both applied", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "trail-att": map[string]interface{}{"attestation_name": "trail-att"}, - "trail-other": map[string]interface{}{"attestation_name": "trail-other"}, - }, - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "art-att": map[string]interface{}{"attestation_name": "art-att"}, - "art-other": map[string]interface{}{"attestation_name": "art-other"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "trail-att": map[string]any{"attestation_name": "trail-att"}, + "trail-other": map[string]any{"attestation_name": "trail-other"}, + }, + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "art-att": map[string]any{"attestation_name": "art-att"}, + "art-other": map[string]any{"attestation_name": "art-other"}, }, }, }, }, } result := FilterAttestations(input, []string{"trail-att", "cli.art-att"}) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - trailAs := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + trailAs := cs["attestations_statuses"].(map[string]any) assert.Len(t, trailAs, 1) assert.Contains(t, trailAs, "trail-att") - cliAs := cs["artifacts_statuses"].(map[string]interface{})["cli"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + cliAs := cs["artifacts_statuses"].(map[string]any)["cli"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Len(t, cliAs, 1) assert.Contains(t, cliAs, "art-att") }) t.Run("filters with no matches leave all attestations_statuses empty", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{"attestation_name": "bar"}, - }, - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{"attestation_name": "foo"}, + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{"attestation_name": "bar"}, + }, + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{"attestation_name": "foo"}, }, }, }, }, } result := FilterAttestations(input, []string{"nonexistent"}) - cs := result.(map[string]interface{})["compliance_status"].(map[string]interface{}) - trailAs := cs["attestations_statuses"].(map[string]interface{}) + cs := result.(map[string]any)["compliance_status"].(map[string]any) + trailAs := cs["attestations_statuses"].(map[string]any) assert.Empty(t, trailAs) - cliAs := cs["artifacts_statuses"].(map[string]interface{})["cli"].(map[string]interface{})["attestations_statuses"].(map[string]interface{}) + cliAs := cs["artifacts_statuses"].(map[string]any)["cli"].(map[string]any)["attestations_statuses"].(map[string]any) assert.Empty(t, cliAs) }) } func TestFilterAttestations_MalformedFilters(t *testing.T) { - makeInput := func() interface{} { - return map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{"attestation_name": "bar"}, - "baz": map[string]interface{}{"attestation_name": "baz"}, - }, - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{"attestation_name": "foo"}, + makeInput := func() any { + return map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{"attestation_name": "bar"}, + "baz": map[string]any{"attestation_name": "baz"}, + }, + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{"attestation_name": "foo"}, }, }, }, @@ -512,10 +512,10 @@ func TestFilterAttestations_MalformedFilters(t *testing.T) { func TestRehydrateTrail(t *testing.T) { t.Run("nil details map leaves trail unchanged", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", }, @@ -523,36 +523,36 @@ func TestRehydrateTrail(t *testing.T) { }, } result := RehydrateTrail(input, nil) - resultMap := result.(map[string]interface{}) - cs := resultMap["compliance_status"].(map[string]interface{}) - bar := cs["attestations_statuses"].(map[string]interface{})["bar"].(map[string]interface{}) + resultMap := result.(map[string]any) + cs := resultMap["compliance_status"].(map[string]any) + bar := cs["attestations_statuses"].(map[string]any)["bar"].(map[string]any) assert.Equal(t, "bar", bar["attestation_name"]) assert.Equal(t, "att-uuid-001", bar["attestation_id"]) assert.Nil(t, bar["origin_url"]) }) t.Run("empty details map leaves trail unchanged", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", }, }, }, } - result := RehydrateTrail(input, map[string]interface{}{}) - bar := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["attestations_statuses"].(map[string]interface{})["bar"].(map[string]interface{}) + result := RehydrateTrail(input, map[string]any{}) + bar := result.(map[string]any)["compliance_status"].(map[string]any)["attestations_statuses"].(map[string]any)["bar"].(map[string]any) assert.Equal(t, "bar", bar["attestation_name"]) assert.Nil(t, bar["origin_url"]) }) t.Run("merges detail fields into trail-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", "is_compliant": true, @@ -560,24 +560,24 @@ func TestRehydrateTrail(t *testing.T) { }, }, } - details := map[string]interface{}{ - "att-uuid-001": map[string]interface{}{ + details := map[string]any{ + "att-uuid-001": map[string]any{ "origin_url": "https://github.com/org/repo/pull/42", - "user_data": map[string]interface{}{"key": "value"}, + "user_data": map[string]any{"key": "value"}, }, } result := RehydrateTrail(input, details) - bar := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["attestations_statuses"].(map[string]interface{})["bar"].(map[string]interface{}) + bar := result.(map[string]any)["compliance_status"].(map[string]any)["attestations_statuses"].(map[string]any)["bar"].(map[string]any) assert.Equal(t, "https://github.com/org/repo/pull/42", bar["origin_url"]) - assert.Equal(t, map[string]interface{}{"key": "value"}, bar["user_data"]) + assert.Equal(t, map[string]any{"key": "value"}, bar["user_data"]) assert.Equal(t, true, bar["is_compliant"]) }) t.Run("does not overwrite existing fields", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", "status": "COMPLETE", @@ -585,25 +585,25 @@ func TestRehydrateTrail(t *testing.T) { }, }, } - details := map[string]interface{}{ - "att-uuid-001": map[string]interface{}{ + details := map[string]any{ + "att-uuid-001": map[string]any{ "status": "DIFFERENT", "origin_url": "https://example.com", }, } result := RehydrateTrail(input, details) - bar := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["attestations_statuses"].(map[string]interface{})["bar"].(map[string]interface{}) + bar := result.(map[string]any)["compliance_status"].(map[string]any)["attestations_statuses"].(map[string]any)["bar"].(map[string]any) assert.Equal(t, "COMPLETE", bar["status"], "existing field should not be overwritten") assert.Equal(t, "https://example.com", bar["origin_url"], "new field should be added") }) t.Run("merges detail fields into artifact-level attestation", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "artifacts_statuses": map[string]interface{}{ - "cli": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "foo": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "artifacts_statuses": map[string]any{ + "cli": map[string]any{ + "attestations_statuses": map[string]any{ + "foo": map[string]any{ "attestation_name": "foo", "attestation_id": "att-uuid-002", "is_compliant": true, @@ -613,22 +613,22 @@ func TestRehydrateTrail(t *testing.T) { }, }, } - details := map[string]interface{}{ - "att-uuid-002": map[string]interface{}{ + details := map[string]any{ + "att-uuid-002": map[string]any{ "origin_url": "https://example.com/artifact", }, } result := RehydrateTrail(input, details) - foo := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["artifacts_statuses"].(map[string]interface{})["cli"].(map[string]interface{})["attestations_statuses"].(map[string]interface{})["foo"].(map[string]interface{}) + foo := result.(map[string]any)["compliance_status"].(map[string]any)["artifacts_statuses"].(map[string]any)["cli"].(map[string]any)["attestations_statuses"].(map[string]any)["foo"].(map[string]any) assert.Equal(t, "https://example.com/artifact", foo["origin_url"]) assert.Equal(t, true, foo["is_compliant"]) }) t.Run("attestation with no matching detail is left unchanged", func(t *testing.T) { - input := map[string]interface{}{ - "compliance_status": map[string]interface{}{ - "attestations_statuses": map[string]interface{}{ - "bar": map[string]interface{}{ + input := map[string]any{ + "compliance_status": map[string]any{ + "attestations_statuses": map[string]any{ + "bar": map[string]any{ "attestation_name": "bar", "attestation_id": "att-uuid-001", "is_compliant": true, @@ -636,13 +636,13 @@ func TestRehydrateTrail(t *testing.T) { }, }, } - details := map[string]interface{}{ - "att-uuid-999": map[string]interface{}{ + details := map[string]any{ + "att-uuid-999": map[string]any{ "origin_url": "https://example.com", }, } result := RehydrateTrail(input, details) - bar := result.(map[string]interface{})["compliance_status"].(map[string]interface{})["attestations_statuses"].(map[string]interface{})["bar"].(map[string]interface{}) + bar := result.(map[string]any)["compliance_status"].(map[string]any)["attestations_statuses"].(map[string]any)["bar"].(map[string]any) assert.Equal(t, "bar", bar["attestation_name"]) assert.Equal(t, true, bar["is_compliant"]) assert.Nil(t, bar["origin_url"], "no matching detail means no new fields") diff --git a/internal/evaluations/client.go b/internal/evaluations/client.go index fb114738b..234be7b3a 100644 --- a/internal/evaluations/client.go +++ b/internal/evaluations/client.go @@ -34,7 +34,7 @@ type TrailRef struct { type CreateRequest struct { Trails []TrailRef Files map[string]string - Params map[string]interface{} + Params map[string]any Decision *Decision } @@ -114,7 +114,7 @@ func (c *Client) Create(org string, request CreateRequest) (*Evaluation, error) if params == nil { // The server's field is a plain object defaulting to empty, so null // fails its validation where an empty object is accepted. - params = map[string]interface{}{} + params = map[string]any{} } response, err := c.http.Do(&requests.RequestParams{ @@ -142,10 +142,10 @@ func (c *Client) Create(org string, request CreateRequest) (*Evaluation, error) // every model behind this endpoint forbids unknown fields: the wire shape has // to be stated exactly here rather than inherited from a caller's struct. type createPayload struct { - Context createContext `json:"context"` - Policy inlinePolicy `json:"policy"` - Params map[string]interface{} `json:"params"` - Decision *Decision `json:"decision,omitempty"` + Context createContext `json:"context"` + Policy inlinePolicy `json:"policy"` + Params map[string]any `json:"params"` + Decision *Decision `json:"decision,omitempty"` } type createContext struct { diff --git a/internal/evaluations/client_test.go b/internal/evaluations/client_test.go index e559ffbbf..5e8aec5b7 100644 --- a/internal/evaluations/client_test.go +++ b/internal/evaluations/client_test.go @@ -21,7 +21,7 @@ type received struct { path string authHeader string contentType string - body map[string]interface{} + body map[string]any } func newFakeServer(t *testing.T, status int, responseBody string) (*httptest.Server, *received) { @@ -61,7 +61,7 @@ func aCreateRequest() CreateRequest { return CreateRequest{ Trails: []TrailRef{{Flow: "release", Trail: "my-trail"}}, Files: map[string]string{"policy.rego": "package policy\n\nallow := true\n"}, - Params: map[string]interface{}{"threshold": float64(2)}, + Params: map[string]any{"threshold": float64(2)}, } } @@ -90,21 +90,21 @@ func TestCreateSendsExactlyTheAgreedPayload(t *testing.T) { // is a 400 rather than something ignored. require.ElementsMatch(t, []string{"context", "policy", "params"}, keysOf(seen.body)) - context, ok := seen.body["context"].(map[string]interface{}) + context, ok := seen.body["context"].(map[string]any) require.True(t, ok) require.Equal(t, []string{"trails"}, keysOf(context)) - require.Equal(t, []interface{}{ - map[string]interface{}{"flow": "release", "trail": "my-trail"}, + require.Equal(t, []any{ + map[string]any{"flow": "release", "trail": "my-trail"}, }, context["trails"]) - policy, ok := seen.body["policy"].(map[string]interface{}) + policy, ok := seen.body["policy"].(map[string]any) require.True(t, ok) require.Equal(t, []string{"files"}, keysOf(policy)) - require.Equal(t, map[string]interface{}{ + require.Equal(t, map[string]any{ "policy.rego": "package policy\n\nallow := true\n", }, policy["files"]) - require.Equal(t, map[string]interface{}{"threshold": float64(2)}, seen.body["params"]) + require.Equal(t, map[string]any{"threshold": float64(2)}, seen.body["params"]) } func TestCreateSendsEveryTrailInOneEvaluation(t *testing.T) { @@ -119,10 +119,10 @@ func TestCreateSendsEveryTrailInOneEvaluation(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, seen.hits) - context := seen.body["context"].(map[string]interface{}) - require.Equal(t, []interface{}{ - map[string]interface{}{"flow": "release", "trail": "first"}, - map[string]interface{}{"flow": "release", "trail": "second"}, + context := seen.body["context"].(map[string]any) + require.Equal(t, []any{ + map[string]any{"flow": "release", "trail": "first"}, + map[string]any{"flow": "release", "trail": "second"}, }, context["trails"]) } @@ -136,7 +136,7 @@ func TestCreateSendsAbsentParamsAsAnEmptyObject(t *testing.T) { // Not null: the field is a plain dict on the server, so null fails // validation where an empty object is the documented default. - require.Equal(t, map[string]interface{}{}, seen.body["params"]) + require.Equal(t, map[string]any{}, seen.body["params"]) } func TestCreateDecodesTheCreatedEvaluation(t *testing.T) { @@ -225,7 +225,7 @@ func TestCreateSendsNothingOnADryRun(t *testing.T) { require.Equal(t, 0, seen.hits) } -func keysOf(m map[string]interface{}) []string { +func keysOf(m map[string]any) []string { keys := make([]string, 0, len(m)) for key := range m { keys = append(keys, key) @@ -286,7 +286,7 @@ func TestCreateSendsTheDecisionItWasGiven(t *testing.T) { _, err := newTestClient(t, server.URL, false).Create("my-org", request) require.NoError(t, err) - require.Equal(t, map[string]interface{}{ + require.Equal(t, map[string]any{ "control": "SDLC-CTRL-0007", "name": "SDLC-CTRL-0007-decision", "flow": "release", diff --git a/internal/github/github.go b/internal/github/github.go index 037fc843a..e77e5b4d9 100644 --- a/internal/github/github.go +++ b/internal/github/github.go @@ -419,7 +419,7 @@ func (c *GithubConfig) PREvidenceByPRNumber(prNumber int) (*types.PREvidence, er } `graphql:"repository(owner: $owner, name: $repo)"` } - variables := map[string]interface{}{ + variables := map[string]any{ "owner": graphql.String(c.Org), "repo": graphql.String(c.Repository), "prNumber": graphql.Int(prNumber), @@ -518,7 +518,7 @@ func (c *GithubConfig) PREvidenceForCommitV2(commit string) ([]*types.PREvidence } `graphql:"repository(owner: $owner, name: $repo)"` } - variables := map[string]interface{}{ + variables := map[string]any{ "owner": graphql.String(c.Org), "repo": graphql.String(c.Repository), "commitSHA": GitObjectID(commit), diff --git a/internal/gitview/gitView.go b/internal/gitview/gitView.go index 8bdab9d9f..ae13f6133 100644 --- a/internal/gitview/gitView.go +++ b/internal/gitview/gitView.go @@ -27,13 +27,13 @@ type CommitInfo struct { } type GitRepoInfo struct { - URL string `json:"url,omitempty"` - Name string `json:"name,omitempty"` - ID string `json:"id,omitempty"` - Description string `json:"description,omitempty"` - Provider string `json:"provider,omitempty"` - NamespacePath []string `json:"namespace_path,omitempty"` - AdditionalInfo map[string]interface{} `json:"additional_info,omitempty"` + URL string `json:"url,omitempty"` + Name string `json:"name,omitempty"` + ID string `json:"id,omitempty"` + Description string `json:"description,omitempty"` + Provider string `json:"provider,omitempty"` + NamespacePath []string `json:"namespace_path,omitempty"` + AdditionalInfo map[string]any `json:"additional_info,omitempty"` } // GitView diff --git a/internal/logger/logger.go b/internal/logger/logger.go index ce9ce385f..42dc865a6 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -46,14 +46,14 @@ func (l *Logger) SetInfoOut(out io.Writer) { l.infoLog.SetOutput(out) } -func (l *Logger) Debug(format string, v ...interface{}) { +func (l *Logger) Debug(format string, v ...any) { if l.DebugEnabled { format = fmt.Sprintf("[debug] %s\n", format) l.debugLog.Printf(format, v...) } } -func (l *Logger) Warn(format string, v ...interface{}) { +func (l *Logger) Warn(format string, v ...any) { if l.QuietEnabled { return } @@ -61,18 +61,18 @@ func (l *Logger) Warn(format string, v ...interface{}) { l.warnLog.Printf(format, v...) } -func (l *Logger) Error(format string, v ...interface{}) { +func (l *Logger) Error(format string, v ...any) { format = fmt.Sprintf("Error: %s\n", format) l.errLog.Fatalf(format, v...) } -func (l *Logger) Info(format string, v ...interface{}) { +func (l *Logger) Info(format string, v ...any) { format = fmt.Sprintf("%s\n", format) l.infoLog.Printf(format, v...) } // Print writes to the info output without appending a trailing newline // (log.Logger always appends one), e.g. for inline prompts. -func (l *Logger) Print(format string, v ...interface{}) { +func (l *Logger) Print(format string, v ...any) { _, _ = fmt.Fprintf(l.infoLog.Writer(), format, v...) } diff --git a/internal/requests/requests.go b/internal/requests/requests.go index 0c613c250..719862ea6 100644 --- a/internal/requests/requests.go +++ b/internal/requests/requests.go @@ -23,7 +23,7 @@ import ( type FormItem struct { Type string FieldName string - Content interface{} + Content any } // FileBytes is the Content of a "file-bytes" FormItem: a file the caller has @@ -73,7 +73,7 @@ type CustomLogger struct { } // Printf intercepts the log message and removes the hardcoded [DEBUG] part -func (cl *CustomLogger) Printf(format string, args ...interface{}) { +func (cl *CustomLogger) Printf(format string, args ...any) { msg := fmt.Sprintf(format, args...) // Remove the hardcoded [DEBUG] prefix if it exists @@ -117,7 +117,7 @@ func NewKosliClient(httpProxyURL string, maxAPIRetries int, debug bool, logger * type RequestParams struct { Method string URL string - Payload interface{} + Payload any Form []FormItem AdditionalHeaders map[string]string Username string @@ -141,7 +141,7 @@ func (p *RequestParams) newHTTPRequest() (*http.Request, map[string]any, error) } var body io.Reader - var jsonFields map[string]interface{} + var jsonFields map[string]any if len(p.Form) > 0 { // Multipart form handling (with possible file attachments) @@ -205,7 +205,7 @@ func createMultipartRequestBody(items []FormItem) (string, *bytes.Buffer, map[st }() // Map to store the JSON fields for logging during dry-run - jsonFields := make(map[string]interface{}) + jsonFields := make(map[string]any) for _, item := range items { switch item.Type { diff --git a/internal/requests/requests_test.go b/internal/requests/requests_test.go index ce969632a..5343b19f1 100644 --- a/internal/requests/requests_test.go +++ b/internal/requests/requests_test.go @@ -594,7 +594,7 @@ func (suite *RequestsTestSuite) TestMultipartFieldJSON_IsCompact() { { Type: "field", FieldName: "data_json", - Content: map[string]interface{}{"key": "value", "nested": map[string]interface{}{"a": 1}}, + Content: map[string]any{"key": "value", "nested": map[string]any{"a": 1}}, }, } _, body, jsonFields, err := createMultipartRequestBody(formItems) @@ -615,9 +615,9 @@ func (suite *RequestsTestSuite) TestNonMultipartJSON_IsCompact() { Method: http.MethodPut, URL: "https://example.com/api/v2/test", Token: "test-token", - Payload: map[string]interface{}{ + Payload: map[string]any{ "key": "value", - "nested": map[string]interface{}{"a": 1}, + "nested": map[string]any{"a": 1}, }, } req, _, err := params.newHTTPRequest() diff --git a/internal/snyk/snyk.go b/internal/snyk/snyk.go index d8be5937c..43d1096ce 100644 --- a/internal/snyk/snyk.go +++ b/internal/snyk/snyk.go @@ -146,7 +146,7 @@ func findLevel(r *sarif.Run, id string) (string, error) { // } problem, problem_exists := ruleDesc.Properties["problem"] if problem_exists && problem != nil { - severity, severity_exists := problem.(map[string]interface{})["severity"] + severity, severity_exists := problem.(map[string]any)["severity"] if severity_exists { return severity.(string), nil } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 8ebb327d9..0aa57ccf5 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -170,7 +170,7 @@ func CreateFileWithContent(path, content string) error { } func ConvertStringListToInterfaceList(approversList []string) []any { - approversIface := make([]interface{}, len(approversList)) + approversIface := make([]any, len(approversList)) for i, v := range approversList { approversIface[i] = v } From 8d30cde13d64ad3b8c511c41aa2d1c28e431e00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 18 Sep 2026 06:54:21 +0200 Subject: [PATCH 2/5] chore(lint): enforce any over interface{} via gofmt rewrite rule Also sets a 5m run timeout so a hung lint run fails in CI instead of blocking the job. --- .golangci.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index cd76c1842..bc293320d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,8 +1,12 @@ version: "2" +run: + timeout: 5m # Set the maximum time for the analysis run + linters: enable: - - forbidigo + - forbidigo # Forbid usage of certain Go constructs + settings: forbidigo: analyze-types: true @@ -11,3 +15,16 @@ linters: msg: "binary.NativeEndian is not portable; s390x is big-endian. Use binary.BigEndian or binary.LittleEndian explicitly." - pattern: '^unsafe\.Pointer$' msg: "unsafe.Pointer byte reinterpretation is endian-sensitive on s390x. Prefer encoding/binary, or add //nolint:forbidigo with justification." + +formatters: + enable: + - gofmt # Check code formatting + + settings: + gofmt: + # Apply the rewrite rules to the source before reformatting. + # https://pkg.go.dev/cmd/gofmt + # Default: [] + rewrite-rules: + - pattern: 'interface{}' + replacement: 'any' From 4701306cbde6f004ee6769e9c95be3871534a6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 18 Sep 2026 06:54:21 +0200 Subject: [PATCH 3/5] docs(skills): use any in archetype-read reference --- .claude/skills/new-command/references/archetype-read.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/new-command/references/archetype-read.md b/.claude/skills/new-command/references/archetype-read.md index d6267afad..91f7257fd 100644 --- a/.claude/skills/new-command/references/archetype-read.md +++ b/.claude/skills/new-command/references/archetype-read.md @@ -50,7 +50,7 @@ Read whichever canonical file matches and adapt. **`run` method** - Build the base URL into a `base` variable (not `url` — `listArtifacts.go` does this so the `net/url` package stays in scope), then append query params via `url.Values{}` and `params.Encode()`. - Same `GET` + `output.FormattedPrint` pattern as read-single. -- The `printsAsTable` helper unmarshals to a `[]map[string]interface{}` and handles the empty-list case with `logger.Info("No were found.")`. +- The `printsAsTable` helper unmarshals to a `[]map[string]any` and handles the empty-list case with `logger.Info("No were found.")`. **`RunE` signature** - `return o.run(out)` (no `args` needed when there are no positional args). From 44838910ccf0a95de4219c559a9d9c79fd67b0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 18 Sep 2026 07:06:06 +0200 Subject: [PATCH 4/5] chore(lint): make .golangci.yml the single source of the lint timeout The timeout was passed as a flag in both the Makefile and the CI workflow, which overrides the config file value. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6139d2203..1ef31af7e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -102,7 +102,7 @@ jobs: uses: golangci/golangci-lint-action@v9 with: version: latest - args: --timeout=5m -v + args: -v - name: Test the server image lookup run: ./hack/test-get-server-image.sh From 99aab78f6686f25b5ae29aa0c2e303525af61491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Fri, 18 Sep 2026 07:06:06 +0200 Subject: [PATCH 5/5] chore(fmt): apply the interface{} -> any rewrite in make fmt golangci-lint now rejects interface{} via a gofmt rewrite rule, but go fmt does not apply rewrite rules, so make fmt could not fix what make lint rejected. Use gofmt directly rather than golangci-lint fmt because make build runs in the Dockerfile without golangci-lint. Also drops the --timeout flag from make lint (see previous commit). --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 48088b93e..53a1951d8 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,9 @@ ldflags: ## Print ldflags @echo $(LDFLAGS) fmt: ## Reformat package sources - @go fmt ./... +# Mirrors the gofmt rewrite rule in .golangci.yml so fmt can fix what lint rejects. +# gofmt is used directly because golangci-lint is not available where `make build` runs (Dockerfile). + @gofmt -l -w -r 'interface{} -> any' $$(go list -f '{{.Dir}}' ./...) ensure_golangci-lint: @if command -v brew >/dev/null 2>&1; then \ @@ -77,7 +79,7 @@ ensure_golangci-lint: fi lint: deps vet ensure_golangci-lint ## Run linting - @golangci-lint run --timeout=5m --color always -v ./... + @golangci-lint run --color always -v ./... vet: fmt ## Run Go vet @go vet ./...