Author: toad
Date: 2008-02-09 13:05:18 +0000 (Sat, 09 Feb 2008)
New Revision: 17744
Modified:
trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
Log:
Whitelist specified chunk types.
Blacklist *all* types of text chunks, if deleteText is enabled.
Modified: trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
2008-02-09 12:48:29 UTC (rev 17743)
+++ trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
2008-02-09 13:05:18 UTC (rev 17744)
@@ -31,12 +31,11 @@
import java.util.zip.CRC32;
/**
- * Content filter for PNG's.
- * This one just verifies that a PNG is valid, and throws if it isn't.
+ * Content filter for PNG's. Only allows valid chunks (valid CRC, known chunk
type).
*
* It can strip the timestamp and "text(.)*" chunks if asked to
*
- * FIXME: should be a whitelisting filter instead of a blacklisting one
+ * FIXME: validate chunk contents where possible.
*/
public class PNGFilter implements ContentDataFilter {
@@ -45,6 +44,18 @@
private final boolean checkCRCs;
static final byte[] pngHeader =
{(byte) 137, (byte) 80, (byte) 78, (byte) 71, (byte) 13, (byte)
10, (byte) 26, (byte) 10};
+ static final String[] HARMLESS_CHUNK_TYPES = {
+ "tRNS",
+ "cHRM",
+ "gAMA",
+ "iCCP", // FIXME Embedded ICC profile: could this conceivably
cause a web lookup?
+ "sBIT", // FIXME rather obscure ??
+ "sRGB",
+ "bKGD",
+ "hIST",
+ "pHYs",
+ "sPLT"
+ };
PNGFilter(boolean deleteText, boolean deleteTimestamp, boolean
checkCRCs) {
this.deleteText = deleteText;
@@ -184,10 +195,13 @@
}
}
+ boolean validChunkType = true;
+
if(!skip && "IHDR".equals(chunkTypeString)) {
if(hasSeenIHDR)
throw new IOException("Two IHDR
chunks detected!!");
hasSeenIHDR = true;
+ validChunkType = true;
}
if(!hasSeenIHDR)
@@ -197,11 +211,13 @@
if(hasSeenIEND)
throw new IOException("Two IEND
chunks detected!!");
hasSeenIEND = true;
+ validChunkType = true;
}
if(!skip &&
"PLTE".equalsIgnoreCase(chunkTypeString)) {
if(hasSeenIDAT)
throw new IOException("PLTE
must be before IDAT");
+ validChunkType = true;
hasSeenPLTE = true;
}
@@ -209,18 +225,36 @@
if(hasSeenIDAT &&
!"IDAT".equalsIgnoreCase(lastChunkType))
throw new IOException("Multiple
IDAT chunks must be consecutive!");
hasSeenIDAT = true;
+ validChunkType = true;
}
-
+
+ if(!validChunkType) {
+ for(int
i=0;i<HARMLESS_CHUNK_TYPES.length;i++) {
+
if(HARMLESS_CHUNK_TYPES[i].equals(chunkTypeString))
+ validChunkType = true;
+ }
+ }
+
if(dis.available() < 1) {
if(!(hasSeenIEND && hasSeenIHDR))
throw new IOException("Missing
IEND or IHDR!");
finished = true;
}
- if(deleteText &&
"text".equalsIgnoreCase(chunkTypeString))
+ if("text".equalsIgnoreCase(chunkTypeString) ||
"itxt".equalsIgnoreCase(chunkTypeString)
+ ||
"ztxt".equalsIgnoreCase(chunkTypeString)) {
+ if(deleteText) skip = true;
+ else validChunkType = true;
+ } else if(deleteTimestamp &&
"time".equalsIgnoreCase(chunkTypeString)) {
+ if(deleteTimestamp) skip = true;
+ else validChunkType = true;
+ }
+
+ if(!validChunkType) {
+ if(output == null)
+ throw new IOException("Unknown
chunk type");
skip = true;
- else if(deleteTimestamp &&
"time".equalsIgnoreCase(chunkTypeString))
- skip = true;
+ }
if(skip && output == null)
return null;