[java-support] 08/15: Tests for parsing bad inputs.

Scott Cantor cantor.2 at osu.edu
Mon Apr 18 20:50:04 UTC 2022


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch dev/JSPT-111
in repository java-support.

View the commit online:
http://git.shibboleth.net/view/?p=java-support.git;a=commit;h=4532e6570ea71e0d535aa3d651728dbf2042730f

commit 4532e6570ea71e0d535aa3d651728dbf2042730f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu May 20 16:16:00 2021 -0400

    Tests for parsing bad inputs.
---
 .../shibboleth/utilities/java/support/ddf/DDF.java |  83 ++++++++----
 .../utilities/java/support/ddf/DDFTest.java        | 146 +++++++++++++++++++--
 2 files changed, 190 insertions(+), 39 deletions(-)

diff --git a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
index c84f6be..53764c1 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
@@ -1275,28 +1275,29 @@ public class DDF implements Iterable<DDF> {
         } catch (final IllegalArgumentException e) {
             throw new IOException("Invalid DDF type");
         }
-        
-        // Required last byte read will vary by type.
-        if (type == DDFType.DDF_EMPTY || type == DDFType.DDF_POINTER) {
-            if (ch != 0x0A) {
-                throw new IOException("Empty/pointer record not terminated by linefeed");
-            }
-            // Nothing else to do, it's already empty.
-            return obj;
-        }
-        
-        // All others should be followed by a space.
-        if (ch != 0x20) {
-            throw new IOException("Type field not followed by space character");
-        }
 
         // Process typical value types.
         final StringBuilder valueBuilder = new StringBuilder();
         switch (type) {
+            case DDF_EMPTY:
+            case DDF_POINTER:
+                if (ch != 0x0A) {
+                    throw new IOException("Empty/pointer record not terminated by linefeed");
+                }
+                // Nothing else to do, it's already empty.
+                return obj;
+            
             case DDF_STRING:
             case DDF_STRING_UNSAFE:
-            case DDF_INT:
-            case DDF_FLOAT:
+                if (ch == 0x0A) {
+                    if (type == DDFType.DDF_STRING) {
+                        return obj.string(null);
+                    }
+                    return obj.unsafe_string(null);
+                } else if (ch != 0x20) {
+                    throw new IOException("Type field not followed by space character");
+                }
+                
                 while ((ch = is.read()) != -1 && !Character.isWhitespace(ch)) {
                     if (ch >= 0 && ch <= 127) {
                         // The int is a code point from 0..255, but our grammar constrains this to 0..127 so
@@ -1314,23 +1315,50 @@ public class DDF implements Iterable<DDF> {
                 try {
                     if (type == DDFType.DDF_STRING) {
                         // String values are handled as UTF-8.
-                        obj.string(URLDecoder.decode(valueBuilder.toString(), "UTF-8"));
-                    } else if (type == DDFType.DDF_STRING_UNSAFE) {
-                        // Unsafe string values are processed as ISO-8859-1.
-                        // They may be anything, but it will guarantee a single byte encoding.
-                        obj.unsafe_string(URLDecoder.decode(valueBuilder.toString(), "ISO-8859-1"));
-                    } else if (type == DDFType.DDF_INT) {
-                        obj.integer(valueBuilder.toString());
-                    } else if (type == DDFType.DDF_FLOAT) {
-                        obj.floating(valueBuilder.toString());
+                        return obj.string(URLDecoder.decode(valueBuilder.toString(), "UTF-8"));
                     }
+                    
+                    // Unsafe string values are processed as ISO-8859-1.
+                    // They may be anything, but it will guarantee a single byte encoding.
+                    return obj.unsafe_string(URLDecoder.decode(valueBuilder.toString(), "ISO-8859-1"));
+                    
                 } catch (final IllegalArgumentException e) {
                     throw new IOException(e);
                 }
-                return obj;
+
+            case DDF_INT:
+            case DDF_FLOAT:
+                if (ch != 0x20) {
+                    throw new IOException("Type field not followed by space character");
+                }
+                
+                while ((ch = is.read()) != -1 && !Character.isWhitespace(ch)) {
+                    if (ch >= 0 && ch <= 127) {
+                        // The int is a code point from 0..255, but our grammar constrains this to 0..127 so
+                        // this is a safe append, to promote the ASCII into Unicode.
+                        valueBuilder.appendCodePoint(ch);
+                    } else {
+                        throw new IOException("Invalid code point outside US-ASCII range");
+                    }
+                }
+                
+                if (ch != 0x0A) {
+                    throw new IOException("Numeric value not followed by linefeed");
+                } else if (valueBuilder.length() == 0) {
+                    throw new IOException("Numeric value missing");
+                }
+                
+                if (type == DDFType.DDF_INT) {
+                    return obj.integer(valueBuilder.toString());
+                }
+                return obj.floating(valueBuilder.toString());
                 
             case DDF_STRUCT:
             case DDF_LIST:
+                if (ch != 0x20) {
+                    throw new IOException("Type field not followed by space character");
+                }
+
                 while ((ch = is.read()) != -1 && Character.isDigit(ch)) {
                     // This is safe because the byte contract of the stream disallows
                     // any non-ASCII digit from satisfying the isDigit check.
@@ -1339,6 +1367,8 @@ public class DDF implements Iterable<DDF> {
                 
                 if (ch != 0x0A) {
                     throw new IOException("Record count not followed by linefeed");
+                } else if (valueBuilder.length() == 0) {
+                    throw new IOException("Record count missing");
                 }
                 
                 int count;
@@ -1358,7 +1388,6 @@ public class DDF implements Iterable<DDF> {
                     obj.add(deserialize(is));
                 }
                 return obj;
-                
 
             default:
                 throw new IOException("Unexpected record type");
diff --git a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
index 1889c58..9845a30 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
@@ -249,6 +249,19 @@ public class DDFTest {
         }
     }
     
+    /**
+     * Convert test file contents to a byte array.
+     * 
+     * @param name file name
+     * 
+     * @return byte array
+     * 
+     * @throws IOException on error
+     */
+    private byte[] testFile(@Nonnull final String name) throws IOException {
+        return getClass().getResourceAsStream(name).readAllBytes();
+    }
+    
     @Test
     public void testDeserialize() throws IOException {
         try (final InputStream is = getClass().getResourceAsStream("empty-noname.ddf")) {
@@ -313,18 +326,127 @@ public class DDFTest {
         }
     }
     
-    
-    /**
-     * Convert test file contents to a byte array.
-     * 
-     * @param name file name
-     * 
-     * @return byte array
-     * 
-     * @throws IOException on error
-     */
-    private byte[] testFile(@Nonnull final String name) throws IOException {
-        return getClass().getResourceAsStream(name).readAllBytes();
+    @Test
+    public void testBadInputs() {
+        try (final InputStream is = new ByteArrayInputStream(new String().getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String("\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(" ").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(".\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". -2").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 0 \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 1 foo \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 2\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 2 \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 3\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 3 \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 4 \n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 4\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 4 2\n. 1 foo\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+        
+        try (final InputStream is = new ByteArrayInputStream(new String(". 5 foo\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
+        try (final InputStream is = new ByteArrayInputStream(new String(". 5 1\n").getBytes("UTF-8"))) {
+            DDF.deserialize(is);
+            fail("Should have thrown IOException");
+        } catch (final IOException e) {
+            
+        }
+
     }
     
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list