diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-evaluate_schema.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-evaluate_schema.all.hurl
index a85ffacb..d0dd253c 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-evaluate_schema.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-evaluate_schema.all.hurl
@@ -300,6 +300,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 914,
+ "method": "tools/call",
+ "params": {
+ "name": "evaluate_schema",
+ "arguments": { "schema": "{{base}}/test/object#meta", "stringifiedInstance": "\"hi\"" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 914
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
index 65c66517..9cad8469 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
@@ -549,6 +549,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 215,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_dependencies",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 215
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
index ac3aee7b..669a8580 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
@@ -439,6 +439,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 315,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_dependents",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 315
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_health.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_health.all.hurl
index 483e9418..d97a6fb4 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_health.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_health.all.hurl
@@ -306,6 +306,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 415,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_health",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 415
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_locations.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_locations.all.hurl
index 2622c55c..684e6ad2 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_locations.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_locations.all.hurl
@@ -311,6 +311,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 515,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_locations",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 515
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_metadata.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_metadata.all.hurl
index fc33e18d..b3f9bc82 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_metadata.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_metadata.all.hurl
@@ -393,6 +393,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 815,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_metadata",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 815
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_positions.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_positions.all.hurl
index 51ff8bf8..a87b7c3e 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_positions.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_positions.all.hurl
@@ -309,6 +309,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 615,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_positions",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 615
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_stats.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_stats.all.hurl
index 37a5d8e5..9cd46aba 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_stats.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-get_schema_stats.all.hurl
@@ -305,6 +305,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 715,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_stats",
+ "arguments": { "schema": "{{base}}/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 715
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/mcp-2025-11-25-trace_schema_evaluation.all.hurl b/enterprise/e2e/html/hurl/mcp-2025-11-25-trace_schema_evaluation.all.hurl
index ec64b29b..fba043d5 100644
--- a/enterprise/e2e/html/hurl/mcp-2025-11-25-trace_schema_evaluation.all.hurl
+++ b/enterprise/e2e/html/hurl/mcp-2025-11-25-trace_schema_evaluation.all.hurl
@@ -381,6 +381,43 @@ HTTP 200
[Asserts]
jsonpath "$.valid" == true
+# Schema URI with a fragment is rejected
+POST {{base}}/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 1014,
+ "method": "tools/call",
+ "params": {
+ "name": "trace_schema_evaluation",
+ "arguments": { "schema": "{{base}}/test/object#meta", "stringifiedInstance": "\"hi\"" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "<([^>]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 1014
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
+
POST {{base}}/self/v1/mcp
MCP-Protocol-Version: 2025-11-25
Content-Type: application/json
diff --git a/enterprise/e2e/html/hurl/schemas-fragment.all.hurl b/enterprise/e2e/html/hurl/schemas-fragment.all.hurl
new file mode 100644
index 00000000..9335e23f
--- /dev/null
+++ b/enterprise/e2e/html/hurl/schemas-fragment.all.hurl
@@ -0,0 +1,125 @@
+# The REST endpoints reject schema paths that embed a URI fragment.
+
+GET {{base}}/self/v1/api/schemas/metadata/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/locations/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/positions/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/stats/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/health/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/dependencies/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/self/v1/api/schemas/dependents/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+POST {{base}}/self/v1/api/schemas/evaluate/test/object%23meta
+Content-Type: application/json
+```
+"hi"
+```
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+POST {{base}}/self/v1/api/schemas/trace/test/object%23meta
+Content-Type: application/json
+```
+"hi"
+```
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+GET {{base}}/test/object%23meta
+HTTP 400
+Content-Type: application/problem+json
+Access-Control-Allow-Origin: *
+Link: ; rel="describedby"
+[Asserts]
+jsonpath "$.title" == "sourcemeta:one/invalid-uri"
+jsonpath "$.status" == 400
+jsonpath "$.detail" == "The schema URI must not contain a fragment"
+
+# A CORS preflight to a fragment-bearing path still returns the full set of
+# preflight headers, so the browser does not see this as a CORS failure.
+OPTIONS {{base}}/self/v1/api/schemas/evaluate/test/object%23meta
+HTTP 204
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: POST, OPTIONS
+Access-Control-Allow-Headers: Content-Type
+Access-Control-Max-Age: 3600
+
+OPTIONS {{base}}/self/v1/api/schemas/trace/test/object%23meta
+HTTP 204
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: POST, OPTIONS
+Access-Control-Allow-Headers: Content-Type
+Access-Control-Max-Age: 3600
diff --git a/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl b/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
index 328b9290..0f16fa88 100644
--- a/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
+++ b/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependencies.all.hurl
@@ -303,3 +303,40 @@ POST {{base}}/v1/catalog/self/v1/api/schemas/evaluate{{schema_path}}
HTTP 200
[Asserts]
jsonpath "$.valid" == true
+
+# Schema URI with a fragment is rejected
+POST {{base}}/v1/catalog/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 1500,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_dependencies",
+ "arguments": { "schema": "{{base}}/v1/catalog/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 1500
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/v1/catalog/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
diff --git a/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl b/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
index 2423d202..2b01870f 100644
--- a/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
+++ b/enterprise/e2e/path/hurl/mcp-2025-11-25-get_schema_dependents.all.hurl
@@ -173,3 +173,40 @@ POST {{base}}/v1/catalog/self/v1/api/schemas/evaluate{{schema_path}}
HTTP 200
[Asserts]
jsonpath "$.valid" == true
+
+# Schema URI with a fragment is rejected
+POST {{base}}/v1/catalog/self/v1/mcp
+MCP-Protocol-Version: 2025-11-25
+Content-Type: application/json
+```
+{
+ "jsonrpc": "2.0",
+ "id": 1600,
+ "method": "tools/call",
+ "params": {
+ "name": "get_schema_dependents",
+ "arguments": { "schema": "{{base}}/v1/catalog/test/object#meta" }
+ }
+}
+```
+HTTP 200
+Content-Type: application/json
+Access-Control-Allow-Origin: {{base}}
+Link: ; rel="describedby"
+[Captures]
+last_response: body
+schema_path: header "Link" regex "]+)>"
+[Asserts]
+jsonpath "$.jsonrpc" == "2.0"
+jsonpath "$.id" == 1600
+jsonpath "$.error.code" == -32602
+jsonpath "$.error.message" == "Invalid params"
+jsonpath "$.result" not exists
+
+POST {{base}}/v1/catalog/self/v1/api/schemas/evaluate{{schema_path}}
+```
+{{last_response}}
+```
+HTTP 200
+[Asserts]
+jsonpath "$.valid" == true
diff --git a/src/actions/action_dependency_tree_v1.h b/src/actions/action_dependency_tree_v1.h
index 21f407d8..06a079ea 100644
--- a/src/actions/action_dependency_tree_v1.h
+++ b/src/actions/action_dependency_tree_v1.h
@@ -64,6 +64,14 @@ class ActionDependencyTree_v1 : public sourcemeta::one::RouterAction {
return;
}
+ if (matches.front().find('#') != std::string_view::npos ||
+ matches.front().find("%23") != std::string_view::npos) {
+ sourcemeta::one::json_error(
+ request, response, sourcemeta::one::STATUS_BAD_REQUEST, "invalid-uri",
+ "The schema URI must not contain a fragment", this->error_schema_);
+ return;
+ }
+
const auto schemas_root{this->base() / "schemas"};
auto absolute_path{schemas_root / matches.front() / "%"};
absolute_path /= this->metapack_;
@@ -89,7 +97,8 @@ class ActionDependencyTree_v1 : public sourcemeta::one::RouterAction {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
- if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string())) {
+ if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string()) ||
+ arguments.at("schema").to_string().find('#') != std::string::npos) {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
diff --git a/src/actions/action_jsonschema_evaluate_v1.h b/src/actions/action_jsonschema_evaluate_v1.h
index 2ba4ce8a..cb1783b4 100644
--- a/src/actions/action_jsonschema_evaluate_v1.h
+++ b/src/actions/action_jsonschema_evaluate_v1.h
@@ -76,7 +76,8 @@ class ActionJSONSchemaEvaluate_v1 : public sourcemeta::one::RouterAction {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
- if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string())) {
+ if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string()) ||
+ arguments.at("schema").to_string().find('#') != std::string::npos) {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
@@ -136,6 +137,14 @@ class ActionJSONSchemaEvaluate_v1 : public sourcemeta::one::RouterAction {
return;
}
+ if (path.find('#') != std::string_view::npos ||
+ path.find("%23") != std::string_view::npos) {
+ sourcemeta::one::json_error(
+ request, response, sourcemeta::one::STATUS_BAD_REQUEST, "invalid-uri",
+ "The schema URI must not contain a fragment", error_schema);
+ return;
+ }
+
if (request.method() != "post") {
sourcemeta::one::json_error(
request, response, sourcemeta::one::STATUS_METHOD_NOT_ALLOWED,
diff --git a/src/actions/action_jsonschema_serve_v1.h b/src/actions/action_jsonschema_serve_v1.h
index a7f891ec..44096d21 100644
--- a/src/actions/action_jsonschema_serve_v1.h
+++ b/src/actions/action_jsonschema_serve_v1.h
@@ -39,6 +39,14 @@ class ActionJSONSchemaServe_v1 : public sourcemeta::one::RouterAction {
sourcemeta::one::HTTPRequest &request,
sourcemeta::one::HTTPResponse &response,
std::string_view error_schema) -> void {
+ if (schema_path.find('#') != std::string_view::npos ||
+ schema_path.find("%23") != std::string_view::npos) {
+ sourcemeta::one::json_error(
+ request, response, sourcemeta::one::STATUS_BAD_REQUEST, "invalid-uri",
+ "The schema URI must not contain a fragment", error_schema);
+ return;
+ }
+
// Because Visual Studio Code famously does not support `$id` or `id`
// See
// https://github.com/microsoft/vscode-json-languageservice/issues/224
diff --git a/src/actions/action_jsonschema_trace_v1.h b/src/actions/action_jsonschema_trace_v1.h
index 2eb0cbc2..ced9cf9f 100644
--- a/src/actions/action_jsonschema_trace_v1.h
+++ b/src/actions/action_jsonschema_trace_v1.h
@@ -78,7 +78,8 @@ class ActionJSONSchemaTrace_v1 : public sourcemeta::one::RouterAction {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
- if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string())) {
+ if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string()) ||
+ arguments.at("schema").to_string().find('#') != std::string::npos) {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
diff --git a/src/actions/action_serve_explorer_artifact_v1.h b/src/actions/action_serve_explorer_artifact_v1.h
index d09e8e75..548fa4a5 100644
--- a/src/actions/action_serve_explorer_artifact_v1.h
+++ b/src/actions/action_serve_explorer_artifact_v1.h
@@ -51,6 +51,15 @@ class ActionServeExplorerArtifact_v1 : public sourcemeta::one::RouterAction {
auto rest(const std::span matches,
sourcemeta::one::HTTPRequest &request,
sourcemeta::one::HTTPResponse &response) -> void override {
+ if (!matches.empty() &&
+ (matches.front().find('#') != std::string_view::npos ||
+ matches.front().find("%23") != std::string_view::npos)) {
+ sourcemeta::one::json_error(
+ request, response, sourcemeta::one::STATUS_BAD_REQUEST, "invalid-uri",
+ "The schema URI must not contain a fragment", this->error_schema_);
+ return;
+ }
+
auto absolute_path{this->base() / "explorer"};
if (!matches.empty() && !matches.front().empty()) {
absolute_path /= matches.front();
@@ -70,7 +79,8 @@ class ActionServeExplorerArtifact_v1 : public sourcemeta::one::RouterAction {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
- if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string())) {
+ if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string()) ||
+ arguments.at("schema").to_string().find('#') != std::string::npos) {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
diff --git a/src/actions/action_serve_schema_artifact_v1.h b/src/actions/action_serve_schema_artifact_v1.h
index 1ad808f2..642133d8 100644
--- a/src/actions/action_serve_schema_artifact_v1.h
+++ b/src/actions/action_serve_schema_artifact_v1.h
@@ -60,6 +60,14 @@ class ActionServeSchemaArtifact_v1 : public sourcemeta::one::RouterAction {
return;
}
+ if (matches.front().find('#') != std::string_view::npos ||
+ matches.front().find("%23") != std::string_view::npos) {
+ sourcemeta::one::json_error(
+ request, response, sourcemeta::one::STATUS_BAD_REQUEST, "invalid-uri",
+ "The schema URI must not contain a fragment", this->error_schema_);
+ return;
+ }
+
auto absolute_path{this->base() / "schemas" / matches.front() / "%"};
absolute_path /= std::string{this->artifact_} + ".metapack";
ActionServeMetapackFile_v1::serve(absolute_path, sourcemeta::one::STATUS_OK,
@@ -75,7 +83,8 @@ class ActionServeSchemaArtifact_v1 : public sourcemeta::one::RouterAction {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}
- if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string())) {
+ if (!sourcemeta::core::URI::is_uri(arguments.at("schema").to_string()) ||
+ arguments.at("schema").to_string().find('#') != std::string::npos) {
return sourcemeta::core::jsonrpc_make_error_invalid_params(request_id);
}