diff --git a/internal/handlers/validate.go b/internal/handlers/validate.go index 05393e8..96af4ae 100644 --- a/internal/handlers/validate.go +++ b/internal/handlers/validate.go @@ -32,10 +32,14 @@ func (vh *PubliccodeymlValidatorHandler) Query(ctx fiber.Ctx) error { var normalized *string valid := true - parser := vh.parser - if checks := fiber.Query[bool](ctx, "external-checks", false); checks { - parser = vh.parserExternalChecks + parser, invalidExternalChecks := vh.parserForExternalChecks(ctx) + if invalidExternalChecks { + return common.Error( + fiber.StatusBadRequest, + "invalid external-checks parameter", + "external-checks must be true or false", + ) } if len(ctx.Body()) == 0 { @@ -72,3 +76,19 @@ func (vh *PubliccodeymlValidatorHandler) Query(ctx fiber.Ctx) error { //nolint:wrapcheck return ctx.JSON(fiber.Map{"valid": valid, "results": results, "normalized": normalized}) } + +func (vh *PubliccodeymlValidatorHandler) parserForExternalChecks(ctx fiber.Ctx) (*publiccodeParser.Parser, bool) { + args := ctx.Request().URI().QueryArgs() + if !args.Has("external-checks") { + return vh.parser, false + } + + switch string(args.Peek("external-checks")) { + case "true": + return vh.parserExternalChecks, false + case "false": + return vh.parser, false + default: + return nil, true + } +} diff --git a/main_test.go b/main_test.go index d1a8b68..6b3a914 100644 --- a/main_test.go +++ b/main_test.go @@ -84,7 +84,7 @@ func runTestCases(t *testing.T, tests []TestCase) { if test.headers != nil { req.Header = test.headers } - req.URL.RawQuery = u.Query().Encode() + req.URL.RawQuery = u.RawQuery res, err := app.Test(req, fiber.TestConfig{Timeout: 0, FailOnTimeout: false}) assert.Nil(t, err) @@ -185,6 +185,17 @@ func TestValidateEndpoint(t *testing.T) { assert.Empty(t, response["results"]) }, }, + { + description: "validate: external checks explicitly disabled", + query: "QUERY /v1/validate?external-checks=false", + body: externalChecksYml, + expectedCode: 200, + expectedContentType: "application/json; charset=utf-8", + validateFunc: func(t *testing.T, response map[string]any) { + assert.Equal(t, true, response["valid"]) + assert.Empty(t, response["results"]) + }, + }, { description: "validate: external checks enabled", query: "QUERY /v1/validate?external-checks=true", @@ -200,6 +211,29 @@ func TestValidateEndpoint(t *testing.T) { assert.Nil(t, response["normalized"]) }, }, + { + description: "validate: repeated external checks use first value", + query: "QUERY /v1/validate?external-checks=true&external-checks=false", + body: externalChecksYml, + expectedCode: 200, + expectedContentType: "application/json; charset=utf-8", + validateFunc: func(t *testing.T, response map[string]any) { + assert.Equal(t, false, response["valid"]) + results, ok := response["results"].([]any) + assert.True(t, ok) + assert.NotEmpty(t, results) + assert.Contains(t, fmt.Sprint(results), "non-public") + assert.Nil(t, response["normalized"]) + }, + }, + { + description: "validate: malformed external checks flag", + query: "QUERY /v1/validate?external-checks=invalid", + body: validYml, + expectedCode: 400, + expectedBody: `{"title":"invalid external-checks parameter","detail":"external-checks must be true or false","status":400}`, + expectedContentType: "application/problem+json", + }, { description: "validate: invalid file", query: "QUERY /v1/validate", diff --git a/publiccode-validator-api.oas.yaml b/publiccode-validator-api.oas.yaml index 3904ee5..10b1515 100644 --- a/publiccode-validator-api.oas.yaml +++ b/publiccode-validator-api.oas.yaml @@ -96,15 +96,22 @@ paths: description: "missing required field" normalized: null "400": - description: Bad request (e.g. empty body) + description: Bad request (e.g. empty body or invalid external-checks value) content: application/problem+json: schema: $ref: "#/components/schemas/ProblemJSON" - example: - title: empty body - detail: need a body to validate - status: 400 + examples: + emptyBody: + value: + title: empty body + detail: need a body to validate + status: 400 + invalidExternalChecks: + value: + title: invalid external-checks parameter + detail: external-checks must be true or false + status: 400 "405": description: Method not allowed headers: