VYPR
Moderate severityNVD Advisory· Published Sep 19, 2018· Updated Sep 17, 2024

CVE-2018-8017

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.

PackageAffected versionsPatched versions
org.apache.tika:tika-coreMaven
>= 1.2, < 1.191.19

Affected products

2

Patches

4
8a6a9e1344f5

fix logic in iptc parser

https://github.com/apache/tikatballisonMay 25, 2018via ghsa
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
    
62926cae31a0

fix logic in iptc parser

https://github.com/apache/tikatballisonMay 25, 2018via ghsa
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
    
8a6a9e1344f5

fix logic in iptc parser

https://github.com/apache/tikatballisonMay 25, 2018via ghsa
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
    
62926cae31a0

fix logic in iptc parser

https://github.com/apache/tikatballisonMay 25, 2018via ghsa
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

News mentions

0

No linked articles in our index yet.