From f61e7d0613c6df6225e050ea5dbfb92fbbf0ee80 Mon Sep 17 00:00:00 2001 From: Mihaela Mihaljevic Date: Wed, 17 Jun 2026 18:06:08 +0200 Subject: [PATCH] fix(schema): reject an empty-string namespace value (#145) A namespace name must be a non-empty anyURI, so and are invalid. Omitting the attribute (no target namespace, or a no-namespace import) stays valid. XSTS invalid-schemas-accepted 134 to 132, no other bucket moved. --- CHANGELOG.md | 2 ++ Sources/Schema/SchemaStructure.swift | 1 + Sources/Schema/SchemaStructureHelpers.swift | 15 +++++++++++++++ Tests/XSTSSuiteTests.swift | 5 ++++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04ce157..c7e7b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ Second pre-release. Continues the W3C XSTS schema-validity campaign: **invalid-s ### Fixed +- XSTS invalid-schemas-accepted (#145): a namespace value may not be the empty string. `` and `` are rejected, since a namespace name must be a non-empty `anyURI` (omitting the attribute, for no target namespace or a no-namespace import, stays valid). Caught two invalid schemas: invalid-schemas-accepted 134 → 132, no other bucket moved. + - XSTS invalid-schemas-accepted (#145): a wildcard restriction may not weaken `processContents`. When a complex type restricts another, a restricting `xs:any` must have a `processContents` at least as strong as the base wildcard's (`strict` > `lax` > `skip`; Wildcard Subset, §3.10.6), in addition to narrowing the namespace. Caught three invalid schemas: invalid-schemas-accepted 137 → 134, no other bucket moved. - XSTS invalid-schemas-accepted (#145): a `simpleContent` restriction's base must be a complex type. Restricting a built-in (`xs:integer`, `xs:anySimpleType`) or a user `simpleType` through `` is rejected (`src-ct.2`); a complex type with simple content is built by extending a simple type, not by restricting one. Caught five invalid schemas: invalid-schemas-accepted 142 → 137, no other bucket moved. diff --git a/Sources/Schema/SchemaStructure.swift b/Sources/Schema/SchemaStructure.swift index 284df21..8852be4 100644 --- a/Sources/Schema/SchemaStructure.swift +++ b/Sources/Schema/SchemaStructure.swift @@ -66,6 +66,7 @@ extension PureXML.Schema.XSDParser { append(topLevelDeclarationErrors(schema), at: schema, into: &findings) append(nestedNamedDefinitionErrors(schema), at: schema, into: &findings) append(anySimpleTypeRestrictionErrors(schema), at: schema, into: &findings) + append(emptyNamespaceErrors(schema), at: schema, into: &findings) return findings } diff --git a/Sources/Schema/SchemaStructureHelpers.swift b/Sources/Schema/SchemaStructureHelpers.swift index d1e32c1..2745276 100644 --- a/Sources/Schema/SchemaStructureHelpers.swift +++ b/Sources/Schema/SchemaStructureHelpers.swift @@ -224,4 +224,19 @@ extension PureXML.Schema.XSDParser { } return [] } + + /// A namespace value must be a non-empty `anyURI` (or absent): the empty string + /// is not a legal namespace name. So `` and + /// `` are invalid, while omitting the attribute (no target + /// namespace, a no-namespace import) stays valid. + static func emptyNamespaceErrors(_ schema: XSDTree) -> [String] { + var errors: [String] = [] + if PureXML.Schema.XSDNode.attribute(schema, "targetNamespace") == "" { + errors.append("the 'targetNamespace' attribute may not be the empty string") + } + for importNode in descendants(schema, named: "import") where PureXML.Schema.XSDNode.attribute(importNode, "namespace") == "" { + errors.append("an 'import' namespace may not be the empty string") + } + return errors + } } diff --git a/Tests/XSTSSuiteTests.swift b/Tests/XSTSSuiteTests.swift index ef7e4a0..fa32e8f 100644 --- a/Tests/XSTSSuiteTests.swift +++ b/Tests/XSTSSuiteTests.swift @@ -108,8 +108,11 @@ struct XSTSSuiteTests { /// Requiring a restricting wildcard's `processContents` to be at least as strong /// as the base's (strict > lax > skip; Wildcard Subset §3.10.6) then caught three /// more invalid schemas: invalid-schemas accepted 137 → 134, no other bucket moved. + /// Rejecting an empty-string namespace value (`targetNamespace=""`, `import + /// namespace=""`) then caught two more invalid schemas: invalid-schemas accepted + /// 134 → 132, no other bucket moved. private let knownSchemaValidRejected = 1 - private let knownSchemaInvalidAccepted = 134 + private let knownSchemaInvalidAccepted = 132 private let knownInstanceValidRejected = 3 private let knownInstanceInvalidAccepted = 132