From 7a6fcd3b84ed313935ab8f9762fe3e1245dc07f4 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 12:42:28 -0400 Subject: [PATCH 1/8] [Fix #1166] Allow relative URIs in LiteralUri pattern Replace the absolute-only URI pattern with the RFC 3986 Appendix B URI-reference regex, which accepts both absolute and relative URIs. This allows values like `openapi/petstore.json` or `proto/greeter.proto`. LiteralUriTemplate keeps the absolute pattern since templates need a scheme. Signed-off-by: Ricardo Zanini --- schema/workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/workflow.yaml b/schema/workflow.yaml index 11ad36c3..d81b8d0a 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -1459,7 +1459,7 @@ $defs: - title: LiteralUri type: string format: uri - pattern: "^[A-Za-z][A-Za-z0-9+\\-.]*://.*" + pattern: "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" endpoint: title: Endpoint description: Represents an endpoint. From 609ece53197c01561787e231b57b31ef6568c75d Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 14:29:43 -0400 Subject: [PATCH 2/8] Relax LiteralUriTemplate pattern and add RFC 3986 reasoning - Update LiteralUriTemplate pattern to RFC 3986 Appendix B regex, matching the LiteralUri change - Add reasoning in dsl-reference.md that URI types conform to RFC 3986 URI-reference syntax - Add 39 tests for URI pattern validation Signed-off-by: Ricardo Zanini --- .ci/validation/src/uri-pattern.test.ts | 118 +++++++++++++++++++++++++ dsl-reference.md | 2 + schema/workflow.yaml | 2 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 .ci/validation/src/uri-pattern.test.ts diff --git a/.ci/validation/src/uri-pattern.test.ts b/.ci/validation/src/uri-pattern.test.ts new file mode 100644 index 00000000..b8d74ad5 --- /dev/null +++ b/.ci/validation/src/uri-pattern.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright 2023-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// RFC 3986 Appendix B URI-reference regex used in schema/workflow.yaml +// for both LiteralUri and LiteralUriTemplate +const URI_REFERENCE_PATTERN = + /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; + +describe("LiteralUri pattern (RFC 3986 URI-reference)", () => { + const absoluteUris = [ + "http://example.com", + "https://example.com/path", + "https://example.com/path?query=1", + "https://example.com/path#fragment", + "https://example.com/path?query=1#fragment", + "https://user:pass@example.com:8080/path", + "ftp://files.example.com/public", + "grpc://localhost:50051", + "file:///etc/hosts", + "custom-scheme://authority/path", + ]; + + const relativePathUris = [ + "openapi/petstore.json", + "proto/greeter.proto", + "schemas/user.yaml", + "docs/api.json", + "../parent/file.json", + "./sibling/file.yaml", + "file.json", + ]; + + const absolutePathUris = [ + "/api/v1/users", + "/openapi/petstore.json", + "/proto/greeter.proto", + ]; + + const urisWithQueryFragment = [ + "openapi/petstore.json?version=3", + "/api/docs#section", + "resource?key=value&other=1", + "path/to/resource#anchor", + ]; + + const networkPathUris = ["//example.com/path", "//localhost:8080/api"]; + + test.each(absoluteUris)("accepts absolute URI: %s", (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + }); + + test.each(relativePathUris)("accepts relative path URI: %s", (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + }); + + test.each(absolutePathUris)("accepts absolute-path URI: %s", (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + }); + + test.each(urisWithQueryFragment)( + "accepts URI with query/fragment: %s", + (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + } + ); + + test.each(networkPathUris)("accepts network-path URI: %s", (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + }); +}); + +describe("LiteralUriTemplate pattern (RFC 3986 URI-reference)", () => { + const absoluteTemplates = [ + "http://example.com", + "https://example.com/path/{id}", + "https://server.com/{path}", + "https://api.example.com/v1/{resource}?limit={limit}", + "ftp://files.example.com", + "grpc://localhost:50051", + "custom-scheme://authority/path", + ]; + + const relativeTemplates = [ + "openapi/{version}/petstore.json", + "{basePath}/resource", + "proto/greeter.proto", + "../{parent}/file.json", + "/api/{version}/users", + "docs/{file}.json", + ]; + + test.each(absoluteTemplates)( + "accepts absolute URI template: %s", + (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + } + ); + + test.each(relativeTemplates)( + "accepts relative URI template: %s", + (uri) => { + expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + } + ); +}); diff --git a/dsl-reference.md b/dsl-reference.md index 833d9408..36cfd635 100644 --- a/dsl-reference.md +++ b/dsl-reference.md @@ -2464,6 +2464,8 @@ headers: The DSL has limited support for URI template syntax as defined by [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570). Specifically, only the [Simple String Expansion](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.2) is supported, which allows authors to embed variables in a URI. +All URI and URI template types in the DSL conform to the [URI-reference](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B) syntax defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. + To substitute a variable within a URI, use the `{}` syntax. The identifier inside the curly braces will be replaced with its value during runtime evaluation. If no value is found for the identifier, an empty string will be used. This has the following limitations compared to runtime expressions: diff --git a/schema/workflow.yaml b/schema/workflow.yaml index d81b8d0a..a3aa4d22 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -1455,7 +1455,7 @@ $defs: - title: LiteralUriTemplate type: string format: uri-template - pattern: "^[A-Za-z][A-Za-z0-9+\\-.]*://.*" + pattern: "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" - title: LiteralUri type: string format: uri From f1f817ea6ce53c4982d5e17b29f959a59cb015b7 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 14:38:14 -0400 Subject: [PATCH 3/8] Anchor URI pattern and exclude runtime expressions The RFC 3986 Appendix B regex matches any string (including empty and runtime expressions), breaking oneOf disambiguation. Add end anchor, negative lookahead for ${ expressions, and require at least one non-whitespace character. Signed-off-by: Ricardo Zanini --- .ci/validation/src/uri-pattern.test.ts | 32 +++++++++++++++++++++++--- schema/workflow.yaml | 4 ++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.ci/validation/src/uri-pattern.test.ts b/.ci/validation/src/uri-pattern.test.ts index b8d74ad5..269b2cad 100644 --- a/.ci/validation/src/uri-pattern.test.ts +++ b/.ci/validation/src/uri-pattern.test.ts @@ -14,10 +14,12 @@ * limitations under the License. */ -// RFC 3986 Appendix B URI-reference regex used in schema/workflow.yaml -// for both LiteralUri and LiteralUriTemplate +// Anchored RFC 3986 URI-reference regex used in schema/workflow.yaml +// for both LiteralUri and LiteralUriTemplate. +// Adds: $ end anchor, (?!\s*\$\{) to exclude runtime expressions, +// (?=\S) to reject empty/whitespace-only strings. const URI_REFERENCE_PATTERN = - /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; + /^(?!\s*\$\{)(?=\S)(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; describe("LiteralUri pattern (RFC 3986 URI-reference)", () => { const absoluteUris = [ @@ -116,3 +118,27 @@ describe("LiteralUriTemplate pattern (RFC 3986 URI-reference)", () => { } ); }); + +describe("URI pattern rejects invalid or ambiguous values", () => { + const runtimeExpressions = [ + "${ .foo }", + " ${ .bar } ", + "${.baz}", + "${ .context.endpoint }", + ]; + + test.each(runtimeExpressions)( + "rejects runtime expression: %s", + (expr) => { + expect(URI_REFERENCE_PATTERN.test(expr)).toBe(false); + } + ); + + test("rejects empty string", () => { + expect(URI_REFERENCE_PATTERN.test("")).toBe(false); + }); + + test("rejects whitespace-only string", () => { + expect(URI_REFERENCE_PATTERN.test(" ")).toBe(false); + }); +}); diff --git a/schema/workflow.yaml b/schema/workflow.yaml index a3aa4d22..e001da60 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -1455,11 +1455,11 @@ $defs: - title: LiteralUriTemplate type: string format: uri-template - pattern: "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" + pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" - title: LiteralUri type: string format: uri - pattern: "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" + pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" endpoint: title: Endpoint description: Represents an endpoint. From 73a99f93a5d51ac1afd986846b2eaa5d5f462790 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 14:42:01 -0400 Subject: [PATCH 4/8] Use format uri-reference for LiteralUri and fix doc link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change LiteralUri format from uri to uri-reference to semantically match relative reference support - Fix dsl-reference.md link to RFC 3986 §4.1 (URI-reference definition) instead of Appendix B (parsing regex) Signed-off-by: Ricardo Zanini --- dsl-reference.md | 2 +- schema/workflow.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsl-reference.md b/dsl-reference.md index 36cfd635..f6a739da 100644 --- a/dsl-reference.md +++ b/dsl-reference.md @@ -2464,7 +2464,7 @@ headers: The DSL has limited support for URI template syntax as defined by [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570). Specifically, only the [Simple String Expansion](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.2) is supported, which allows authors to embed variables in a URI. -All URI and URI template types in the DSL conform to the [URI-reference](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B) syntax defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. +All URI and URI template types in the DSL conform to the [URI-reference](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) syntax defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. To substitute a variable within a URI, use the `{}` syntax. The identifier inside the curly braces will be replaced with its value during runtime evaluation. If no value is found for the identifier, an empty string will be used. diff --git a/schema/workflow.yaml b/schema/workflow.yaml index e001da60..cf6d9058 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -1458,7 +1458,7 @@ $defs: pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" - title: LiteralUri type: string - format: uri + format: uri-reference pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" endpoint: title: Endpoint From 7c9cfe6db0f2924b9fb067bf7db8b7c98f7570fe Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 14:46:18 -0400 Subject: [PATCH 5/8] Derive URI test pattern from schema to prevent drift Read LiteralUri and LiteralUriTemplate patterns directly from schema/workflow.yaml instead of duplicating the regex in a JS literal. Also assert both patterns are identical. Signed-off-by: Ricardo Zanini --- .ci/validation/src/uri-pattern.test.ts | 51 ++++++++++++++++++-------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/.ci/validation/src/uri-pattern.test.ts b/.ci/validation/src/uri-pattern.test.ts index 269b2cad..2c00fa9f 100644 --- a/.ci/validation/src/uri-pattern.test.ts +++ b/.ci/validation/src/uri-pattern.test.ts @@ -14,12 +14,31 @@ * limitations under the License. */ -// Anchored RFC 3986 URI-reference regex used in schema/workflow.yaml -// for both LiteralUri and LiteralUriTemplate. -// Adds: $ end anchor, (?!\s*\$\{) to exclude runtime expressions, -// (?=\S) to reject empty/whitespace-only strings. -const URI_REFERENCE_PATTERN = - /^(?!\s*\$\{)(?=\S)(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; +import * as fs from "fs"; +import * as path from "path"; +import * as yaml from "js-yaml"; + +const schemaPath = path.resolve(__dirname, "../../../schema/workflow.yaml"); +const schema = yaml.load( + fs.readFileSync(schemaPath, "utf-8") +) as any; + +const uriTemplateDef = schema["$defs"]["uriTemplate"]; +const literalUriTemplateDef = uriTemplateDef.anyOf.find( + (v: any) => v.title === "LiteralUriTemplate" +); +const literalUriDef = uriTemplateDef.anyOf.find( + (v: any) => v.title === "LiteralUri" +); + +const LITERAL_URI_PATTERN = new RegExp(literalUriDef.pattern); +const LITERAL_URI_TEMPLATE_PATTERN = new RegExp(literalUriTemplateDef.pattern); + +describe("Schema pattern consistency", () => { + test("LiteralUri and LiteralUriTemplate use the same pattern", () => { + expect(literalUriDef.pattern).toBe(literalUriTemplateDef.pattern); + }); +}); describe("LiteralUri pattern (RFC 3986 URI-reference)", () => { const absoluteUris = [ @@ -61,26 +80,26 @@ describe("LiteralUri pattern (RFC 3986 URI-reference)", () => { const networkPathUris = ["//example.com/path", "//localhost:8080/api"]; test.each(absoluteUris)("accepts absolute URI: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_PATTERN.test(uri)).toBe(true); }); test.each(relativePathUris)("accepts relative path URI: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_PATTERN.test(uri)).toBe(true); }); test.each(absolutePathUris)("accepts absolute-path URI: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_PATTERN.test(uri)).toBe(true); }); test.each(urisWithQueryFragment)( "accepts URI with query/fragment: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_PATTERN.test(uri)).toBe(true); } ); test.each(networkPathUris)("accepts network-path URI: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_PATTERN.test(uri)).toBe(true); }); }); @@ -107,14 +126,14 @@ describe("LiteralUriTemplate pattern (RFC 3986 URI-reference)", () => { test.each(absoluteTemplates)( "accepts absolute URI template: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_TEMPLATE_PATTERN.test(uri)).toBe(true); } ); test.each(relativeTemplates)( "accepts relative URI template: %s", (uri) => { - expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true); + expect(LITERAL_URI_TEMPLATE_PATTERN.test(uri)).toBe(true); } ); }); @@ -130,15 +149,15 @@ describe("URI pattern rejects invalid or ambiguous values", () => { test.each(runtimeExpressions)( "rejects runtime expression: %s", (expr) => { - expect(URI_REFERENCE_PATTERN.test(expr)).toBe(false); + expect(LITERAL_URI_PATTERN.test(expr)).toBe(false); } ); test("rejects empty string", () => { - expect(URI_REFERENCE_PATTERN.test("")).toBe(false); + expect(LITERAL_URI_PATTERN.test("")).toBe(false); }); test("rejects whitespace-only string", () => { - expect(URI_REFERENCE_PATTERN.test(" ")).toBe(false); + expect(LITERAL_URI_PATTERN.test(" ")).toBe(false); }); }); From 7147c1818aecc90965733e15c295336c0cdd99be Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Thu, 23 Jul 2026 15:33:35 -0400 Subject: [PATCH 6/8] Address review: use node: imports and clarify template wording - Use node:fs and node:path import specifiers to match project convention - Clarify that URI templates are validated before variable expansion, avoiding implying strict RFC 3986 conformance for unexpanded templates Signed-off-by: Ricardo Zanini --- .ci/validation/src/uri-pattern.test.ts | 4 ++-- dsl-reference.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/validation/src/uri-pattern.test.ts b/.ci/validation/src/uri-pattern.test.ts index 2c00fa9f..8a1e77cd 100644 --- a/.ci/validation/src/uri-pattern.test.ts +++ b/.ci/validation/src/uri-pattern.test.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import * as fs from "fs"; -import * as path from "path"; +import * as fs from "node:fs"; +import * as path from "node:path"; import * as yaml from "js-yaml"; const schemaPath = path.resolve(__dirname, "../../../schema/workflow.yaml"); diff --git a/dsl-reference.md b/dsl-reference.md index f6a739da..a955484f 100644 --- a/dsl-reference.md +++ b/dsl-reference.md @@ -2464,7 +2464,7 @@ headers: The DSL has limited support for URI template syntax as defined by [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570). Specifically, only the [Simple String Expansion](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.2) is supported, which allows authors to embed variables in a URI. -All URI and URI template types in the DSL conform to the [URI-reference](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) syntax defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. +All URI types in the DSL accept [URI-references](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) as defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. URI templates are validated against the same pattern before variable expansion. To substitute a variable within a URI, use the `{}` syntax. The identifier inside the curly braces will be replaced with its value during runtime evaluation. If no value is found for the identifier, an empty string will be used. From 82e31d5f3e800833f7f4c12cab6757db751a5730 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:41:55 -0400 Subject: [PATCH 7/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- dsl-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsl-reference.md b/dsl-reference.md index a955484f..42869e41 100644 --- a/dsl-reference.md +++ b/dsl-reference.md @@ -2464,7 +2464,7 @@ headers: The DSL has limited support for URI template syntax as defined by [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570). Specifically, only the [Simple String Expansion](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.2) is supported, which allows authors to embed variables in a URI. -All URI types in the DSL accept [URI-references](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) as defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. URI templates are validated against the same pattern before variable expansion. +URI-typed string fields that use the schema’s `uriTemplate` type accept [URI-references](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) as defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), meaning both absolute URIs (e.g., `https://example.com/path`) and relative references (e.g., `openapi/petstore.json`, `/api/v1/users`) are accepted. In the JSON Schema, both `LiteralUri` and `LiteralUriTemplate` are validated using the RFC 3986 Appendix B URI-reference regex (with an extra guard to avoid matching runtime expressions). To substitute a variable within a URI, use the `{}` syntax. The identifier inside the curly braces will be replaced with its value during runtime evaluation. If no value is found for the identifier, an empty string will be used. From 27ab7814506cb2c28afa28251f957265dd737aed Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Tue, 28 Jul 2026 15:59:09 -0400 Subject: [PATCH 8/8] Reject whitespace in URI patterns The RFC 3986 Appendix B regex is a parsing regex that permits whitespace inside path, query, and fragment segments. Tighten the character classes to reject whitespace anywhere in the URI by excluding \s from [^?#], [^#], and (.*) groups. Signed-off-by: Ricardo Zanini --- .ci/validation/src/uri-pattern.test.ts | 18 ++++++++++++++++++ schema/workflow.yaml | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.ci/validation/src/uri-pattern.test.ts b/.ci/validation/src/uri-pattern.test.ts index 8a1e77cd..52c4d430 100644 --- a/.ci/validation/src/uri-pattern.test.ts +++ b/.ci/validation/src/uri-pattern.test.ts @@ -160,4 +160,22 @@ describe("URI pattern rejects invalid or ambiguous values", () => { test("rejects whitespace-only string", () => { expect(LITERAL_URI_PATTERN.test(" ")).toBe(false); }); + + const urisWithWhitespace = [ + "http://example.com/path with spaces", + "openapi/pet store.json", + "/api/v1/my resource", + "https://example.com/path?query=has space", + "https://example.com/path#frag ment", + "proto/greeter .proto", + "//example.com/some path", + ]; + + test.each(urisWithWhitespace)( + "rejects URI containing whitespace: %s", + (uri) => { + expect(LITERAL_URI_PATTERN.test(uri)).toBe(false); + expect(LITERAL_URI_TEMPLATE_PATTERN.test(uri)).toBe(false); + } + ); }); diff --git a/schema/workflow.yaml b/schema/workflow.yaml index cf6d9058..b8dc7ff2 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -1455,11 +1455,11 @@ $defs: - title: LiteralUriTemplate type: string format: uri-template - pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" + pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#\\s]*))?([^?#\\s]*)(\\?([^#\\s]*))?(#(\\S*))?$" - title: LiteralUri type: string format: uri-reference - pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$" + pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#\\s]*))?([^?#\\s]*)(\\?([^#\\s]*))?(#(\\S*))?$" endpoint: title: Endpoint description: Represents an endpoint.