From 3028fe7eaa43f6f65d4451ae947ed103155b0071 Mon Sep 17 00:00:00 2001 From: Michael McGuinness Date: Mon, 23 Jun 2025 15:58:20 +0000 Subject: [PATCH] Merged in feature/linting (pull request #168) Linting Updates * precommit * smallerchecks * govuln --- .golangci.yml | 25 ++- .../user.creation.tool/cognito-permit-sync.go | 23 +-- cmd/cognito_test/user.creation.tool/permit.go | 46 +++--- cmd/healthcheck/main.go | 14 +- cmd/queryAPI/main.go | 2 +- devbox.json | 11 +- devbox.lock | 148 ++++++++++++------ docs/README.md | 16 ++ internal/collector/parse.go | 2 +- internal/database/migrations.go | 15 +- internal/document/clean/contentType.go | 2 +- internal/document/clean/create.go | 4 +- internal/document/types/pdf.go | 2 +- internal/query/normalize.go | 4 +- internal/server/api/listener.go | 8 +- internal/server/runner/poll.go | 4 +- internal/serviceconfig/auth/config.go | 2 +- internal/serviceconfig/aws/config.go | 2 +- .../prometheus/generator/main.go | 2 +- .../prometheus/prometheus_test.go | 10 +- internal/test/mockserver_test.go | 1 + internal/test/runner.go | 2 + scripts/Taskfile.yml | 4 +- test/queryAPI/accessory_test.go | 5 + 24 files changed, 226 insertions(+), 128 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 9a2938af..1b701cb3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,6 +17,14 @@ linters: - cyclop - unused - usetesting + - paralleltest + - bodyclose # Check HTTP response bodies are closed + - gochecknoinits # Disallow `init` functions + # - gocritic # Advanced style/performance checks + # - errorlint # Enforce error wrapping best practices + # - exhaustive # Require case exhaustiveness in switches + # - forbidigo # Ban specific identifiers (e.g., unsafe patterns) + # - nestif # Limit nested complexity (default: min-complexity=5) linters-settings: errcheck: @@ -36,9 +44,20 @@ linters-settings: - HACK - TODO - FIXME +# govet: +# enable-all: true # Enable all vet analyzers +# gocritic: +# enabled-checks: +# - rangeValCopy # Warn on copying large range values +# - hugeParam # Warn on large parameters passed by value +# - badCall # Detect suspicious function calls +# # Add other strict checks from https://go-critic.com issues: exclude-rules: + - path-except: ^test/ + linters: + - paralleltest - path: cmd/metricsExample_test/main\.go linters: - gosec @@ -54,10 +73,6 @@ issues: - path: internal/serviceconfig/objectstore/config.go linters: - gosec - text: "G401" - - path: internal/serviceconfig/objectstore/config.go - linters: - - gosec - text: "G501" + text: "G401|G501" max-issues-per-linter: 0 max-same-issues: 0 diff --git a/cmd/cognito_test/user.creation.tool/cognito-permit-sync.go b/cmd/cognito_test/user.creation.tool/cognito-permit-sync.go index ed465afc..fcbe94f4 100644 --- a/cmd/cognito_test/user.creation.tool/cognito-permit-sync.go +++ b/cmd/cognito_test/user.creation.tool/cognito-permit-sync.go @@ -7,6 +7,7 @@ package main import ( "context" "encoding/csv" + "errors" "flag" "fmt" "io" @@ -114,7 +115,7 @@ func run() error { // Initialize audit logger auditLogger, err := NewAuditLogger(config.DryRun) if err != nil { - return fmt.Errorf("failed to initialize audit logger: %v", err) + return fmt.Errorf("failed to initialize audit logger: %w", err) } defer auditLogger.Close() @@ -138,20 +139,20 @@ func run() error { // Validate configuration if err := validateConfig(config); err != nil { - return fmt.Errorf("configuration error: %v", err) + return fmt.Errorf("configuration error: %w", err) } // Initialize AWS Cognito client ctx := context.Background() cognitoClient, err := initializeCognitoClient() if err != nil { - return fmt.Errorf("error initializing Cognito client: %v", err) + return fmt.Errorf("error initializing Cognito client: %w", err) } // Read and parse CSV file users, err := readCSV(config.CSVPath, config) if err != nil { - return fmt.Errorf("error reading CSV file: %v", err) + return fmt.Errorf("error reading CSV file: %w", err) } if len(users) == 0 { @@ -385,7 +386,7 @@ func readCSV(path string, config AppConfig) ([]map[string]string, error) { // Read and validate header header, err := reader.Read() if err != nil { - return nil, fmt.Errorf("error reading CSV header: %v", err) + return nil, fmt.Errorf("error reading CSV header: %w", err) } if err := validateCSVHeader(header, config); err != nil { @@ -443,11 +444,11 @@ func readUserRecords(reader *csv.Reader, config AppConfig) ([]map[string]string, for { record, err := reader.Read() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { - return nil, fmt.Errorf("error reading line %d: %v", lineNum+1, err) + return nil, fmt.Errorf("error reading line %d: %w", lineNum+1, err) } lineNum++ @@ -652,7 +653,7 @@ func ensureCognitoUser(ctx context.Context, procCtx *UserProcessingContext) (*Co if err != nil { auditLogger.LogAction(ActionCreate, SystemCognito, user["email"], ResultFailure, fmt.Sprintf("Failed to retrieve existing user: %v", err)) fmt.Printf(" ❌ Failed to retrieve existing Cognito user: %v\n", err) - return nil, fmt.Errorf("failed to retrieve existing Cognito user: %v", err) + return nil, fmt.Errorf("failed to retrieve existing Cognito user: %w", err) } return existingUser, nil } @@ -676,7 +677,7 @@ func ensureCognitoUser(ctx context.Context, procCtx *UserProcessingContext) (*Co if err != nil { auditLogger.LogAction(ActionCreate, SystemCognito, user["email"], ResultFailure, fmt.Sprintf("Failed to create: %v", err)) fmt.Printf(" ❌ Cognito creation failed: %v\n", err) - return nil, fmt.Errorf("failed to create in Cognito: %v", err) + return nil, fmt.Errorf("failed to create in Cognito: %w", err) } auditLogger.LogActionWithSubjectID(ActionCreate, SystemCognito, user["email"], cognitoUser.SubjectID, ResultSuccess, "User created successfully") @@ -696,7 +697,7 @@ func ensurePermitUser(procCtx *UserProcessingContext, cognitoUser *CognitoUserRe if err != nil { auditLogger.LogAction(ActionCreate, SystemPermit, user["email"], ResultFailure, fmt.Sprintf("Failed to check user existence: %v", err)) fmt.Printf(" ❌ Failed to check Permit.io user existence: %v\n", err) - return fmt.Errorf("failed to check Permit.io user existence: %v", err) + return fmt.Errorf("failed to check Permit.io user existence: %w", err) } if permitExists { @@ -716,7 +717,7 @@ func ensurePermitUser(procCtx *UserProcessingContext, cognitoUser *CognitoUserRe if err != nil { auditLogger.LogActionWithSubjectID(ActionCreate, SystemPermit, user["email"], cognitoUser.SubjectID, ResultFailure, fmt.Sprintf("Failed to create: %v", err)) fmt.Printf(" ❌ Permit.io creation failed: %v\n", err) - return fmt.Errorf("failed to create in Permit.io: %v", err) + return fmt.Errorf("failed to create in Permit.io: %w", err) } auditLogger.LogActionWithSubjectID(ActionCreate, SystemPermit, user["email"], cognitoUser.SubjectID, ResultSuccess, "User created successfully") diff --git a/cmd/cognito_test/user.creation.tool/permit.go b/cmd/cognito_test/user.creation.tool/permit.go index cf309b15..ca6beba5 100644 --- a/cmd/cognito_test/user.creation.tool/permit.go +++ b/cmd/cognito_test/user.creation.tool/permit.go @@ -46,12 +46,12 @@ func createPermitUser(client *http.Client, config AppConfig, cognitoUser *Cognit jsonData, err := json.Marshal(userObj) if err != nil { - return fmt.Errorf("error marshaling user data: %v", err) + return fmt.Errorf("error marshaling user data: %w", err) } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { - return fmt.Errorf("error creating request: %v", err) + return fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) @@ -59,7 +59,7 @@ func createPermitUser(client *http.Client, config AppConfig, cognitoUser *Cognit resp, err := client.Do(req) if err != nil { - return fmt.Errorf("error making request: %v", err) + return fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -100,12 +100,12 @@ func assignRole(client *http.Client, config AppConfig, userID, role string) erro jsonData, err := json.Marshal(assignment) if err != nil { - return fmt.Errorf("error marshaling role assignment: %v", err) + return fmt.Errorf("error marshaling role assignment: %w", err) } req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { - return fmt.Errorf("error creating request: %v", err) + return fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) @@ -113,7 +113,7 @@ func assignRole(client *http.Client, config AppConfig, userID, role string) erro resp, err := client.Do(req) if err != nil { - return fmt.Errorf("error making request: %v", err) + return fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -141,7 +141,7 @@ func findPermitUserByEmail(client *http.Client, config AppConfig, email string) req, err := http.NewRequest("GET", url, nil) if err != nil { - return "", fmt.Errorf("error creating request: %v", err) + return "", fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) @@ -149,7 +149,7 @@ func findPermitUserByEmail(client *http.Client, config AppConfig, email string) resp, err := client.Do(req) if err != nil { - return "", fmt.Errorf("error making request: %v", err) + return "", fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -163,7 +163,7 @@ func findPermitUserByEmail(client *http.Client, config AppConfig, email string) // First try to unmarshal as an object (paginated response) var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { - return "", fmt.Errorf("error parsing response: %v", err) + return "", fmt.Errorf("error parsing response: %w", err) } // Look for users in common pagination structures @@ -207,14 +207,14 @@ func deletePermitUser(client *http.Client, config AppConfig, userKey string) err req, err := http.NewRequest("DELETE", url, nil) if err != nil { - return fmt.Errorf("error creating request: %v", err) + return fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) resp, err := client.Do(req) if err != nil { - return fmt.Errorf("error making request: %v", err) + return fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -238,7 +238,7 @@ func getPermitUserRoles(client *http.Client, config AppConfig, userKey string) ( req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, fmt.Errorf("error creating request: %v", err) + return nil, fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) @@ -246,7 +246,7 @@ func getPermitUserRoles(client *http.Client, config AppConfig, userKey string) ( resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("error making request: %v", err) + return nil, fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -257,7 +257,7 @@ func getPermitUserRoles(client *http.Client, config AppConfig, userKey string) ( body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("error reading response: %v", err) + return nil, fmt.Errorf("error reading response: %w", err) } var roles []string @@ -271,7 +271,7 @@ func getPermitUserRoles(client *http.Client, config AppConfig, userKey string) ( // If that fails, try as object with data/items/etc var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { - return nil, fmt.Errorf("error parsing response as object or array: %v", err) + return nil, fmt.Errorf("error parsing response as object or array: %w", err) } // Look for role assignments in common response structures @@ -308,12 +308,12 @@ func unassignRole(client *http.Client, config AppConfig, userID, role string) er jsonData, err := json.Marshal(assignment) if err != nil { - return fmt.Errorf("error marshaling role assignment: %v", err) + return fmt.Errorf("error marshaling role assignment: %w", err) } req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(jsonData)) if err != nil { - return fmt.Errorf("error creating request: %v", err) + return fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+config.PermitAPIKey) @@ -321,7 +321,7 @@ func unassignRole(client *http.Client, config AppConfig, userID, role string) er resp, err := client.Do(req) if err != nil { - return fmt.Errorf("error making request: %v", err) + return fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -458,7 +458,7 @@ func ValidateCSVRoles(client *http.Client, config AppConfig, csvUsers []map[stri // Get all existing roles from Permit.io existingRoles, err := getExistingRoles(client, config) if err != nil { - return requestedRoles, fmt.Errorf("failed to retrieve existing roles from Permit.io: %v", err) + return requestedRoles, fmt.Errorf("failed to retrieve existing roles from Permit.io: %w", err) } // Check which requested roles are missing @@ -528,7 +528,7 @@ func getExistingRoles(client *http.Client, config AppConfig) ([]string, error) { // Create HTTP request req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, fmt.Errorf("error creating request: %v", err) + return nil, fmt.Errorf("error creating request: %w", err) } // Set authorization header @@ -538,7 +538,7 @@ func getExistingRoles(client *http.Client, config AppConfig) ([]string, error) { // Execute request resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("error making request: %v", err) + return nil, fmt.Errorf("error making request: %w", err) } defer resp.Body.Close() @@ -551,7 +551,7 @@ func getExistingRoles(client *http.Client, config AppConfig) ([]string, error) { // Read and parse response body body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("error reading response: %v", err) + return nil, fmt.Errorf("error reading response: %w", err) } // Parse JSON response - try different response structures @@ -566,7 +566,7 @@ func getExistingRoles(client *http.Client, config AppConfig) ([]string, error) { // If that fails, try as object with data/items/etc var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { - return nil, fmt.Errorf("error parsing response as object or array: %v", err) + return nil, fmt.Errorf("error parsing response as object or array: %w", err) } // Look for roles in common response structures diff --git a/cmd/healthcheck/main.go b/cmd/healthcheck/main.go index 6d6bcb04..6bddea8f 100644 --- a/cmd/healthcheck/main.go +++ b/cmd/healthcheck/main.go @@ -2,19 +2,27 @@ package main import ( + "errors" "net/http" "os" ) func main() { - resp, err := http.Get("http://localhost:8080/health") + err := check() if err != nil { os.Exit(1) } +} + +func check() error { + resp, err := http.Get("http://localhost:8080/health") + if err != nil { + return err + } defer resp.Body.Close() if resp.StatusCode != 200 { - os.Exit(1) + return errors.New("Status code not OK") } - os.Exit(0) + return nil } diff --git a/cmd/queryAPI/main.go b/cmd/queryAPI/main.go index 2b9c376b..647f619f 100644 --- a/cmd/queryAPI/main.go +++ b/cmd/queryAPI/main.go @@ -101,7 +101,7 @@ func main() { swagger, err := queryapi.GetSwagger() if err != nil { - return nil, fmt.Errorf("error loading swagger: %s", err) + return nil, fmt.Errorf("error loading swagger: %w", err) } return swagger, nil diff --git a/devbox.json b/devbox.json index 1928d964..ff837fb2 100644 --- a/devbox.json +++ b/devbox.json @@ -57,13 +57,14 @@ "postgresql@17.2", "bc@1.07.1", "gomarkdoc@1.1.0", - "go@1.24.0", "sqlcheck@1.3", "docker-client@27.3.1", - "gotestsum@latest", - "goperf@latest", - "graphviz@latest", - "psrecord@latest" + "gotestsum@1.12.2", + "goperf@0-unstable-2025-05-05", + "graphviz@12.2.1", + "psrecord@1.2", + "govulncheck@1.1.4", + "go@1.24.3" ], "shell": { "init_hook": [ diff --git a/devbox.lock b/devbox.lock index 62566319..6b906f50 100644 --- a/devbox.lock +++ b/devbox.lock @@ -161,10 +161,6 @@ } } }, - "github:NixOS/nixpkgs/nixpkgs-unstable": { - "last_modified": "2025-06-10T02:39:58Z", - "resolved": "github:NixOS/nixpkgs/cdc68935eba9f86d155585fdf6f17af6824f38ac?lastModified=1749523198&narHash=sha256-How2kQw0psKmCdXgojc95Sf3K5maHB3qfINxTZFCAPM%3D" - }, "glibcLocales@latest": { "last_modified": "2025-05-16T20:19:48Z", "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#glibcLocales", @@ -289,51 +285,51 @@ } } }, - "go@1.24.0": { - "last_modified": "2025-02-12T00:10:52Z", - "resolved": "github:NixOS/nixpkgs/83a2581c81ff5b06f7c1a4e7cc736a455dfcf7b4#go_1_24", + "go@1.24.3": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#go", "source": "devbox-search", - "version": "1.24.0", + "version": "1.24.3", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/qldcnifalkvyah0wnv7m4zb854yd9l88-go-1.24.0", + "path": "/nix/store/ps3admpzmc1ryvn9q7sw5xfd94dkrb3f-go-1.24.3", "default": true } ], - "store_path": "/nix/store/qldcnifalkvyah0wnv7m4zb854yd9l88-go-1.24.0" + "store_path": "/nix/store/ps3admpzmc1ryvn9q7sw5xfd94dkrb3f-go-1.24.3" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/rrxgml7w4pfmibjbspkdvrw8vd2vnarb-go-1.24.0", + "path": "/nix/store/45bnqhyyq40p91k3cjw0farx3hn1swx6-go-1.24.3", "default": true } ], - "store_path": "/nix/store/rrxgml7w4pfmibjbspkdvrw8vd2vnarb-go-1.24.0" + "store_path": "/nix/store/45bnqhyyq40p91k3cjw0farx3hn1swx6-go-1.24.3" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/7imv22pl4qrjwvi6jzlfb305rc2min45-go-1.24.0", + "path": "/nix/store/9z2kb6hxij7pqi0fgcn9ijhpb7ajpazs-go-1.24.3", "default": true } ], - "store_path": "/nix/store/7imv22pl4qrjwvi6jzlfb305rc2min45-go-1.24.0" + "store_path": "/nix/store/9z2kb6hxij7pqi0fgcn9ijhpb7ajpazs-go-1.24.3" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/vh5d5bj1sljdhdypy80x1ydx2jx6rv2q-go-1.24.0", + "path": "/nix/store/5xvi25nqmbrg58aixp4zgczilfnp7pwg-go-1.24.3", "default": true } ], - "store_path": "/nix/store/vh5d5bj1sljdhdypy80x1ydx2jx6rv2q-go-1.24.0" + "store_path": "/nix/store/5xvi25nqmbrg58aixp4zgczilfnp7pwg-go-1.24.3" } } }, @@ -433,99 +429,99 @@ } } }, - "goperf@latest": { - "last_modified": "2025-04-10T20:20:34Z", - "resolved": "github:NixOS/nixpkgs/d19cf9dfc633816a437204555afeb9e722386b76#goperf", + "goperf@0-unstable-2025-05-05": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#goperf", "source": "devbox-search", - "version": "0-unstable-2025-04-07", + "version": "0-unstable-2025-05-05", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/vf411izylvc8fq1m91ri8303xv1mha5d-goperf-0-unstable-2025-04-07", + "path": "/nix/store/sk50sp3wnhsb1lxf9495y2y5p2w0dkik-goperf-0-unstable-2025-05-05", "default": true } ], - "store_path": "/nix/store/vf411izylvc8fq1m91ri8303xv1mha5d-goperf-0-unstable-2025-04-07" + "store_path": "/nix/store/sk50sp3wnhsb1lxf9495y2y5p2w0dkik-goperf-0-unstable-2025-05-05" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/3f2ap46w6bshaf24s0d48rswm3rl12l6-goperf-0-unstable-2025-04-07", + "path": "/nix/store/gwlzys9sgp2vamgl4kvccqy3pv2x6a8k-goperf-0-unstable-2025-05-05", "default": true } ], - "store_path": "/nix/store/3f2ap46w6bshaf24s0d48rswm3rl12l6-goperf-0-unstable-2025-04-07" + "store_path": "/nix/store/gwlzys9sgp2vamgl4kvccqy3pv2x6a8k-goperf-0-unstable-2025-05-05" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/hp9gdz3y082isszsg6zcz8d6g0l1gk59-goperf-0-unstable-2025-04-07", + "path": "/nix/store/i8pyz4hx8hiqhmb6xxhinp1p4h5fjcsm-goperf-0-unstable-2025-05-05", "default": true } ], - "store_path": "/nix/store/hp9gdz3y082isszsg6zcz8d6g0l1gk59-goperf-0-unstable-2025-04-07" + "store_path": "/nix/store/i8pyz4hx8hiqhmb6xxhinp1p4h5fjcsm-goperf-0-unstable-2025-05-05" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ardq6yb76al8kp45jlqa59by17l2a58y-goperf-0-unstable-2025-04-07", + "path": "/nix/store/9dmc6qyh3p2a3kpcm0zb8dh1ri70w5qv-goperf-0-unstable-2025-05-05", "default": true } ], - "store_path": "/nix/store/ardq6yb76al8kp45jlqa59by17l2a58y-goperf-0-unstable-2025-04-07" + "store_path": "/nix/store/9dmc6qyh3p2a3kpcm0zb8dh1ri70w5qv-goperf-0-unstable-2025-05-05" } } }, - "gotestsum@latest": { - "last_modified": "2025-04-05T00:48:53Z", - "resolved": "github:NixOS/nixpkgs/250b695f41e0e2f5afbf15c6b12480de1fe0001b#gotestsum", + "gotestsum@1.12.2": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#gotestsum", "source": "devbox-search", - "version": "1.12.0-unstable-2024-09-17", + "version": "1.12.2", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/93kwis02c81bwh382s1vdmsqp7s886fl-gotestsum-1.12.0-unstable-2024-09-17", + "path": "/nix/store/9kcdv1zs53izyy2a2j8qhdwq0nnn3rc7-gotestsum-1.12.2", "default": true } ], - "store_path": "/nix/store/93kwis02c81bwh382s1vdmsqp7s886fl-gotestsum-1.12.0-unstable-2024-09-17" + "store_path": "/nix/store/9kcdv1zs53izyy2a2j8qhdwq0nnn3rc7-gotestsum-1.12.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/hmil65sxh60c0mx2f1b41b883dmni5x9-gotestsum-1.12.0-unstable-2024-09-17", + "path": "/nix/store/lzg48k73z9n2zlb9znsrpmlv1g8c6f7a-gotestsum-1.12.2", "default": true } ], - "store_path": "/nix/store/hmil65sxh60c0mx2f1b41b883dmni5x9-gotestsum-1.12.0-unstable-2024-09-17" + "store_path": "/nix/store/lzg48k73z9n2zlb9znsrpmlv1g8c6f7a-gotestsum-1.12.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/1gj7gmd3jsxcn8sxa2zac13f7mh4frx4-gotestsum-1.12.0-unstable-2024-09-17", + "path": "/nix/store/wbc09rgw3yqz1k84xhqjkf6kcdd3k53p-gotestsum-1.12.2", "default": true } ], - "store_path": "/nix/store/1gj7gmd3jsxcn8sxa2zac13f7mh4frx4-gotestsum-1.12.0-unstable-2024-09-17" + "store_path": "/nix/store/wbc09rgw3yqz1k84xhqjkf6kcdd3k53p-gotestsum-1.12.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/5iq2g0zd9xiss7px89mdha3qlcws0cl4-gotestsum-1.12.0-unstable-2024-09-17", + "path": "/nix/store/4ixg21kip6xv15jm1p5zdcx46wmgnqw8-gotestsum-1.12.2", "default": true } ], - "store_path": "/nix/store/5iq2g0zd9xiss7px89mdha3qlcws0cl4-gotestsum-1.12.0-unstable-2024-09-17" + "store_path": "/nix/store/4ixg21kip6xv15jm1p5zdcx46wmgnqw8-gotestsum-1.12.2" } } }, @@ -577,9 +573,57 @@ } } }, - "graphviz@latest": { - "last_modified": "2025-03-24T07:07:41Z", - "resolved": "github:NixOS/nixpkgs/1750f3c1c89488e2ffdd47cab9d05454dddfb734#graphviz", + "govulncheck@1.1.4": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#govulncheck", + "source": "devbox-search", + "version": "1.1.4", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/8rni7fry5sg928cqal7nglpqm8gppv81-govulncheck-1.1.4", + "default": true + } + ], + "store_path": "/nix/store/8rni7fry5sg928cqal7nglpqm8gppv81-govulncheck-1.1.4" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vkblf0jwdhrmy2m3pna5nn0cqrik7vlb-govulncheck-1.1.4", + "default": true + } + ], + "store_path": "/nix/store/vkblf0jwdhrmy2m3pna5nn0cqrik7vlb-govulncheck-1.1.4" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/clr0jn9ggj928iyghhvd20h487rvz3r7-govulncheck-1.1.4", + "default": true + } + ], + "store_path": "/nix/store/clr0jn9ggj928iyghhvd20h487rvz3r7-govulncheck-1.1.4" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/6nd9kbizh0w8yg0lsvm2gf458xd8m8j2-govulncheck-1.1.4", + "default": true + } + ], + "store_path": "/nix/store/6nd9kbizh0w8yg0lsvm2gf458xd8m8j2-govulncheck-1.1.4" + } + } + }, + "graphviz@12.2.1": { + "last_modified": "2025-05-16T20:19:48Z", + "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#graphviz", "source": "devbox-search", "version": "12.2.1", "systems": { @@ -587,41 +631,41 @@ "outputs": [ { "name": "out", - "path": "/nix/store/n261cb4vg4wijd3x0kbh2wxjccxk2b2y-graphviz-12.2.1", + "path": "/nix/store/jcznx2c2hz7iwk97xgzizi3rqkvnghmc-graphviz-12.2.1", "default": true } ], - "store_path": "/nix/store/n261cb4vg4wijd3x0kbh2wxjccxk2b2y-graphviz-12.2.1" + "store_path": "/nix/store/jcznx2c2hz7iwk97xgzizi3rqkvnghmc-graphviz-12.2.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/shsff1y57kvz22qkkgy269lvn8s9yckz-graphviz-12.2.1", + "path": "/nix/store/gpayvnlnryg41v41zkymfl5zvwifyzn3-graphviz-12.2.1", "default": true } ], - "store_path": "/nix/store/shsff1y57kvz22qkkgy269lvn8s9yckz-graphviz-12.2.1" + "store_path": "/nix/store/gpayvnlnryg41v41zkymfl5zvwifyzn3-graphviz-12.2.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/3mjmaid5ra8paxwf603dlc20w09739la-graphviz-12.2.1", + "path": "/nix/store/jswrjcp9ilxyvgl44ccya89smf52vwgz-graphviz-12.2.1", "default": true } ], - "store_path": "/nix/store/3mjmaid5ra8paxwf603dlc20w09739la-graphviz-12.2.1" + "store_path": "/nix/store/jswrjcp9ilxyvgl44ccya89smf52vwgz-graphviz-12.2.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/199jxrl2dwd5afi6njmj7wvaz9ni79kz-graphviz-12.2.1", + "path": "/nix/store/75f5zxbsnbciq33ahnawq8kpspvy4k3x-graphviz-12.2.1", "default": true } ], - "store_path": "/nix/store/199jxrl2dwd5afi6njmj7wvaz9ni79kz-graphviz-12.2.1" + "store_path": "/nix/store/75f5zxbsnbciq33ahnawq8kpspvy4k3x-graphviz-12.2.1" } } }, @@ -898,7 +942,7 @@ } } }, - "psrecord@latest": { + "psrecord@1.2": { "last_modified": "2025-05-16T20:19:48Z", "resolved": "github:NixOS/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3#psrecord", "source": "devbox-search", diff --git a/docs/README.md b/docs/README.md index 4f0d21c5..79e35fdc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -144,4 +144,20 @@ It supports the document initialisation and document text processing stages. Eve ## cognito.auth.harness +## permit.harness + +## user.creation.tool + +audit.go provides comprehensive audit trail logging functionality for compliance and monitoring. This file now uses the `log/slog` structured logging package introduced in Go 1.21. A single environment variable LOG_FORMAT controls the output format: + + + +The public API (type AuditLogger and all its methods) is unchanged, so no call‑sites (e.g. cognito-permit-sync.go) need to be modified. + +cognito-permit-sync.go is the main entry point and orchestration logic for the user creation tool. This file coordinates the synchronization of users between AWS Cognito and Permit.io systems, handling CSV parsing, user creation/deletion/disable/enable operations, role validation, configuration management, and command-line interface. It serves as the primary workflow controller. + +cognito.go handles AWS Cognito User Pool operations for user identity management. This file provides functions to create, delete, enable, disable, and retrieve users from AWS Cognito User Pools, including credential validation, user attribute management, and integration with AWS SDK v2 for identity provider operations. + +permit.go handles Permit.io API operations for user management and role-based access control (RBAC). This file provides functions to create, delete, search, and manage users in Permit.io, as well as assign/unassign roles and validate role definitions. It integrates with Cognito user data to sync user information between the two systems. + Generated by Doczy diff --git a/internal/collector/parse.go b/internal/collector/parse.go index fa301ee3..6ae69715 100644 --- a/internal/collector/parse.go +++ b/internal/collector/parse.go @@ -19,7 +19,7 @@ func parseDBCollector(c *repository.Fullactivecollector) (*Collector, error) { if len(c.Fields) > 0 { err := json.Unmarshal(c.Fields, &fields) if err != nil { - return nil, fmt.Errorf("error unmarshalling: %s", err) + return nil, fmt.Errorf("error unmarshalling: %w", err) } } else { fields = map[string]uuid.UUID{} diff --git a/internal/database/migrations.go b/internal/database/migrations.go index 1dd009d6..2001f569 100644 --- a/internal/database/migrations.go +++ b/internal/database/migrations.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "embed" + "errors" "fmt" "log/slog" @@ -21,14 +22,14 @@ func createDB(ctx context.Context, cfg database.ConfigProvider) error { db, err := sql.Open(cfg.GetDBDriver(), cfg.GetDBAdminDBURI()) if err != nil { - return fmt.Errorf("error opening admin database: %v", err) + return fmt.Errorf("error opening admin database: %w", err) } slog.Debug("pinging admin database", "uri", cfg.GetDBAdminDBURI()) err = db.PingContext(ctx) if err != nil { - return fmt.Errorf("error pinging admin database: %v", err) + return fmt.Errorf("error pinging admin database: %w", err) } slog.Debug("creating admin database", "uri", cfg.GetDBAdminDBURI()) @@ -44,14 +45,14 @@ func createDB(ctx context.Context, cfg database.ConfigProvider) error { db, err = sql.Open(cfg.GetDBDriver(), cfg.GetDBURI()) if err != nil { - return fmt.Errorf("error opening database: %v", err) + return fmt.Errorf("error opening database: %w", err) } slog.Debug("pinging database", "uri", cfg.GetDBAdminDBURI()) err = db.PingContext(ctx) if err != nil { - return fmt.Errorf("error pinging database: %v", err) + return fmt.Errorf("error pinging database: %w", err) } return nil @@ -73,14 +74,14 @@ func RunMigrations(ctx context.Context, cfg database.ConfigProvider) error { m, err := migrate.NewWithSourceInstance("iofs", source, cfg.GetDBURI()) if err != nil { - return fmt.Errorf("failed to create migrate instance: %v", err) + return fmt.Errorf("failed to create migrate instance: %w", err) } err = m.Up() - if err == migrate.ErrNoChange { + if errors.Is(err, migrate.ErrNoChange) { slog.Info("No migration changes required") } else if err != nil { - return fmt.Errorf("failed to apply migrations: %v", err) + return fmt.Errorf("failed to apply migrations: %w", err) } else { slog.Info("Migrations applied successfully!") } diff --git a/internal/document/clean/contentType.go b/internal/document/clean/contentType.go index 333dc8f7..cb954d60 100644 --- a/internal/document/clean/contentType.go +++ b/internal/document/clean/contentType.go @@ -98,7 +98,7 @@ func (s *Service) getBytesBuffer(ctx context.Context, params *CleanParams, start buffer := make([]byte, length) n, err := out.Body.Read(buffer) if err != nil && n == 0 { - return nil, fmt.Errorf("unable to read object body: %v", err) + return nil, fmt.Errorf("unable to read object body: %w", err) } return buffer[:n], nil diff --git a/internal/document/clean/create.go b/internal/document/clean/create.go index 2fe8d557..a9ac7372 100644 --- a/internal/document/clean/create.go +++ b/internal/document/clean/create.go @@ -13,13 +13,13 @@ import ( func (s *Service) Clean(ctx context.Context, documentId uuid.UUID) error { isclean, err := s.cfg.GetDBQueries().HasDocumentCleanEntry(ctx, documentId) if err != nil { - return fmt.Errorf("unable to verify if document has been cleaned: %s", err) + return fmt.Errorf("unable to verify if document has been cleaned: %w", err) } if !isclean { err = s.clean(ctx, documentId) if err != nil { - return fmt.Errorf("unable to clean document: %s", err) + return fmt.Errorf("unable to clean document: %w", err) } } diff --git a/internal/document/types/pdf.go b/internal/document/types/pdf.go index 2f0400c3..cc553d0b 100644 --- a/internal/document/types/pdf.go +++ b/internal/document/types/pdf.go @@ -128,7 +128,7 @@ func (s *PDF) GetPageAsPNG(ctx context.Context, index int) ([]byte, error) { img, err := fitzdoc.ImagePNG(0, float64(s.pngDPI)) if err != nil { - return nil, fmt.Errorf("failed to render page %d: %v", index, err) + return nil, fmt.Errorf("failed to render page %d: %w", index, err) } return img, nil diff --git a/internal/query/normalize.go b/internal/query/normalize.go index c99e1b50..6c6c6612 100644 --- a/internal/query/normalize.go +++ b/internal/query/normalize.go @@ -36,12 +36,12 @@ func (s *Service) NormalizeConfig(config Config) error { var data map[string]interface{} if err := json.Unmarshal([]byte(trim), &data); err != nil { - return fmt.Errorf("error unmarshalling JSON: %s", err) + return fmt.Errorf("error unmarshalling JSON: %w", err) } prettyJSON, err := json.Marshal(data) if err != nil { - return fmt.Errorf("error marshaling JSON: %s", err) + return fmt.Errorf("error marshaling JSON: %w", err) } strJSON := string(prettyJSON) diff --git a/internal/server/api/listener.go b/internal/server/api/listener.go index 613afaa3..b77cac59 100644 --- a/internal/server/api/listener.go +++ b/internal/server/api/listener.go @@ -44,7 +44,7 @@ type BaseConfig struct { // experimental since it is not used yet and may pose cyclical dependency issues // in handlers that are called by the generated // swagger code. This needs to be verified. -//func GetConfigFromContext(ctx echo.Context) (Config, error) { +// func GetConfigFromContext(ctx echo.Context) (Config, error) { // cfg, ok := ctx.Get(configContextKey).(Config) // if !ok { // return nil, errors.New("config not found in context") @@ -170,13 +170,13 @@ func New(ctx context.Context, cfg Config) (*Server, error) { // Put this in when refactoring is done and remove from main.go //// This must be done before the rbac.InitializeAuthProvider then remove from main.go for service. - //errInitializingConfig := serviceconfig.InitializeConfig(cfg) - //if errInitializingConfig != nil { + // errInitializingConfig := serviceconfig.InitializeConfig(cfg) + // if errInitializingConfig != nil { // return nil, errInitializingConfig //} // //// Initialize the rbac config here since only the API service needs it - //errorGettingAuthProvider := rbac.InitializeAuthProvider(cfg.GetAuthConfig()) + // errorGettingAuthProvider := rbac.InitializeAuthProvider(cfg.GetAuthConfig()) //if errorGettingAuthProvider != nil { // return nil, errorGettingAuthProvider //} diff --git a/internal/server/runner/poll.go b/internal/server/runner/poll.go index 63fc9b82..5f98e511 100644 --- a/internal/server/runner/poll.go +++ b/internal/server/runner/poll.go @@ -43,7 +43,7 @@ func (c *Server[B]) pollMessage(ctx context.Context) error { QueueURL: c.cfg.GetQueueURL(), }) if err != nil { - return fmt.Errorf("message fetch fail: %v", err) + return fmt.Errorf("message fetch fail: %w", err) } for _, message := range result.Messages { @@ -74,7 +74,7 @@ func (c *Server[B]) processMessage(ctx context.Context, message *types.Message) ReceiptHandle: message.ReceiptHandle, }) if err != nil { - return fmt.Errorf("message delete fail: %v", err) + return fmt.Errorf("message delete fail: %w", err) } return nil diff --git a/internal/serviceconfig/auth/config.go b/internal/serviceconfig/auth/config.go index 3f251e6c..5e27f117 100644 --- a/internal/serviceconfig/auth/config.go +++ b/internal/serviceconfig/auth/config.go @@ -81,7 +81,7 @@ type ConfigProvider interface { // Returns an error if initialization fails, nil otherwise. func (c *CognitoConfig) InitializeAuthConfig(baseURL string, logger *slog.Logger) error { tempConfig := InitializeConfig(baseURL, logger) - //fmt.Printf("base url after InitializeAuthConfig: %s\n", tempConfig.) + // fmt.Printf("base url after InitializeAuthConfig: %s\n", tempConfig.) if tempConfig == nil { return fmt.Errorf("could not initialize auth config") } diff --git a/internal/serviceconfig/aws/config.go b/internal/serviceconfig/aws/config.go index 23c5d87a..66ea94d1 100644 --- a/internal/serviceconfig/aws/config.go +++ b/internal/serviceconfig/aws/config.go @@ -86,7 +86,7 @@ func (c *AWSConfig) SetAWSProfile(val Profile) { func GetAWSConfigWithOpts(ctx context.Context, opts ...func(*config.LoadOptions) error) (aws.Config, error) { cfg, err := config.LoadDefaultConfig(ctx, opts...) if err != nil { - return aws.Config{}, fmt.Errorf("unable to load SDK config: %v", err) + return aws.Config{}, fmt.Errorf("unable to load SDK config: %w", err) } return cfg, nil diff --git a/internal/serviceconfig/observability/prometheus/generator/main.go b/internal/serviceconfig/observability/prometheus/generator/main.go index 4b49aa12..f259c56c 100644 --- a/internal/serviceconfig/observability/prometheus/generator/main.go +++ b/internal/serviceconfig/observability/prometheus/generator/main.go @@ -1,4 +1,4 @@ -//ignore go:build ignore +// ignore go:build ignore package main diff --git a/internal/serviceconfig/observability/prometheus/prometheus_test.go b/internal/serviceconfig/observability/prometheus/prometheus_test.go index 6ed32db6..af675764 100644 --- a/internal/serviceconfig/observability/prometheus/prometheus_test.go +++ b/internal/serviceconfig/observability/prometheus/prometheus_test.go @@ -17,12 +17,12 @@ func GetFreePortForTesting() (int, error) { // Create a TCP listener on port 0 (lets OS choose a free port) addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { - return 0, fmt.Errorf("failed to resolve TCP address: %v", err) + return 0, fmt.Errorf("failed to resolve TCP address: %w", err) } l, err := net.ListenTCP("tcp", addr) if err != nil { - return 0, fmt.Errorf("failed to create TCP listener: %v", err) + return 0, fmt.Errorf("failed to create TCP listener: %w", err) } defer l.Close() @@ -143,7 +143,7 @@ func TestMetricsIntegration(t *testing.T) { metricsOutput := string(body) // if needed for debugging. - //log.Infof("Metrics output: %s", metricsOutput) + // log.Infof("Metrics output: %s", metricsOutput) // Step 6: Verify that our custom metrics and go metrics are present expectedMetrics := []string{ @@ -190,9 +190,11 @@ func TestMetricsIntegration(t *testing.T) { // Try to connect to the server - it should fail after a short delay time.Sleep(time.Second) testingUrl := fmt.Sprintf("http://localhost:%d/metrics", testingPort) - _, err = http.Get(testingUrl) + resp, err := http.Get(testingUrl) if err == nil { t.Error("Expected server to be shutdown after context cancellation") + } else if resp != nil { + defer resp.Body.Close() } }) } diff --git a/internal/test/mockserver_test.go b/internal/test/mockserver_test.go index f2868aa4..4ea548b4 100644 --- a/internal/test/mockserver_test.go +++ b/internal/test/mockserver_test.go @@ -82,6 +82,7 @@ func TestWaitForMockEndpoint(t *testing.T) { resp, err := server.Client.Do(req) require.NoError(t, err) + defer resp.Body.Close() respStr, err := io.ReadAll(resp.Body) require.NoError(t, err) diff --git a/internal/test/runner.go b/internal/test/runner.go index cbf0773c..0ed8dbc0 100644 --- a/internal/test/runner.go +++ b/internal/test/runner.go @@ -52,6 +52,8 @@ func GetRunnerEnvFromName(name RunnerName) RunnerEnv { switch name { case StoreEventRunnerName: return StoreEventRunnerEnv + case DocInitRunnerName: + return DocInitRunnerEnv case DocSyncRunnerName: return DocSyncRunnerEnv case DocCleanRunnerName: diff --git a/scripts/Taskfile.yml b/scripts/Taskfile.yml index ac260b8f..e5ac5fab 100644 --- a/scripts/Taskfile.yml +++ b/scripts/Taskfile.yml @@ -78,7 +78,9 @@ tasks: - task: docker:lint - task: openapi:lint - task: compose:lint - go:lint: golangci-lint run {{.CLI_ARGS}} + go:lint: + - golangci-lint run {{.CLI_ARGS}} + # - govulncheck ./... yaml:lint: yamllint . -s {{.CLI_ARGS}} json:lint: jsonlint devbox.json -q -s -i {{.CLI_ARGS}} docs:generate: diff --git a/test/queryAPI/accessory_test.go b/test/queryAPI/accessory_test.go index 3dee8e80..518e8ce0 100644 --- a/test/queryAPI/accessory_test.go +++ b/test/queryAPI/accessory_test.go @@ -23,26 +23,31 @@ func TestQueryAPIAccessories(t *testing.T) { resp, err := http.Get(fmt.Sprintf("%s/swagger/doc.json", c.API.URI)) require.NoError(t, err) + defer resp.Body.Close() assert.NotNil(t, resp) assert.Equal(t, http.StatusOK, resp.StatusCode) resp, err = http.Get(fmt.Sprintf("%s/swagger/doc.yaml", c.API.URI)) require.NoError(t, err) + defer resp.Body.Close() assert.NotNil(t, resp) assert.Equal(t, http.StatusOK, resp.StatusCode) resp, err = http.Get(fmt.Sprintf("%s/swagger/index.html", c.API.URI)) require.NoError(t, err) + defer resp.Body.Close() assert.NotNil(t, resp) assert.Equal(t, http.StatusOK, resp.StatusCode) resp, err = http.Get(fmt.Sprintf("%s/metrics", c.API.URI)) require.NoError(t, err) + defer resp.Body.Close() assert.NotNil(t, resp) assert.Equal(t, http.StatusOK, resp.StatusCode) resp, err = http.Get(fmt.Sprintf("%s/health", c.API.URI)) require.NoError(t, err) + defer resp.Body.Close() assert.NotNil(t, resp) assert.Equal(t, http.StatusOK, resp.StatusCode)