# HG changeset patch
# Node ID 33398db3f060
# Parent  13a246aa0656
diff --git a/libpurple/tests/Makefile.am b/libpurple/tests/Makefile.am
--- a/libpurple/tests/Makefile.am
+++ b/libpurple/tests/Makefile.am
@@ -4,7 +4,17 @@
 clean-local:
 	-rm -rf libpurple..
 
+fuzz_programs=\
+	fuzz_xmlnode \
+	fuzz_jabber_id_new \
+	fuzz_jabber_caps \
+	fuzz_html_to_xhtml \
+	fuzz_markup_strip_html \
+	fuzz_mime
+
 check_PROGRAMS=check_libpurple
+check_PROGRAMS+=$(fuzz_programs)
+
 
 check_libpurple_SOURCES=\
         check_libpurple.c \
@@ -33,4 +43,28 @@
         @CHECK_LIBS@ \
 		$(GLIB_LIBS)
 
+fuzz_xmlnode_SOURCES=fuzz_xmlnode.c
+fuzz_xmlnode_LDADD=$(check_libpurple_LDADD)
+fuzz_xmlnode_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
+fuzz_jabber_id_new_SOURCES=fuzz_jabber_id_new.c
+fuzz_jabber_id_new_LDADD=$(check_libpurple_LDADD)
+fuzz_jabber_id_new_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
+fuzz_jabber_caps_SOURCES=fuzz_jabber_caps.c
+fuzz_jabber_caps_LDADD=$(check_libpurple_LDADD)
+fuzz_jabber_caps_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
+fuzz_mime_SOURCES=fuzz_mime.c
+fuzz_mime_LDADD=$(check_libpurple_LDADD)
+fuzz_mime_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
+fuzz_html_to_xhtml_SOURCES=fuzz_html_to_xhtml.c
+fuzz_html_to_xhtml_LDADD=$(check_libpurple_LDADD)
+fuzz_html_to_xhtml_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
+fuzz_markup_strip_html_SOURCES=fuzz_markup_strip_html.c
+fuzz_markup_strip_html_LDADD=$(check_libpurple_LDADD)
+fuzz_markup_strip_html_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS)
+
 endif
diff --git a/libpurple/tests/dictionaries/html.dict b/libpurple/tests/dictionaries/html.dict
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/dictionaries/html.dict
@@ -0,0 +1,478 @@
+#
+# AFL dictionary for HTML parsers
+# -------------------------------
+#
+# A basic collection of HTML string likely to matter to HTML parsers.
+#
+# Created by Michal Zalewski <lcamtuf@google.com>
+#
+
+tag_a="<a>"
+tag_abbr="<abbr>"
+tag_acronym="<acronym>"
+tag_address="<address>"
+tag_annotation_xml="<annotation-xml>"
+tag_applet="<applet>"
+tag_area="<area>"
+tag_article="<article>"
+tag_aside="<aside>"
+tag_audio="<audio>"
+tag_b="<b>"
+tag_base="<base>"
+tag_basefont="<basefont>"
+tag_bdi="<bdi>"
+tag_bdo="<bdo>"
+tag_bgsound="<bgsound>"
+tag_big="<big>"
+tag_blink="<blink>"
+tag_blockquote="<blockquote>"
+tag_body="<body>"
+tag_br="<br>"
+tag_button="<button>"
+tag_canvas="<canvas>"
+tag_caption="<caption>"
+tag_center="<center>"
+tag_cite="<cite>"
+tag_code="<code>"
+tag_col="<col>"
+tag_colgroup="<colgroup>"
+tag_data="<data>"
+tag_datalist="<datalist>"
+tag_dd="<dd>"
+tag_del="<del>"
+tag_desc="<desc>"
+tag_details="<details>"
+tag_dfn="<dfn>"
+tag_dir="<dir>"
+tag_div="<div>"
+tag_dl="<dl>"
+tag_dt="<dt>"
+tag_em="<em>"
+tag_embed="<embed>"
+tag_fieldset="<fieldset>"
+tag_figcaption="<figcaption>"
+tag_figure="<figure>"
+tag_font="<font>"
+tag_footer="<footer>"
+tag_foreignobject="<foreignobject>"
+tag_form="<form>"
+tag_frame="<frame>"
+tag_frameset="<frameset>"
+tag_h1="<h1>"
+tag_h2="<h2>"
+tag_h3="<h3>"
+tag_h4="<h4>"
+tag_h5="<h5>"
+tag_h6="<h6>"
+tag_head="<head>"
+tag_header="<header>"
+tag_hgroup="<hgroup>"
+tag_hr="<hr>"
+tag_html="<html>"
+tag_i="<i>"
+tag_iframe="<iframe>"
+tag_image="<image>"
+tag_img="<img>"
+tag_input="<input>"
+tag_ins="<ins>"
+tag_isindex="<isindex>"
+tag_kbd="<kbd>"
+tag_keygen="<keygen>"
+tag_label="<label>"
+tag_legend="<legend>"
+tag_li="<li>"
+tag_link="<link>"
+tag_listing="<listing>"
+tag_main="<main>"
+tag_malignmark="<malignmark>"
+tag_map="<map>"
+tag_mark="<mark>"
+tag_marquee="<marquee>"
+tag_math="<math>"
+tag_menu="<menu>"
+tag_menuitem="<menuitem>"
+tag_meta="<meta>"
+tag_meter="<meter>"
+tag_mglyph="<mglyph>"
+tag_mi="<mi>"
+tag_mn="<mn>"
+tag_mo="<mo>"
+tag_ms="<ms>"
+tag_mtext="<mtext>"
+tag_multicol="<multicol>"
+tag_nav="<nav>"
+tag_nextid="<nextid>"
+tag_nobr="<nobr>"
+tag_noembed="<noembed>"
+tag_noframes="<noframes>"
+tag_noscript="<noscript>"
+tag_object="<object>"
+tag_ol="<ol>"
+tag_optgroup="<optgroup>"
+tag_option="<option>"
+tag_output="<output>"
+tag_p="<p>"
+tag_param="<param>"
+tag_plaintext="<plaintext>"
+tag_pre="<pre>"
+tag_progress="<progress>"
+tag_q="<q>"
+tag_rb="<rb>"
+tag_rp="<rp>"
+tag_rt="<rt>"
+tag_rtc="<rtc>"
+tag_ruby="<ruby>"
+tag_s="<s>"
+tag_samp="<samp>"
+tag_script="<script>"
+tag_section="<section>"
+tag_select="<select>"
+tag_small="<small>"
+tag_source="<source>"
+tag_spacer="<spacer>"
+tag_span="<span>"
+tag_strike="<strike>"
+tag_strong="<strong>"
+tag_style="<style>"
+tag_sub="<sub>"
+tag_summary="<summary>"
+tag_sup="<sup>"
+tag_svg="<svg>"
+tag_table="<table>"
+tag_tbody="<tbody>"
+tag_td="<td>"
+tag_template="<template>"
+tag_textarea="<textarea>"
+tag_tfoot="<tfoot>"
+tag_th="<th>"
+tag_thead="<thead>"
+tag_time="<time>"
+tag_title="<title>"
+tag_tr="<tr>"
+tag_track="<track>"
+tag_tt="<tt>"
+tag_u="<u>"
+tag_ul="<ul>"
+tag_var="<var>"
+tag_video="<video>"
+tag_wbr="<wbr>"
+tag_xmp="<xmp>"
+
+
+# attributes
+
+"accept"
+"accept-charset"
+"accesskey"
+"action"
+"align"
+"allow"
+"alt"
+"async"
+"autocapitalize"
+"autocomplete"
+"autofocus"
+"autoplay"
+"background"
+"bgcolor"
+"border"
+"capture"
+"challenge"
+"charset"
+"checked"
+"cite"
+"class"
+"code"
+"codebase"
+"color"
+"cols"
+"colspan"
+"content"
+"contenteditable"
+"contextmenu"
+"controls"
+"coords"
+"crossorigin"
+"csp"
+"data"
+"data-"
+"datetime"
+"decoding"
+"default"
+"defer"
+"dir"
+"dirname"
+"disabled"
+"download"
+"draggable"
+"dropzone"
+"enctype"
+"enterkeyhint"
+"for"
+"form"
+"formaction"
+"formenctype"
+"formmethod"
+"formnovalidate"
+"formtarget"
+"headers"
+"height"
+"hidden"
+"high"
+"href"
+"hreflang"
+"http-equiv"
+"icon"
+"id"
+"importance"
+"integrity"
+"inputmode"
+"ismap"
+"itemprop"
+"keytype"
+"kind"
+"label"
+"lang"
+"language"
+"loading"
+"list"
+"loop"
+"low"
+"manifest"
+"max"
+"maxlength"
+"minlength"
+"media"
+"method"
+"min"
+"multiple"
+"muted"
+"name"
+"novalidate"
+"onabort"
+"onactivate"
+"onafterprint"
+"onafterupdate"
+"onanimationend"
+"onanimationiteration"
+"onanimationstart"
+"onautocomplete"
+"onautocompleteerror"
+"onbeforeactivate"
+"onbeforecopy"
+"onbeforecut"
+"onbeforedeactivate"
+"onbeforeeditfocus"
+"onbeforepaste"
+"onbeforeprint"
+"onbeforeunload"
+"onbeforeupdate"
+"onbegin"
+"onblur"
+"onbounce"
+"oncancel"
+"oncanplay"
+"oncanplaythrough"
+"oncellchange"
+"onchange"
+"onclick"
+"onclose"
+"oncompassneedscalibration"
+"oncontextmenu"
+"oncontrolselect"
+"oncopy"
+"oncuechange"
+"oncut"
+"ondataavailable"
+"ondatasetchanged"
+"ondatasetcomplete"
+"ondblclick"
+"ondeactivate"
+"ondevicelight"
+"ondevicemotion"
+"ondeviceorientation"
+"ondeviceproximity"
+"ondrag"
+"ondragdrop"
+"ondragend"
+"ondragenter"
+"ondragexit"
+"ondragleave"
+"ondragover"
+"ondragstart"
+"ondrop"
+"ondurationchange"
+"onemptied"
+"onend"
+"onended"
+"onerror"
+"onerrorupdate"
+"onexit"
+"onfilterchange"
+"onfinish"
+"onfocus"
+"onfocusin"
+"onfocusout"
+"onformchange "
+"onforminput "
+"ongesturechange"
+"ongestureend"
+"ongesturestart"
+"onhashchange"
+"onhelp"
+"oninput"
+"oninvalid"
+"onkeydown"
+"onkeypress"
+"onkeyup"
+"onlanguagechange"
+"onlayoutcomplete"
+"onload"
+"onloadeddata"
+"onloadedmetadata"
+"onloadstart"
+"onlosecapture"
+"onmediacomplete"
+"onmediaerror"
+"onmessage"
+"onmousedown"
+"onmouseenter"
+"onmouseleave"
+"onmousemove"
+"onmouseout"
+"onmouseover"
+"onmouseup"
+"onmousewheel"
+"onmove"
+"onmoveend"
+"onmovestart"
+"onmozfullscreenchange"
+"onmozfullscreenerror"
+"onmozpointerlockchange"
+"onmozpointerlockerror"
+"onmsgesturechange"
+"onmsgesturedoubletap"
+"onmsgesturehold"
+"onmsgesturerestart"
+"onmsinertiastart"
+"onmspointercancel"
+"onmspointerdown"
+"onmspointerenter"
+"onmspointerhover"
+"onmspointerleave"
+"onmspointermove"
+"onmspointerout"
+"onmspointerover"
+"onmspointerup"
+"onoffline"
+"ononline"
+"onorientationchange"
+"onoutofsync"
+"onpagehide"
+"onpageshow"
+"onpaste"
+"onpause"
+"onplay"
+"onplaying"
+"onpopstate"
+"onprogress"
+"onpropertychange"
+"onratechange"
+"onreadystatechange"
+"onreceived"
+"onrepeat"
+"onreset"
+"onresize"
+"onresizeend"
+"onresizestart"
+"onresume"
+"onreverse"
+"onrowdelete"
+"onrowenter"
+"onrowexit"
+"onrowinserted"
+"onrowsdelete"
+"onrowsinserted"
+"onscroll"
+"onsearch"
+"onseek"
+"onseeked"
+"onseeking"
+"onselect"
+"onselectionchange"
+"onselectstart"
+"onshow"
+"onstalled"
+"onstart"
+"onstop"
+"onstorage"
+"onsubmit"
+"onsuspend"
+"onsynchrestored"
+"ontimeerror"
+"ontimeupdate"
+"ontoggle"
+"ontouchcancel"
+"ontouchend"
+"ontouchmove"
+"ontouchstart"
+"ontrackchange"
+"ontransitionend"
+"onunload"
+"onurlflip"
+"onuserproximity"
+"onvolumechange"
+"onwaiting"
+"onwebkitanimationend"
+"onwebkitanimationiteration"
+"onwebkitanimationstart"
+"onwebkitmouseforcechanged"
+"onwebkitmouseforcedown"
+"onwebkitmouseforceup"
+"onwebkitmouseforcewillbegin"
+"onwebkittransitionend"
+"onwebkitwillrevealbottom"
+"onwheel"
+"onzoom"
+"open"
+"optimum"
+"pattern"
+"ping"
+"placeholder"
+"poster"
+"preload"
+"radiogroup"
+"readonly"
+"referrerpolicy"
+"rel"
+"required"
+"reversed"
+"rows"
+"rowspan"
+"sandbox"
+"scope"
+"scoped"
+"selected"
+"shape"
+"size"
+"sizes"
+"slot"
+"span"
+"spellcheck"
+"src"
+"srcdoc"
+"srclang"
+"srcset"
+"start"
+"step"
+"style"
+"summary"
+"tabindex"
+"target"
+"title"
+"translate"
+"type"
+"usemap"
+"value"
+"width"
+"wrap"
diff --git a/libpurple/tests/dictionaries/xml.dict b/libpurple/tests/dictionaries/xml.dict
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/dictionaries/xml.dict
@@ -0,0 +1,82 @@
+#
+# AFL dictionary for XML
+# ----------------------
+#
+# Several basic syntax elements and attributes, modeled on libxml2.
+#
+# Created by Michal Zalewski <lcamtuf@google.com>
+#
+
+attr_encoding=" encoding=\"1\""
+attr_generic=" a=\"1\""
+attr_href=" href=\"1\""
+attr_standalone=" standalone=\"no\""
+attr_version=" version=\"1\""
+attr_xml_base=" xml:base=\"1\""
+attr_xml_id=" xml:id=\"1\""
+attr_xml_lang=" xml:lang=\"1\""
+attr_xml_space=" xml:space=\"1\""
+attr_xmlns=" xmlns=\"1\""
+
+entity_builtin="&lt;"
+entity_decimal="&#1;"
+entity_external="&a;"
+entity_hex="&#x1;"
+
+string_any="ANY"
+string_brackets="[]"
+string_cdata="CDATA"
+string_col_fallback=":fallback"
+string_col_generic=":a"
+string_col_include=":include"
+string_dashes="--"
+string_empty="EMPTY"
+string_empty_dblquotes="\"\""
+string_empty_quotes="''"
+string_entities="ENTITIES"
+string_entity="ENTITY"
+string_fixed="#FIXED"
+string_id="ID"
+string_idref="IDREF"
+string_idrefs="IDREFS"
+string_implied="#IMPLIED"
+string_nmtoken="NMTOKEN"
+string_nmtokens="NMTOKENS"
+string_notation="NOTATION"
+string_parentheses="()"
+string_pcdata="#PCDATA"
+string_percent="%a"
+string_public="PUBLIC"
+string_required="#REQUIRED"
+string_schema=":schema"
+string_system="SYSTEM"
+string_ucs4="UCS-4"
+string_utf16="UTF-16"
+string_utf8="UTF-8"
+string_xmlns="xmlns:"
+
+tag_attlist="<!ATTLIST"
+tag_cdata="<![CDATA["
+tag_close="</a>"
+tag_doctype="<!DOCTYPE"
+tag_element="<!ELEMENT"
+tag_entity="<!ENTITY"
+tag_ignore="<![IGNORE["
+tag_include="<![INCLUDE["
+tag_notation="<!NOTATION"
+tag_open="<a>"
+tag_open_close="<a />"
+tag_open_exclamation="<!"
+tag_open_q="<?"
+tag_sq2_close="]]>"
+tag_xml_q="<?xml?>"
+
+encoding_utf="UTF-"
+encoding_iso1="ISO-8859"
+encoding_iso3="ISO-10646-UCS"
+encoding_iso5="ISO-LATIN-1"
+encoding_jis="SHIFT_JIS"
+encoding_utf7="UTF-7"
+encoding_utf16le="UTF-16BE"
+encoding_utf16le="UTF-16LE"
+encoding_ascii="US-ASCII"
diff --git a/libpurple/tests/fuzz_html_to_xhtml.c b/libpurple/tests/fuzz_html_to_xhtml.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_html_to_xhtml.c
@@ -0,0 +1,30 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <purple.h>
+
+#include "../util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_html = (char *)malloc(size + 1);
+
+	if (malicious_html == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_html, data, size);
+	malicious_html[size] = '\0';
+
+	gchar *xhtml = "", *plaintext = "";
+
+	purple_markup_html_to_xhtml(malicious_html, &xhtml, &plaintext);
+
+	g_free(xhtml);
+	g_free(plaintext);
+
+	free(malicious_html);
+
+	return 0;
+}
diff --git a/libpurple/tests/fuzz_jabber_caps.c b/libpurple/tests/fuzz_jabber_caps.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_jabber_caps.c
@@ -0,0 +1,32 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "../xmlnode.h"
+#include "../protocols/jabber/caps.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_xml = (char *)malloc(size + 1);
+	char *str;
+        xmlnode *query;
+
+	if (malicious_xml == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_xml, data, size);
+	malicious_xml[size] = '\0';
+
+        query = xmlnode_new(malicious_xml);
+
+        jabber_caps_parse_client_info(query);
+
+        xmlnode_free(query);
+
+	free(malicious_xml);
+
+	return 0;
+}
diff --git a/libpurple/tests/fuzz_jabber_id_new.c b/libpurple/tests/fuzz_jabber_id_new.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_jabber_id_new.c
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "../account.h"
+#include "../conversation.h"
+#include "../xmlnode.h"
+#include "../protocols/jabber/jutil.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_jid = (char *)malloc(size + 1);
+
+	if (malicious_jid == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_jid, data, size);
+	malicious_jid[size] = '\0';
+
+	JabberID *jid = jabber_id_new(malicious_jid);
+
+	jabber_id_free(jid);
+
+	free(malicious_jid);
+
+	return 0;
+}
diff --git a/libpurple/tests/fuzz_markup_strip_html.c b/libpurple/tests/fuzz_markup_strip_html.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_markup_strip_html.c
@@ -0,0 +1,28 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <purple.h>
+
+#include "../util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_html = (char *)malloc(size + 1);
+	char *stripped;
+
+	if (malicious_html == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_html, data, size);
+	malicious_html[size] = '\0';
+
+	stripped = purple_markup_strip_html(malicious_html);
+
+	g_free(stripped);
+
+	free(malicious_html);
+
+	return 0;
+}
diff --git a/libpurple/tests/fuzz_mime.c b/libpurple/tests/fuzz_mime.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_mime.c
@@ -0,0 +1,26 @@
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <purple.h>
+
+#include "../util.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_mime = (char *)malloc(size + 1);
+	if (malicious_mime == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_mime, data, size);
+	malicious_mime[size] = '\0';
+
+	gchar *result = purple_mime_decode_field(malicious_mime);
+	g_free(result);
+
+
+	free(malicious_mime);
+
+	return 0;
+}
diff --git a/libpurple/tests/fuzz_xmlnode.c b/libpurple/tests/fuzz_xmlnode.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/fuzz_xmlnode.c
@@ -0,0 +1,47 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "../xmlnode.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	char *malicious_xml = (char *)malloc(size + 1);
+	char *str;
+	xmlnode *xml;
+
+	if (malicious_xml == NULL) {
+		return 0;
+	}
+
+	memcpy(malicious_xml, data, size);
+	malicious_xml[size] = '\0';
+
+	xml = xmlnode_from_str(malicious_xml, -1);
+
+	str = xmlnode_to_str(xml, NULL);
+
+	if (str == NULL) {
+		g_free(str);
+		xmlnode_free(xml);
+		free(malicious_xml);
+		return 0;
+	}
+
+	if (strcmp(malicious_xml, str) != 0) {
+		g_free(str);
+		xmlnode_free(xml);
+		free(malicious_xml);
+		__builtin_trap();
+	}
+
+	g_free(str);
+
+	xmlnode_free(xml);
+
+	free(malicious_xml);
+
+	return 0;
+}
