From 77ddbf1d49d61ea3e906002957c8922a7a8f5756 Mon Sep 17 00:00:00 2001 From: Fabio Bonelli Date: Fri, 4 Sep 2026 05:57:42 +0200 Subject: [PATCH] fix: accept every RFC 3339 form in the from and to parameters --- CHANGELOG.md | 1 + internal/handlers/general/query.go | 4 ++-- logs_test.go | 36 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9d332..47269f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A `DELETE` of a missing software answered `404` but still recorded a delete event and notified the webhooks. - The event recorded by an analysis `PATCH` had no actor. +- `?from` and `?to` refused RFC 3339 values with a zone offset. - Catalogs, bundles and webhooks wrote no event to the audit trail. - A webhook `POST` with the url of an existing one answered `500` instead of `409`. diff --git a/internal/handlers/general/query.go b/internal/handlers/general/query.go index 643bd51..1ac1819 100644 --- a/internal/handlers/general/query.go +++ b/internal/handlers/general/query.go @@ -49,7 +49,7 @@ func Clauses(ctx *fiber.Ctx, stmt *gorm.DB, filterField, searchField string) (*g } if from := ctx.Query("from", ""); from != "" { - at, err := time.Parse("2006-01-02T15:04:05Z", from) + at, err := time.Parse(time.RFC3339, from) if err != nil { return nil, common.ErrInvalidDateTime } @@ -58,7 +58,7 @@ func Clauses(ctx *fiber.Ctx, stmt *gorm.DB, filterField, searchField string) (*g } if to := ctx.Query("to", ""); to != "" { - at, err := time.Parse("2006-01-02T15:04:05Z", to) + at, err := time.Parse(time.RFC3339, to) if err != nil { return nil, common.ErrInvalidDateTime } diff --git a/logs_test.go b/logs_test.go index 93c0dac..6808140 100644 --- a/logs_test.go +++ b/logs_test.go @@ -177,6 +177,30 @@ func TestLogsEndpoints(t *testing.T) { assert.Equal(t, 20, len(data)) }, }, + { + description: `GET with "from" query param carrying a zone offset`, + query: "GET /v1/logs?from=2010-03-01T11:56:23%2B02:00", + + expectedCode: 200, + expectedContentType: "application/json", + validateFunc: func(t *testing.T, response map[string]any) { + data := assertListResponse(t, response) + + assert.Equal(t, 20, len(data)) + }, + }, + { + description: `GET with "from" query param carrying fractional seconds`, + query: "GET /v1/logs?from=2010-03-01T09:56:23.000000Z", + + expectedCode: 200, + expectedContentType: "application/json", + validateFunc: func(t *testing.T, response map[string]any) { + data := assertListResponse(t, response) + + assert.Equal(t, 20, len(data)) + }, + }, { description: `GET with invalid "from" query param`, query: "GET /v1/logs?from=3", @@ -200,6 +224,18 @@ func TestLogsEndpoints(t *testing.T) { assert.Equal(t, 6, len(data)) }, }, + { + description: `GET with "to" query param carrying a zone offset`, + query: "GET /v1/logs?to=2010-03-01T11:56:23%2B02:00", + + expectedCode: 200, + expectedContentType: "application/json", + validateFunc: func(t *testing.T, response map[string]any) { + data := response["data"].([]any) + + assert.Equal(t, 6, len(data)) + }, + }, { description: `GET with invalid "to" query param`, query: "GET /v1/logs?to=3",