CVE-2018-8017
Description
In Apache Tika 1.2 to 1.18, a carefully crafted file can trigger an infinite loop in the IptcAnpaParser.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache Tika 1.2–1.18 contains an infinite loop in IptcAnpaParser when parsing a crafted file, leading to denial of service.
Vulnerability
Apache Tika versions 1.2 through 1.18 are vulnerable to an infinite loop in the IptcAnpaParser class [1][2][3]. When processing a specially crafted file, the parser's loop condition fails to correctly handle an index that reaches the end of the byte array, causing the loop to never terminate [2][3]. The affected versions are the entire 1.x line up to and including 1.18 [4].
Exploitation
An attacker must deliver a carefully crafted file to a service that uses Apache Tika to parse IPTC metadata [1][4]. No authentication or special network position is required if the service accepts untrusted file uploads. The vulnerability is triggered simply by the parsing process; no user interaction beyond file submission is needed [2][3]. The commit history shows the fix changes three if statements from read > value.length to read >= value.length, indicating the infinite loop occurs when the read pointer exactly equals the array length [2][3].
Impact
Successful exploitation causes an infinite loop, leading to denial of service (DoS) via CPU exhaustion [4]. There is no disclosure of information, modification of data, or code execution. The impact is limited to availability of the Tika service [4].
Mitigation
Apache Tika 1.20 (released September 2018) contains the fix [4]; version 1.19 was not released. Users still on the 1.x line should upgrade to at least 1.20. For users unable to upgrade, no workaround is documented. Tika 1.x reached end of life in April 2025 [1], so upgrading to a supported release line is recommended.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.tika:tika-coreMaven | >= 1.2, < 1.19 | 1.19 |
Affected products
2- Apache Software Foundation/Apache Tikav5Range: 1.2 to 1.18
Patches
41 file changed · +6 −6
tika-parsers/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java+6 −6 modified@@ -463,7 +463,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c) and not EOL bdy_heading += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on @@ -508,7 +508,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c), or carat (\x5e) and not EOL bdy_title += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -575,7 +575,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { // less than delimiter (\x3c), maybe also badly formed with just new line tmp_line += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -674,7 +674,7 @@ else if (tmp_line.toLowerCase(Locale.ROOT).startsWith("eds") || longline.equals( // read until the train runs out of tracks bdy_body += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } } @@ -713,14 +713,14 @@ private boolean parseFooter(byte[] value, HashMap<String,String> properties) { ftr_source += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read] : 0x00; // attempt to read until end of stream read++; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } while ((val_next != LT) && (val_next != CR) && (val_next != LF) && (val_next != 0)) { // get as much timedate as possible // this is an american format, so arrives as mm-dd-yy HHiizzz ftr_datetime += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; // skip the new lines - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on
1 file changed · +6 −6
tika-parsers/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java+6 −6 modified@@ -463,7 +463,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c) and not EOL bdy_heading += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on @@ -508,7 +508,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c), or carat (\x5e) and not EOL bdy_title += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -575,7 +575,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { // less than delimiter (\x3c), maybe also badly formed with just new line tmp_line += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -674,7 +674,7 @@ else if (tmp_line.toLowerCase(Locale.ROOT).startsWith("eds") || longline.equals( // read until the train runs out of tracks bdy_body += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } } @@ -713,14 +713,14 @@ private boolean parseFooter(byte[] value, HashMap<String,String> properties) { ftr_source += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read] : 0x00; // attempt to read until end of stream read++; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } while ((val_next != LT) && (val_next != CR) && (val_next != LF) && (val_next != 0)) { // get as much timedate as possible // this is an american format, so arrives as mm-dd-yy HHiizzz ftr_datetime += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; // skip the new lines - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on
1 file changed · +6 −6
tika-parsers/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java+6 −6 modified@@ -463,7 +463,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c) and not EOL bdy_heading += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on @@ -508,7 +508,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c), or carat (\x5e) and not EOL bdy_title += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -575,7 +575,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { // less than delimiter (\x3c), maybe also badly formed with just new line tmp_line += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -674,7 +674,7 @@ else if (tmp_line.toLowerCase(Locale.ROOT).startsWith("eds") || longline.equals( // read until the train runs out of tracks bdy_body += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } } @@ -713,14 +713,14 @@ private boolean parseFooter(byte[] value, HashMap<String,String> properties) { ftr_source += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read] : 0x00; // attempt to read until end of stream read++; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } while ((val_next != LT) && (val_next != CR) && (val_next != LF) && (val_next != 0)) { // get as much timedate as possible // this is an american format, so arrives as mm-dd-yy HHiizzz ftr_datetime += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; // skip the new lines - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on
1 file changed · +6 −6
tika-parsers/src/main/java/org/apache/tika/parser/iptc/IptcAnpaParser.java+6 −6 modified@@ -463,7 +463,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c) and not EOL bdy_heading += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on @@ -508,7 +508,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { while ((val_next != LT) && (val_next != CT) && (val_next != CR) && (val_next != LF)) { // less than delimiter (\x3c), or carat (\x5e) and not EOL bdy_title += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -575,7 +575,7 @@ private boolean parseBody(byte[] value, HashMap<String,String> properties) { // less than delimiter (\x3c), maybe also badly formed with just new line tmp_line += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == CT) { // start of a new section , when first didn't finish cleanly @@ -674,7 +674,7 @@ else if (tmp_line.toLowerCase(Locale.ROOT).startsWith("eds") || longline.equals( // read until the train runs out of tracks bdy_body += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } } @@ -713,14 +713,14 @@ private boolean parseFooter(byte[] value, HashMap<String,String> properties) { ftr_source += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read] : 0x00; // attempt to read until end of stream read++; - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } while ((val_next != LT) && (val_next != CR) && (val_next != LF) && (val_next != 0)) { // get as much timedate as possible // this is an american format, so arrives as mm-dd-yy HHiizzz ftr_datetime += (char)(val_next & 0xff); // convert the byte to an unsigned int val_next = (read < value.length) ? value[read++] : 0x00; // skip the new lines - if (read > value.length) { break; } // shouldn't ever hit this, but save a NPE + if (read >= value.length) { break; } // shouldn't ever hit this, but save a NPE } if (val_next == LT) { // hit the delimiter, carry on
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-j53j-gmr9-h8g3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-8017ghsaADVISORY
- www.securityfocus.com/bid/105513ghsavdb-entryx_refsource_BIDWEB
- github.com/apache/tika/commit/62926cae31a02d4f23d21148435804b96c543ccghsaWEB
- github.com/apache/tika/commit/8a6a9e1344f5b10ebfa1a189dc3c30d0da2b9d4ghsaWEB
- lists.apache.org/thread.html/72df7a3f0dda49a912143a1404b489837a11f374dfd1961061873a91%40%3Cdev.tika.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/72df7a3f0dda49a912143a1404b489837a11f374dfd1961061873a91@%3Cdev.tika.apache.org%3EghsaWEB
News mentions
0No linked articles in our index yet.