2828import java .io .Serializable ;
2929import java .lang .reflect .Method ;
3030import java .net .MalformedURLException ;
31+ import java .net .URI ;
3132import java .net .URISyntaxException ;
3233import java .net .URL ;
3334import java .net .URLConnection ;
4445import java .util .Properties ;
4546import java .util .logging .Level ;
4647import java .util .logging .Logger ;
48+ import java .util .zip .ZipEntry ;
49+ import java .util .zip .ZipFile ;
50+ import java .util .zip .ZipInputStream ;
4751
4852import javax .servlet .ServletContext ;
4953import javax .servlet .ServletException ;
@@ -1141,7 +1145,8 @@ private ScssCacheEntry compileScssOnTheFly(String filename,
11411145
11421146 /**
11431147 * Check whether a URL obtained from a classloader refers to a valid static
1144- * resource in the directory VAADIN.
1148+ * resource in the directory VAADIN. Directories do not count as valid
1149+ * resources.
11451150 *
11461151 * Warning: Overriding of this method is not recommended, but is possible to
11471152 * support non-default classloaders or servers that may produce URLs
@@ -1161,6 +1166,9 @@ private ScssCacheEntry compileScssOnTheFly(String filename,
11611166 @ Deprecated
11621167 protected boolean isAllowedVAADINResourceUrl (HttpServletRequest request ,
11631168 URL resourceUrl ) {
1169+ if (resourceUrl == null || resourceIsDirectory (resourceUrl )) {
1170+ return false ;
1171+ }
11641172 String resourcePath = resourceUrl .getPath ();
11651173 if ("jar" .equals (resourceUrl .getProtocol ())) {
11661174 // This branch is used for accessing resources directly from the
@@ -1201,6 +1209,114 @@ protected boolean isAllowedVAADINResourceUrl(HttpServletRequest request,
12011209 }
12021210 }
12031211
1212+ private boolean resourceIsDirectory (URL resource ) {
1213+ if (resource .getPath ().endsWith ("/" )) {
1214+ return true ;
1215+ }
1216+ URI resourceURI = null ;
1217+ boolean isDirectory = false ;
1218+ try {
1219+ resourceURI = resource .toURI ();
1220+ } catch (URISyntaxException e ) {
1221+ getLogger ().log (Level .FINE ,
1222+ "Syntax error in uri from getStaticResource" , e );
1223+ // Return false as we couldn't determine if the resource is a
1224+ // directory.
1225+ return false ;
1226+ }
1227+
1228+ if ("jar" .equals (resource .getProtocol ())) {
1229+ // Get the file path in jar
1230+
1231+ String [] parts = resource .getPath ().split ("!" );
1232+ String pathInJar = null ;
1233+ String pathOfWar = null ;
1234+ String pathOfJar = null ;
1235+ if (parts .length == 2 ) {
1236+ pathInJar = parts [1 ].substring (1 );
1237+ pathOfJar = parts [0 ].substring (8 );
1238+
1239+ } else if (resource .getPath ().startsWith ("file:" )) {
1240+ pathInJar = parts [2 ].substring (1 );
1241+ pathOfJar = parts [1 ].substring (1 );
1242+ pathOfWar = parts [0 ].substring (6 );
1243+ } else {
1244+ pathInJar = parts [2 ].substring (1 );
1245+ pathOfJar = parts [1 ].substring (1 );
1246+ pathOfWar = parts [0 ].substring (10 );
1247+ }
1248+ try {
1249+ // Jars and wars are zip files, hence we use ZipFile to parse
1250+ // them. Java 6 does not have virtual filesystems.
1251+ ZipEntry entry = null ;
1252+ if (pathOfWar == null ) {
1253+ entry = getJarEntry (pathOfJar , pathInJar );
1254+ } else {
1255+ entry = getWarEntry (pathOfWar , pathOfJar , pathInJar );
1256+ }
1257+ if (entry != null ) {
1258+ isDirectory = entry .isDirectory ();
1259+ }
1260+ return isDirectory ;
1261+ } catch (IOException e ) {
1262+ // Return false as we couldn't determine if the resource is a
1263+ // directory.
1264+ return false ;
1265+ }
1266+ }
1267+ // If not a jar check if a file path directory.
1268+ File file = new File (resourceURI );
1269+ return "file" .equals (resource .getProtocol ()) && file .isDirectory ();
1270+ }
1271+
1272+ // Find entry pathInJar within nested jar pathOfJar within war pathOfWar.
1273+ private ZipEntry getWarEntry (String pathOfWar , String pathOfJar ,
1274+ String pathInJar ) throws IOException {
1275+ ZipFile war = null ;
1276+ ZipInputStream stream = null ;
1277+ try {
1278+ // Use ZipInputStream for nested jar files
1279+ war = new ZipFile (pathOfWar );
1280+ ZipEntry jarEntry = war .getEntry (pathOfJar );
1281+ InputStream in = war .getInputStream (jarEntry );
1282+ stream = new ZipInputStream (in );
1283+ return findEntry (stream , pathInJar );
1284+ } finally {
1285+ if (stream != null ) {
1286+ stream .close ();
1287+ }
1288+ if (war != null ) {
1289+ war .close ();
1290+ }
1291+ }
1292+ }
1293+
1294+ // Find entry pathInJar within jar pathOfJar.
1295+ private ZipEntry getJarEntry (String pathOfJar , String pathInJar )
1296+ throws IOException {
1297+ ZipFile jar = null ;
1298+ try {
1299+ jar = new ZipFile (pathOfJar );
1300+ return jar .getEntry (pathInJar );
1301+ } finally {
1302+ if (jar != null ) {
1303+ jar .close ();
1304+ }
1305+ }
1306+ }
1307+
1308+ // Traverse zip's header table for entries and return entry matching name.
1309+ private ZipEntry findEntry (ZipInputStream in , String name )
1310+ throws IOException {
1311+ ZipEntry entry = null ;
1312+ while ((entry = in .getNextEntry ()) != null ) {
1313+ if (entry .getName ().equals (name )) {
1314+ return entry ;
1315+ }
1316+ }
1317+ return null ;
1318+ }
1319+
12041320 /**
12051321 * Checks if the browser has an up to date cached version of requested
12061322 * resource. Currently the check is performed using the "If-Modified-Since"
0 commit comments