CVE-2022-4741: Memory Allocation with Excessive Size Value (CWE-789)
value: A vulnerability was found in docconv up to 1.2.0 and classified as problematic. This issue affects the function ConvertDocx/ConvertODT/ConvertPages/ConvertXML/XMLToText. The manipulation leads to uncontrolled memory allocation. The attack may be initiated remotely. Upgrading to version 1.2.1 is able to address this issue. The name of the patch is 42bcff666855ab978e67a9041d0cdea552f20301. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-216779.
CWE description: The product allocates memory based on an untrusted, large size value, but it does not ensure that the size is within expected limits, allowing arbitrary amounts of memory to be allocated.
CVSS v3 base score: 6.5
Published: 2022-12-25T20:15Z
Repository: https://github.com/sajari/docconv
Language: Go
Files changed: docx.go, limit.go, odt.go, pages.go, xml.go
Commit date: 2022-07-18 15:47:08 +1000
Commit message: fix unbounded memory consumption vulnerability (#111)

Co-authored-by: Helena Mariano <31138349+helenamariano@users.noreply.github.com>
Diff: diff --git a/docx.go b/docx.go
index 0b3c2e6..f9f276a 100644
--- a/docx.go
+++ b/docx.go
@@ -38,11 +38,11 @@ func ConvertDocx(r io.Reader) (string, map[string]string, error) {
 			return "", nil, err
 		}
 		size = si.Size()
 		ra = f
 	} else {
-		b, err := ioutil.ReadAll(r)
+		b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
 		if err != nil {
 			return "", nil, nil
 		}
 		size = int64(len(b))
 		ra = bytes.NewReader(b)
diff --git a/limit.go b/limit.go
new file mode 100644
index 0000000..8ef2940
--- /dev/null
+++ b/limit.go
@@ -0,0 +1,3 @@
+package docconv
+
+const maxBytes = 20 << 20 // 20MB
diff --git a/odt.go b/odt.go
index 68dd968..ad93d07 100644
--- a/odt.go
+++ b/odt.go
@@ -12,11 +12,11 @@ import (
 // ConvertODT converts a ODT file to text
 func ConvertODT(r io.Reader) (string, map[string]string, error) {
 	meta := make(map[string]string)
 	var textBody string
 
-	b, err := ioutil.ReadAll(r)
+	b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
 	if err != nil {
 		return "", nil, err
 	}
 	zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
 	if err != nil {
diff --git a/pages.go b/pages.go
index 5325ecf..54a2b03 100644
--- a/pages.go
+++ b/pages.go
@@ -19,11 +19,11 @@ import (
 // ConvertPages converts a Pages file to text.
 func ConvertPages(r io.Reader) (string, map[string]string, error) {
 	meta := make(map[string]string)
 	var textBody string
 
-	b, err := ioutil.ReadAll(r)
+	b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
 	if err != nil {
 		return "", nil, fmt.Errorf("error reading data: %v", err)
 	}
 
 	zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
diff --git a/xml.go b/xml.go
index 8f422a3..28da02e 100644
--- a/xml.go
+++ b/xml.go
@@ -23,11 +23,11 @@ func ConvertXML(r io.Reader) (string, map[string]string, error) {
 
 // XMLToText converts XML to plain text given how to treat elements.
 func XMLToText(r io.Reader, breaks []string, skip []string, strict bool) (string, error) {
 	var result string
 
-	dec := xml.NewDecoder(r)
+	dec := xml.NewDecoder(io.LimitReader(r, maxBytes))
 	dec.Strict = strict
 	for {
 		t, err := dec.Token()
 		if err != nil {
 			if err == io.EOF {
@@ -74,11 +74,11 @@ func XMLToText(r io.Reader, breaks []string, skip []string, strict bool) (string
 }
 
 // XMLToMap converts XML to a nested string map.
 func XMLToMap(r io.Reader) (map[string]string, error) {
 	m := make(map[string]string)
-	dec := xml.NewDecoder(r)
+	dec := xml.NewDecoder(io.LimitReader(r, maxBytes))
 	var tagName string
 	for {
 		t, err := dec.Token()
 		if err != nil {
 			if err == io.EOF {

Vulnerable code: 		b, err := ioutil.ReadAll(r)
	b, err := ioutil.ReadAll(r)
	b, err := ioutil.ReadAll(r)
	dec := xml.NewDecoder(r)
	dec := xml.NewDecoder(r)
Fixed code: 		b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
package docconv

const maxBytes = 20 << 20 // 20MB
	b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
	b, err := ioutil.ReadAll(io.LimitReader(r, maxBytes))
	dec := xml.NewDecoder(io.LimitReader(r, maxBytes))
	dec := xml.NewDecoder(io.LimitReader(r, maxBytes))