PureXML/Sources/Parsing/DocumentType.swift
Mihaela Mihaljevic c75c97aa9b feat(dtd): external and parameter entities, secure by default
Add internal parameter entities (parsed, stored, and expanded within the
DTD, including bare %name; references that inject markup declarations,
bounded by depth and the expansion budget). Record external general
entities and the external subset identifier, and load them only through an
injected EntityResolver (a struct of closures). The default refusing
resolver fetches nothing, so XXE stays closed: an external entity fails as
undefined and the external subset is left unread. Internal declarations win
over the external subset.

Closes #29
2026-06-08 03:08:20 +02:00

40 lines
2.0 KiB
Swift

public extension PureXML.Parsing {
/// What the parser extracted from a `<!DOCTYPE>`: the internal general
/// entities, the `<!ELEMENT>` content-model strings, the `<!ATTLIST>` bodies,
/// the parameter entities, any external entity declarations, and the external
/// subset identifier. Empty when no DTD was processed.
struct DocumentType: Equatable, Sendable {
public var entities: [String: String]
public var elementModels: [String: String]
/// Raw `<!ATTLIST>` bodies keyed by element name (the text between the
/// element name and `>`, concatenated across multiple declarations).
public var attributeLists: [String: String]
/// Internal parameter entities (`<!ENTITY % name "value">`) keyed by name,
/// with their values already parameter-expanded. DTD-only; never visible
/// in document content.
public var parameterEntities: [String: String]
/// External general entity declarations keyed by name. Their replacement
/// text is loaded only through an injected ``EntityResolver``.
public var externalEntities: [String: ExternalID]
/// The external subset identifier from `<!DOCTYPE root SYSTEM/PUBLIC ...>`,
/// or nil when the DOCTYPE has no external subset.
public var externalSubset: ExternalID?
public init(
entities: [String: String] = [:],
elementModels: [String: String] = [:],
attributeLists: [String: String] = [:],
parameterEntities: [String: String] = [:],
externalEntities: [String: ExternalID] = [:],
externalSubset: ExternalID? = nil,
) {
self.entities = entities
self.elementModels = elementModels
self.attributeLists = attributeLists
self.parameterEntities = parameterEntities
self.externalEntities = externalEntities
self.externalSubset = externalSubset
}
}
}