[Fix #1166] Relax URI patterns to conform to RFC 3986 - #1169
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the JSON Schema definition for LiteralUri in schema/workflow.yaml to accept relative URI references (e.g., openapi/petstore.json) in addition to absolute URIs with schemes, aligning with RFC 3986 usage in practice.
Changes:
- Replaced the
LiteralUriabsolute-only URI regex with an RFC 3986 URI-reference–style regex to allow relative URIs. - Kept
LiteralUriTemplateas absolute-only to preserve existinguri-templatevalidation constraints.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
schema/workflow.yaml:1445
- The RFC 3986 Appendix B regex is a parsing regex and, as used here, it matches the empty string and also matches whitespace-containing/non-URI strings (because the path component
[^?#]*can consume nearly anything). This significantly relaxes validation compared to the previous scheme-required pattern, especially in validators that don’t enforceformat. Consider at least requiring non-empty values and excluding whitespace/control characters so obvious invalid inputs don’t validate.
- title: LiteralUriTemplate
type: string
format: uri-template
pattern: "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"
.ci/validation/src/uri-pattern.test.ts:63
- Only positive cases are asserted. Adding a small set of negative cases (e.g., empty string and whitespace-containing strings) would prevent regressions where the regex becomes effectively “match anything.”
const networkPathUris = ["//example.com/path", "//localhost:8080/api"];
test.each(absoluteUris)("accepts absolute URI: %s", (uri) => {
expect(URI_REFERENCE_PATTERN.test(uri)).toBe(true);
});
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
schema/workflow.yaml:1445
- The new URI-reference regex still accepts whitespace inside the value (e.g., trailing spaces or
http://example.com/has space) because([^?#]*)allows spaces and the only guard is(?=\S)(which only checks the first character). That contradicts RFC 3986 URI-reference syntax (spaces must be percent-encoded) and also undermines the intent of using a pattern for structural validation whenformatvalidation is inconsistent.
Consider strengthening the pattern to reject any whitespace and (optionally) restore the stricter scheme grammar (while keeping the scheme optional so relative references remain valid).
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
schema/workflow.yaml:1449
- Same concern as above: the current pattern allows whitespace anywhere after the first character and doesn’t enforce RFC 3986’s no-whitespace requirement. Tightening the regex here too keeps
LiteralUrialigned with the stated RFC 3986 URI-reference constraint while still allowing relative references.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
dsl-reference.md:2472
- This sentence states that URI/URI-template types “conform” to RFC 3986 URI-reference syntax, but the schema is using a regex derived from Appendix B (a parsing regex) and it’s intentionally more permissive than the full RFC grammar. To avoid over-claiming strict conformance, it would be clearer to describe this as structural validation based on RFC 3986’s URI-reference regex while noting that it accepts both absolute and relative references.
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.
.ci/validation/src/uri-pattern.test.ts:139
- Given the schema change is intended to follow RFC 3986 URI-reference syntax, it’s worth adding explicit negative cases for whitespace inside an otherwise-valid URI (e.g., trailing spaces or spaces in the path). This prevents regressions where the pattern accidentally accepts unencoded spaces.
test("rejects empty string", () => {
expect(URI_REFERENCE_PATTERN.test("")).toBe(false);
});
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
.ci/validation/src/uri-pattern.test.ts:22
- The tests currently hardcode a regex literal, so they can drift from the actual schema pattern in schema/workflow.yaml without failing. Loading the pattern from the schema (and asserting both LiteralUri and LiteralUriTemplate use the same pattern) makes these tests validate the real contract.
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(
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
schema/workflow.yaml:1449
- The RFC 3986 Appendix B regex is a parsing regex and as written here it will still accept whitespace inside the reference (e.g.,
openapi/pet store.json,https://example.com/has space), which is not valid RFC 3986 URI-reference syntax. If the intent is true RFC 3986 conformance, add a no-whitespace guard to the pattern.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
- title: LiteralUri
type: string
format: uri-reference
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
.ci/validation/src/uri-pattern.test.ts:145
- The new URI-reference pattern currently has no negative test coverage for embedded whitespace (which the Appendix B parsing regex would otherwise match). Adding explicit rejection tests for whitespace-containing references will prevent regressions if the pattern is tightened to better match RFC 3986.
describe("URI pattern rejects invalid or ambiguous values", () => {
const runtimeExpressions = [
"${ .foo }",
" ${ .bar } ",
"${.baz}",
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
.ci/validation/src/uri-pattern.test.ts:162
- Given the intent to conform to RFC 3986 URI-reference syntax, it would be good to explicitly test that the pattern rejects URIs containing unencoded whitespace (spaces/newlines). The current negative tests cover empty/whitespace-only strings but not embedded whitespace.
test("rejects whitespace-only string", () => {
expect(LITERAL_URI_PATTERN.test(" ")).toBe(false);
});
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
schema/workflow.yaml:1449
- The added guard
(?=\\S)only asserts the first character is non-whitespace, but RFC 3986 URI-references cannot contain spaces anywhere; as written, values likehttp://exa mple.comwould still match. Consider strengthening the guard to reject any whitespace in the entire string (and add a corresponding negative test).
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
- title: LiteralUri
type: string
format: uri-reference
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
schema/workflow.yaml:1445
- RFC 3986 scheme syntax requires the first character to be a letter (
ALPHA), but Appendix B’s([^:/?#]+):is a parsing regex and will accept invalid schemes like1http://example.com. If this pattern is intended for validation (as described in the PR), tighten the optional scheme subpattern to enforce the RFC’s scheme rule while keeping it optional for relative references.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
schema/workflow.yaml:1449
- RFC 3986 scheme syntax requires the first character to be a letter (
ALPHA), but Appendix B’s([^:/?#]+):is a parsing regex and will accept invalid schemes like1http://example.com. If this pattern is intended for validation (as described in the PR), tighten the optional scheme subpattern to enforce the RFC’s scheme rule while keeping it optional for relative references.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
.ci/validation/src/uri-pattern.test.ts:84
- The current tests cover many “accept” cases plus empty/whitespace-only and runtime-expression rejection, but they don’t cover important “reject” cases introduced by using Appendix B as a validator (e.g., URIs containing internal whitespace, and invalid schemes like
1http://...). Adding explicit negative tests for these cases will lock in the intended validation behavior and catch regressions if the pattern is refined.
test.each(absoluteUris)("accepts absolute URI: %s", (uri) => {
expect(LITERAL_URI_PATTERN.test(uri)).toBe(true);
});
.ci/validation/src/uri-pattern.test.ts:154
- The current tests cover many “accept” cases plus empty/whitespace-only and runtime-expression rejection, but they don’t cover important “reject” cases introduced by using Appendix B as a validator (e.g., URIs containing internal whitespace, and invalid schemes like
1http://...). Adding explicit negative tests for these cases will lock in the intended validation behavior and catch regressions if the pattern is refined.
test.each(runtimeExpressions)(
"rejects runtime expression: %s",
(expr) => {
expect(LITERAL_URI_PATTERN.test(expr)).toBe(false);
}
);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
schema/workflow.yaml:1449
- The RFC 3986 Appendix B regex is primarily a parsing regex, not a strict validation regex. As used here, it will accept values that are not valid RFC 3986 URI-references (e.g., whitespace inside the reference, or schemes that don’t start with a letter like
1abc://...). If the goal is conformance-level validation, consider tightening the pattern by (a) rejecting any whitespace anywhere (not just at the start), and (b) enforcing RFC 3986 scheme syntax when thescheme:group is present (scheme must start with[A-Za-z]and then[A-Za-z0-9+\\-.]*).
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
- title: LiteralUri
type: string
format: uri-reference
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
schema/workflow.yaml:1448
- Changing the JSON Schema
formatfromuritouri-referencemay reduce compatibility with validators/tooling that don’t recognizeuri-reference(some treat unknown formats as errors rather than annotations). If broad validator compatibility is a requirement, consider either keepingformat: uriand relying on the updatedpatternto allow relative references, or dropping theformatentirely forLiteralUriand documenting thatpatternis the authoritative validation.
format: uri-reference
.ci/validation/src/uri-pattern.test.ts:163
- The rejection suite covers runtime-expression-like values and empty/whitespace-only strings, but it doesn’t cover key invalid URI-reference cases that the current regex likely allows (e.g., embedded whitespace such as
\"http://example.com/has space\"or trailing whitespace, and invalid scheme syntax like\"1abc://example.com\"). Adding explicit negative tests for these cases will prevent accidental over-acceptance and clarify the intended strictness of the schema validation.
describe("URI pattern rejects invalid or ambiguous values", () => {
const runtimeExpressions = [
"${ .foo }",
" ${ .bar } ",
"${.baz}",
"${ .context.endpoint }",
];
test.each(runtimeExpressions)(
"rejects runtime expression: %s",
(expr) => {
expect(LITERAL_URI_PATTERN.test(expr)).toBe(false);
}
);
test("rejects empty string", () => {
expect(LITERAL_URI_PATTERN.test("")).toBe(false);
});
test("rejects whitespace-only string", () => {
expect(LITERAL_URI_PATTERN.test(" ")).toBe(false);
});
});
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
dsl-reference.md:2467
- This text states the fields are validated as RFC 3986 URI-references, but the implemented pattern also enforces additional constraints (non-empty via
(?=\\S)and the${...}guard). Please document these extra constraints explicitly (e.g., non-empty / no leading whitespace / runtime-expression exclusion), or adjust the wording to clarify this is a structural approximation rather than full RFC 3986 conformance.
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).
.ci/validation/src/uri-pattern.test.ts:163
- Given the new pattern is meant to validate RFC 3986 URI-references, the rejection tests are currently too narrow to catch common invalid inputs that the Appendix B regex can mistakenly accept (notably internal whitespace and invalid schemes). Add negative test cases such as strings containing spaces (e.g.,
openapi/pet store.json) and schemes that violate RFC 3986 (e.g.,1abc://example.com) so the test suite enforces the intended stricter behavior.
describe("URI pattern rejects invalid or ambiguous values", () => {
const runtimeExpressions = [
"${ .foo }",
" ${ .bar } ",
"${.baz}",
"${ .context.endpoint }",
];
test.each(runtimeExpressions)(
"rejects runtime expression: %s",
(expr) => {
expect(LITERAL_URI_PATTERN.test(expr)).toBe(false);
}
);
test("rejects empty string", () => {
expect(LITERAL_URI_PATTERN.test("")).toBe(false);
});
test("rejects whitespace-only string", () => {
expect(LITERAL_URI_PATTERN.test(" ")).toBe(false);
});
});
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
schema/workflow.yaml:1449
- The exact same (complex) regex is duplicated for
LiteralUriTemplateandLiteralUri. Even with the consistency test, duplication in the schema increases the chance of future drift across definitions or other schema copies. Consider factoring the shared constraint into a single$defsschema (e.g., aUriReferenceStringdef that containstype+pattern), and then composing it into both branches viaallOf/$refso the pattern has a single source of truth.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
schema/workflow.yaml:1453
- The exact same (complex) regex is duplicated for
LiteralUriTemplateandLiteralUri. Even with the consistency test, duplication in the schema increases the chance of future drift across definitions or other schema copies. Consider factoring the shared constraint into a single$defsschema (e.g., aUriReferenceStringdef that containstype+pattern), and then composing it into both branches viaallOf/$refso the pattern has a single source of truth.
pattern: "^(?!\\s*\\$\\{)(?=\\S)(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"
.ci/validation/src/uri-pattern.test.ts:163
- Given the schema pattern is intended to validate URI-references, add explicit negative cases for invalid-but-currently-matchable inputs such as internal whitespace (e.g.,
\"http://example.com/hello world\",\"path with spaces/file.json\") and control characters. This will prevent regressions and will also enforce any tightening of the regex (e.g., a no-whitespace guard) as a required behavior.
describe("URI pattern rejects invalid or ambiguous values", () => {
const runtimeExpressions = [
"${ .foo }",
" ${ .bar } ",
"${.baz}",
"${ .context.endpoint }",
];
test.each(runtimeExpressions)(
"rejects runtime expression: %s",
(expr) => {
expect(LITERAL_URI_PATTERN.test(expr)).toBe(false);
}
);
test("rejects empty string", () => {
expect(LITERAL_URI_PATTERN.test("")).toBe(false);
});
test("rejects whitespace-only string", () => {
expect(LITERAL_URI_PATTERN.test(" ")).toBe(false);
});
});
…Uri 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 <ricardozanini@gmail.com>
- 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 <ricardozanini@gmail.com>
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 <ricardozanini@gmail.com>
- 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 <ricardozanini@gmail.com>
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 <ricardozanini@gmail.com>
- 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 <ricardozanini@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
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 <ricardozanini@gmail.com>
fea67af to
27ab781
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
.ci/validation/src/uri-pattern.test.ts:37
- The tests in this file only validate the regex patterns, but CI schema validation also enforces
formatvia Ajv (addFormats). To ensure this PR actually allows relative values end-to-end, add a couple of schema-validation tests that validate a minimal workflow using a relative URI-reference and a relative URI-template (with{}), so we catch anyformat: uri-reference/format: uri-templatebehavior that would still reject them.
describe("Schema pattern consistency", () => {
Summary
LiteralUriandLiteralUriTemplatepatterns only accepted absolute URIs with a schemeopenapi/petstore.jsonorproto/greeter.protoare valid per RFC 3986 and used in practiceContext
The original absolute-only pattern was added in #1017 to work around
format: uri-templateinconsistencies across JSON Schema validators. The RFC 3986 regex maintains structural validation while supporting relative references.Changes
schema/workflow.yaml: Updated bothLiteralUriandLiteralUriTemplatepatterns to RFC 3986 Appendix B regexdsl-reference.md: Added reasoning that all URI types conform to RFC 3986 URI-reference syntax.ci/validation/src/uri-pattern.test.ts: Added 39 tests covering absolute URIs, relative paths, query/fragment combinations, network-path references, and URI templatesFixes #1166