expose million laughs attack protection implemented by Expat 2.4

This commit is contained in:
Günter Obiltschnig
2021-05-23 21:23:28 +02:00
parent d95d9bd4a4
commit cd6422fde3
4 changed files with 157 additions and 67 deletions

View File

@@ -28,7 +28,7 @@ namespace XML {
class XML_API SAXParser: public XMLReader
/// This class provides a SAX2 (Simple API for XML) interface to expat,
/// This class provides a SAX2 (Simple API for XML) interface to expat,
/// the XML parser toolkit.
/// The following SAX2 features and properties are supported:
/// * http://xml.org/sax/features/external-general-entities
@@ -41,6 +41,14 @@ class XML_API SAXParser: public XMLReader
/// The following proprietary extensions are supported:
/// * http://www.appinf.com/features/enable-partial-reads --
/// see ParserEngine::setEnablePartialReads()
/// * http://www.appinf.com/properties/bla-maximum-amplification
/// see ParserEngine::setBillionLaughsAttackProtectionMaximumAmplification();
/// argument must be a float >= 1.0 formatted as string;
/// property is set-only.
/// * http://www.appinf.com/properties/bla-activation-threshold
/// see ParserEngine::setBillionLaughsAttackProtectionActivationThreshold();
/// argument must be a 64-bit unsigned integer formatted as string;
/// property is set-only.
{
public:
SAXParser();
@@ -48,14 +56,14 @@ public:
SAXParser(const XMLString& encoding);
/// Creates an SAXParser with the given encoding.
~SAXParser();
/// Destroys the SAXParser.
void setEncoding(const XMLString& encoding);
/// Sets the encoding used by the parser if no
/// encoding is specified in the XML document.
const XMLString& getEncoding() const;
/// Returns the name of the encoding used by
/// the parser if no encoding is specified in
@@ -81,11 +89,13 @@ public:
void parse(InputSource* pSource);
void parse(const XMLString& systemId);
void parseMemoryNP(const char* xml, std::size_t size);
/// Extensions
void parseString(const std::string& xml);
static const XMLString FEATURE_PARTIAL_READS;
static const XMLString PROPERTY_BLA_MAXIMUM_AMPLIFICATION;
static const XMLString PROPERTY_BLA_ACTIVATION_THRESHOLD;
protected:
void setupParse();

View File

@@ -47,28 +47,28 @@ class ContextLocator;
class XML_API ParserEngine: public Locator
/// This class provides an object-oriented, stream-based,
/// This class provides an object-oriented, stream-based,
/// low-level interface to the XML Parser Toolkit (expat).
/// It is strongly recommended, that you use the
/// SAX parser classes (which are based on this
/// class) instead of this class, since they provide
/// class) instead of this class, since they provide
/// a standardized, higher-level interface to the parser.
{
public:
ParserEngine();
/// Creates the parser engine.
ParserEngine(const XMLString& encoding);
/// Creates the parser engine and passes the encoding
/// to the underlying parser.
~ParserEngine();
/// Destroys the parser.
void setEncoding(const XMLString& encoding);
/// Sets the encoding used by expat. The encoding must be
/// set before parsing begins, otherwise it will be ignored.
const XMLString& getEncoding() const;
/// Returns the encoding used by expat.
@@ -80,33 +80,33 @@ public:
/// The parser takes ownership of the strategy object
/// and deletes it when it's no longer needed.
/// The default is NoNamespacesStrategy.
NamespaceStrategy* getNamespaceStrategy() const;
/// Returns the NamespaceStrategy currently in use.
void setExpandInternalEntities(bool flag = true);
/// Enables/disables expansion of internal entities (enabled by
/// default). If entity expansion is disabled, internal entities
/// default). If entity expansion is disabled, internal entities
/// are reported via the default handler.
/// Must be set before parsing begins, otherwise it will be
/// ignored.
bool getExpandInternalEntities() const;
/// Returns true if internal entities will be expanded automatically,
/// which is the default.
void setExternalGeneralEntities(bool flag = true);
/// Enable or disable processing of external general entities.
bool getExternalGeneralEntities() const;
/// Returns true if external general entities will be processed; false otherwise.
void setExternalParameterEntities(bool flag = true);
/// Enable or disable processing of external parameter entities.
bool getExternalParameterEntities() const;
/// Returns true if external parameter entities will be processed; false otherwise.
void setEntityResolver(EntityResolver* pResolver);
/// Allow an application to register an entity resolver.
@@ -121,7 +121,7 @@ public:
void setDeclHandler(DeclHandler* pDeclHandler);
/// Allow an application to register a DTD declarations event handler.
DeclHandler* getDeclHandler() const;
/// Return the current DTD declarations handler.
@@ -133,7 +133,7 @@ public:
void setLexicalHandler(LexicalHandler* pLexicalHandler);
/// Allow an application to register a lexical event handler.
LexicalHandler* getLexicalHandler() const;
/// Return the current lexical handler.
@@ -142,12 +142,12 @@ public:
ErrorHandler* getErrorHandler() const;
/// Return the current error handler.
void setEnablePartialReads(bool flag = true);
/// Enable or disable partial reads from the input source.
///
/// This is useful for parsing XML from a socket stream for
/// a protocol like XMPP, where basically single elements
/// a protocol like XMPP, where basically single elements
/// are read one at a time from the input source's stream, and
/// following elements depend upon responses sent back to
/// the peer.
@@ -158,21 +158,44 @@ public:
/// This allows for efficient parsing of "complete" XML documents,
/// but fails in a case such as XMPP, where only XML fragments
/// are sent at a time.
bool getEnablePartialReads() const;
/// Returns true if partial reads are enabled (see
/// setEnablePartialReads()), false otherwise.
void setBillionLaughsAttackProtectionMaximumAmplification(float maximumAmplificationFactor);
/// Sets the maximum tolerated amplification factor
/// for protection against Billion Laughs Attacks.
///
/// The amplification factor is calculated as:
/// amplification := (direct + indirect) / direct
/// while parsing, whereas:
/// - direct is the number of bytes read from the primary document in parsing and
/// - indirect is the number of bytes added by expanding entities and reading of
/// external DTD files, combined.
///
/// maximumAmplificationFactor must be non-NaN and greater than or equal to 1.0.
///
/// Requires an underlying Expat version >= 2.4.0.
void setBillionLaughsAttackProtectionActivationThreshold(Poco::UInt64 activationThresholdBytes);
/// Sets number of output bytes (including amplification from entity expansion and reading DTD files)
/// needed to activate protection against Billion Laughs Attacks.
///
/// Defaults to 8 MiB.
///
/// Requires an underlying Expat version >= 2.4.0.
void parse(InputSource* pInputSource);
/// Parse an XML document from the given InputSource.
void parse(const char* pBuffer, std::size_t size);
/// Parses an XML document from the given buffer.
// Locator
XMLString getPublicId() const;
/// Return the public identifier for the current document event.
XMLString getSystemId() const;
/// Return the system identifier for the current document event.
@@ -180,7 +203,7 @@ public:
/// Return the line number where the current document event ends.
int getColumnNumber() const;
/// Return the column number where the current document event ends.
/// Return the column number where the current document event ends.
protected:
void init();
@@ -191,7 +214,7 @@ protected:
void parseCharInputStream(XMLCharInputStream& istr);
/// Parses an entity from the given stream.
std::streamsize readBytes(XMLByteInputStream& istr, char* pBuffer, std::streamsize bufferSize);
/// Reads at most bufferSize bytes from the given stream into the given buffer.
@@ -213,10 +236,10 @@ protected:
void pushContext(XML_Parser parser, InputSource* pInputSource);
/// Pushes a new entry to the context stack.
void popContext();
/// Pops the top-most entry from the context stack.
void resetContext();
/// Resets and clears the context stack.
@@ -240,7 +263,7 @@ protected:
static void handleEndNamespaceDecl(void* userData, const XML_Char* prefix);
static void handleStartDoctypeDecl(void* userData, const XML_Char* doctypeName, const XML_Char *systemId, const XML_Char* publicId, int hasInternalSubset);
static void handleEndDoctypeDecl(void* userData);
static void handleEntityDecl(void *userData, const XML_Char *entityName, int isParamEntity, const XML_Char *value, int valueLength,
static void handleEntityDecl(void *userData, const XML_Char *entityName, int isParamEntity, const XML_Char *value, int valueLength,
const XML_Char *base, const XML_Char *systemId, const XML_Char *publicId, const XML_Char *notationName);
static void handleExternalParsedEntityDecl(void* userData, const XML_Char* entityName, const XML_Char* base, const XML_Char* systemId, const XML_Char* publicId);
static void handleInternalParsedEntityDecl(void* userData, const XML_Char* entityName, const XML_Char* replacementText, int replacementTextLength);
@@ -248,14 +271,14 @@ protected:
// encoding support
static int convert(void *data, const char *s);
private:
typedef std::map<XMLString, Poco::TextEncoding*> EncodingMap;
typedef std::vector<ContextLocator*> ContextStack;
XML_Parser _parser;
char* _pBuffer;
bool _encodingSpecified;
bool _encodingSpecified;
XMLString _encoding;
bool _expandInternalEntities;
bool _externalGeneralEntities;
@@ -264,14 +287,17 @@ private:
NamespaceStrategy* _pNamespaceStrategy;
EncodingMap _encodings;
ContextStack _context;
EntityResolver* _pEntityResolver;
DTDHandler* _pDTDHandler;
DeclHandler* _pDeclHandler;
ContentHandler* _pContentHandler;
LexicalHandler* _pLexicalHandler;
ErrorHandler* _pErrorHandler;
float _maximumAmplificationFactor;
Poco::UInt64 _activationThresholdBytes;
static const int PARSE_BUFFER_SIZE;
static const XMLString EMPTY_STRING;
};