PureXML/Sources/Schema/XSDParserTypeReferences.swift
Mihaela Mihaljevic 94c6950ae3 fix(schema): process xsd:anyType wildcards as lax; invalid-instances-accepted 101 to 95 (#146, #147)
The ur-type xsd:anyType modeled its element and attribute wildcards with
processContents="skip", so a child element or attribute of an untyped element
(or an element typed xs:anyType) was never validated. XSD 1.0 §3.4.7 specifies
those wildcards as "lax": content that has a global declaration is validated
against it, while undeclared content is admitted. This is why an invalid
declared value reached through a wildcard (e.g. a duration-typed attribute with
value "P" matched by <xsd:anyAttribute/>) was accepted.

Set both wildcards to .lax at all three ur-type construction sites and loosen
isUrType to recognise the lax (or skip) shape.

The lax change required making the wildcard lookup of a global element
declaration namespace-exact. A global element is always in the schema target
namespace, so an unqualified instance element must match only a no-namespace
global, which the qualified key element:{}local already covers. The previous
bare-local-name fallback conflated namespaces and would have validated an
unqualified child against a same-named namespaced declaration; it is removed in
the document path and both streaming sites.

XSTS invalid-instances-accepted 101 to 95, no false positive (valid-instances-
rejected and valid-schemas-rejected held at 0; the spec's own errC007 anyType-
lax case validates correctly), no other bucket moved. An adversarial review
re-ran the full suite and found no new over-rejection.
2026-06-18 10:44:33 +02:00

60 lines
2.8 KiB
Swift

private typealias XSDNode = PureXML.Schema.XSDNode
private typealias XSDContext = PureXML.Schema.XSDContext
private typealias Particle = PureXML.Schema.Particle
private typealias ComplexType = PureXML.Schema.ComplexType
private typealias ElementType = PureXML.Schema.ElementType
private typealias XSDSimpleParser = PureXML.Schema.XSDSimpleParser
private typealias BuiltinType = PureXML.Schema.BuiltinType
private typealias Wildcard = PureXML.Schema.Wildcard
private typealias SimpleType = PureXML.Schema.SimpleType
extension PureXML.Schema.XSDParser {
// MARK: Element and type references
static func elementType(_ node: PureXML.Model.TreeNode, _ context: PureXML.Schema.XSDContext) -> PureXML.Schema.ElementType {
if let typeName = XSDNode.attribute(node, "type") {
return typeReference(typeName, context, at: node)
}
if let inline = XSDNode.firstChild(node, named: "simpleType") {
return .simple(XSDSimpleParser.simpleType(inline, context))
}
if let inline = XSDNode.firstChild(node, named: "complexType") {
return .complex(complexType(inline, context))
}
return .complex(anyType)
}
private static func typeReference(_ typeName: String, _ context: XSDContext, at node: XSDTree? = nil) -> ElementType {
let bindings = node.map {
PureXML.Schema.XSDParser.namespaceBindingsInScope(of: $0, defaultBindings: context.namespaceBindings)
} ?? context.namespaceBindings
let local = XSDNode.stripPrefix(typeName)
let uri = XSDNode.schemaComponentNamespace(typeName, bindings, targetNamespace: context.targetNamespace)
if uri == PureXML.Schema.XSDParser.xsdNamespace {
if let builtin = BuiltinType(rawValue: local) {
return .simple(SimpleType(base: builtin))
}
if local == "anySimpleType" {
return .simple(SimpleType(base: .string, isAnySimpleType: true))
}
if local == "anyType" {
return .complex(anyType)
}
if let item = XSDSimpleParser.listBuiltinItem(local) {
return .simple(.list(item: SimpleType(base: item), isBuiltinList: true))
}
}
return .typeReference(typeDeclarationKey(local, namespaceURI: uri))
}
/// The ur-type `xsd:anyType`. Per XSD 1.0 §3.4.7 its element and attribute
/// wildcards are `lax`, not skip: a child or attribute that has a global
/// declaration is validated against it, while undeclared content is admitted.
private static var anyType: ComplexType {
ComplexType(
attributeWildcard: Wildcard(processContents: .lax),
content: .mixed(Particle(minOccurs: 0, maxOccurs: nil, term: .wildcard(Wildcard(processContents: .lax)))),
)
}
}