{"record":{"id":"1a6761e09006afdf","repo":"ankane/pghero","slug":"invalid-sort","errorCode":null,"errorMessage":"Invalid sort","messagePattern":"Invalid sort","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/pghero/methods/query_stats.rb","lineNumber":20,"sourceCode":"  module Methods\n    module QueryStats\n      def query_stats(\n        current: true,\n        historical: false,\n        limit: nil,\n        sort: nil,\n        user: nil,\n        query_hash: nil,\n        start_at: nil,\n        end_at: nil,\n        min_average_time: nil,\n        min_calls: nil\n      )\n        limit ||= 100\n\n        sort ||= \"total_time\"\n        unless [\"total_time\", \"average_time\", \"calls\"].include?(sort)\n          raise ArgumentError, \"Invalid sort\"\n        end\n\n        current_query_stats, current_total_time =\n          if !current || (historical && end_at && end_at < Time.now)\n            [[], 0]\n          else\n            current_query_stats(limit: limit, sort: sort, user: user, query_hash: query_hash)\n          end\n\n        historical_query_stats, historical_total_time =\n          if historical && historical_query_stats_enabled?\n            historical_query_stats(limit: limit, sort: sort, user: user, query_hash: query_hash, start_at: start_at, end_at: end_at)\n          else\n            [[], 0]\n          end\n\n        query_stats = current_query_stats + historical_query_stats\n        query_stats = combine_query_stats(query_stats.group_by { |q| [q[:query_hash], q[:user]] })","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/ankane/pghero/blob/7edb57986ffd36f9d64f0830c4ccc90a5eac46d6/lib/pghero/methods/query_stats.rb#L2-L38","documentation":"query_stats(sort:) orders current and historical stats by the given column; before touching the database it validates that sort is exactly \"total_time\", \"average_time\", or \"calls\" (strings). Any other value raises ArgumentError \"Invalid sort\". The sort value is also interpolated into SQL (it selects the average_time expression only when sort == \"average_time\"), hence the strict whitelist.","triggerScenarios":"database.query_stats(sort: \"total\") (abbreviated); sort: :calls as a symbol; forwarding params[:sort] from a custom controller or API without whitelisting; copying code that used an older allowed value.","commonSituations":"Custom dashboards built on PgHero's query_stats; API endpoints exposing the sort parameter to users; scheduled reports hardcoding a column label.","solutions":["Pass one of the exact strings: \"total_time\", \"average_time\", or \"calls\" (sort is optional and defaults to \"total_time\")","Whitelist request input: sort = %w[total_time average_time calls].include?(params[:sort]) ? params[:sort] : nil","Convert symbols at the boundary with to_s if your code stores symbols"],"exampleFix":"# before\ndatabase.query_stats(sort: params[:sort])\n\n# after\nsort = %w[total_time average_time calls].include?(params[:sort]) ? params[:sort] : nil\ndatabase.query_stats(sort: sort)","handlingStrategy":"validation","validationCode":"sort = params[:sort] if %w[total_time average_time calls].include?(params[:sort])\ndatabase.query_stats(sort: sort)","typeGuard":"def valid_query_stats_sort?(value)\n  %w[total_time average_time calls].include?(value)\nend","tryCatchPattern":null,"preventionTips":["Whitelist sort at the request boundary instead of forwarding params verbatim","Let sort default to nil (pghero falls back to \"total_time\") rather than sending arbitrary values"],"tags":["pghero","query-stats","argument-validation","ruby"],"backgroundTag":"invalid-enum-value","analyzedSha":"7edb57986ffd36f9d64f0830c4ccc90a5eac46d6","analyzedAt":"2026-08-21T17:33:54.942Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}