An xs:all group in a complexContent extension of a base that already has element content was accepted. The effective content model of such an extension is sequence(base content, extension content), which nests the all and violates the rule that an all group must be the whole content model. The base type is read from the compiled model: rejected only when the base resolves, in the schema's own target namespace, to a complex type with element-only or mixed content. An explicitly-empty base (an empty model group with mixed=false) has effectively empty content and may still be extended by an all (the all becomes the whole content); a simple-content, empty, foreign, or unresolved base is left alone. Checked for a self-contained schema. XSTS: invalid schemas accepted 411 to 403, no other bucket raised.
77 lines
4.3 KiB
Swift
77 lines
4.3 KiB
Swift
extension PureXML.Schema.XSDParser {
|
|
/// Schema-validity findings for the `id` attributes in a schema document. The
|
|
/// `id` attribute on any XSD component is of type `xs:ID`, so each value must
|
|
/// be a valid NCName and all values must be unique within the document
|
|
/// (XSD Structures: `xs:ID` and the ID/IDREF constraints). The values were
|
|
/// never checked, so a malformed (`id=""`, `id="123"`) or duplicated `id` left
|
|
/// the schema wrongly accepted.
|
|
///
|
|
/// Walks the one document rooted at `schema`; an included or imported document
|
|
/// keeps its own `id` scope, so cross-document collisions are not flagged here.
|
|
static func idAttributeErrors(_ schema: XSDTree) -> [String] {
|
|
var errors: [String] = []
|
|
var seen: Set<String> = []
|
|
collectIDErrors(schema, into: &errors, seen: &seen)
|
|
return errors
|
|
}
|
|
|
|
private static func collectIDErrors(_ node: XSDTree, into errors: inout [String], seen: inout Set<String>) {
|
|
// `appinfo` and `documentation` hold arbitrary foreign content whose own
|
|
// `id` attributes are not xs:ID; do not descend into them.
|
|
let local = PureXML.Schema.XSDNode.localName(node)
|
|
if local == "appinfo" || local == "documentation" { return }
|
|
if let id = unprefixedID(node) {
|
|
if !PureXML.Schema.Lexical.isNCName(id) {
|
|
errors.append("id attribute value '\(id)' is not a valid NCName")
|
|
} else if !seen.insert(id).inserted {
|
|
errors.append("duplicate id attribute value '\(id)' in the schema document")
|
|
}
|
|
}
|
|
for child in PureXML.Schema.XSDNode.elementChildren(node) {
|
|
collectIDErrors(child, into: &errors, seen: &seen)
|
|
}
|
|
}
|
|
|
|
/// The value of the unprefixed, no-namespace `id` attribute (the xs:ID one),
|
|
/// or nil. A prefixed attribute such as `xml:id` or a foreign `pre:id` is a
|
|
/// different attribute and is not the schema-component identifier.
|
|
private static func unprefixedID(_ node: XSDTree) -> String? {
|
|
node.attributes.first { $0.name.prefix == nil && $0.name.localName == "id" }?.value
|
|
}
|
|
|
|
/// All compile-time schema-consistency findings, collected together so they are
|
|
/// reported in one pass: `id` validity, schema-for-schemas structure, global
|
|
/// component-name uniqueness, content-model determinism (UPA), and circular
|
|
/// type derivation. The explicit return type keeps the type-checker from
|
|
/// inferring a long concatenation.
|
|
static func consistencyErrors(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext, _ containers: [XSDTree]) -> [String] {
|
|
let structural = idAttributeErrors(schema) + structureErrors(schema) + componentNameErrors(schema)
|
|
+ simpleTypeFinalErrors(schema)
|
|
let determinism = PureXML.Schema.ContentModelDeterminism.violations(in: schema, context: context)
|
|
let cycles = derivationCycleErrors(containers, context.namespaceBindings, context.targetNamespace)
|
|
+ circularReferenceErrors(containers, context.namespaceBindings, context.targetNamespace)
|
|
let placement = allGroupReferencePlacementErrors(containers, context.namespaceBindings, context.targetNamespace)
|
|
return structural + determinism + cycles + placement
|
|
}
|
|
|
|
/// Consistency findings that depend on the resolved named types, collected after
|
|
/// `namedTypes` has populated them: unresolved references, attribute-use
|
|
/// uniqueness and single-ID, ID-typed value constraints, substitution-group
|
|
/// member type derivation (`e-props-correct.4`), and value constraints valid
|
|
/// against a user type (`e-props-correct.2` / `a-props-correct.2`).
|
|
static func postNamedTypeErrors(
|
|
_ schema: XSDTree,
|
|
_ context: PureXML.Schema.XSDContext,
|
|
_ containers: [XSDTree],
|
|
_ derivation: DerivationTables,
|
|
typeMaps: (global: [String: PureXML.Schema.ElementType], named: [String: PureXML.Schema.ElementType]),
|
|
) -> [String] {
|
|
referenceErrors(schema, in: context, elements: typeMaps.global)
|
|
+ attributeUseErrors(containers, context)
|
|
+ idValueConstraintErrors(schema, context)
|
|
+ substitutionTypeErrors(schema, derivation, typeMaps.named)
|
|
+ userTypeValueConstraintErrors(schema, context, typeMaps.named)
|
|
+ extensionAllGroupErrors(schema, context, typeMaps.named)
|
|
}
|
|
}
|