Deepen DTD processing in the doctype scanner: - Conditional sections are processed, not skipped: INCLUDE sections have their declarations scanned, IGNORE sections are discarded, the keyword may be a parameter-entity reference, and nested sections are handled. - <!NOTATION> declarations are parsed and stored on DocumentType. - Unparsed entities (<!ENTITY ... NDATA n>) are parsed and stored with their notation, distinct from general entities. - External parameter entities are loaded through the injected resolver so their declarations expand; the default refusing resolver loads nothing, keeping XXE closed. - NOTATION attributes are a distinct type, validated against the declared notations (value must be listed and the notation declared). Tracking unparsed entities also lets ENTITY/ENTITIES attribute values be validated against the declared NDATA entities, closing the gap left when #73 could only check name syntax. Closes #72
37 lines
1.6 KiB
Swift
37 lines
1.6 KiB
Swift
public extension PureXML.Validation {
|
|
/// A DTD schema: the element content models declared by `<!ELEMENT>` and the
|
|
/// attribute declarations from `<!ATTLIST>`, against which a parsed tree can
|
|
/// be validated. Built from the internal subset surfaced by the parser.
|
|
struct DTDSchema: Sendable {
|
|
let models: [String: ContentModel]
|
|
let attributes: [String: [AttributeDeclaration]]
|
|
/// The names declared by `<!NOTATION>`, against which a `NOTATION` attribute
|
|
/// value (and the names listed in its declaration) are checked.
|
|
let notations: Set<String>
|
|
/// The names of declared unparsed (`NDATA`) entities, against which an
|
|
/// `ENTITY`/`ENTITIES` attribute value is checked.
|
|
let unparsedEntities: Set<String>
|
|
|
|
init(_ documentType: PureXML.Parsing.DocumentType) {
|
|
notations = Set(documentType.notations.keys)
|
|
unparsedEntities = Set(documentType.unparsedEntities.keys)
|
|
var parsedModels: [String: ContentModel] = [:]
|
|
for (name, model) in documentType.elementModels {
|
|
parsedModels[name] = ContentModelParser.parse(model)
|
|
}
|
|
models = parsedModels
|
|
|
|
var parsedAttributes: [String: [AttributeDeclaration]] = [:]
|
|
for (name, body) in documentType.attributeLists {
|
|
parsedAttributes[name] = AttributeListParser.parse(body)
|
|
}
|
|
attributes = parsedAttributes
|
|
}
|
|
|
|
/// Whether the schema declares any elements or attributes.
|
|
public var isEmpty: Bool {
|
|
models.isEmpty && attributes.isEmpty
|
|
}
|
|
}
|
|
}
|