changeset 1253:eca93c9195e1

Added identificator to .appletTrustSettings to specify version of file
author Jiri Vanek <jvanek@redhat.com>
date Tue, 08 Sep 2015 13:38:12 +0200
parents f3a35ac8f513
children dc3862bd6cca
files ChangeLog netx/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java netx/net/sourceforge/jnlp/util/UrlUtils.java tests/netx/unit/net/sourceforge/jnlp/security/SecurityDialogsTest.java tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/VersionRestrictionTest.java
diffstat 7 files changed, 449 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Sep 07 20:11:55 2015 +0200
+++ b/ChangeLog	Tue Sep 08 13:38:12 2015 +0200
@@ -1,3 +1,34 @@
+2015-09-03  Jiri Vanek  <jvanek@redhat.com>
+
+	Fixed ArrayIndexOutOfBound in version cornercase issue
+	* netx/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java:
+	length of array is checked,
+	* tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/VersionRestrictionTest.java:
+	added tests for this case
+	* tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java:
+	(updateAppletActionTest1) adapted to version string
+
+2015-09-03  Jiri Vanek  <jvanek@redhat.com>
+
+	Added identificator to .appletTrustSettings to specify version of file
+	* netx/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java:
+	added handling of version - readVersion, versionPreffix, backup, currentVersion.
+	(readLine) when first line is read, it is checked for version and acted. If
+	loaded version is missing or older then current 2, then file is  not loaded.
+	otherwise normal loading. (writeContent) now inserts  header with version.
+	(actOnVersionLoad) new method, handling consequences of recognized x current version
+	(backupOldFile) new method, backuping old file as .appletTrustSettings.version-backup
+	* netx/net/sourceforge/jnlp/util/UrlUtils.java: consumed exception during
+	normalization is logged only to console/verbose
+	* tests/netx/unit/net/sourceforge/jnlp/security/SecurityDialogsTest.java:
+	added considering of version
+	* tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/LegacyUnsignedAppletActionStorageImplTest.java:
+	same
+	* tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java:
+	same
+	* tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/VersionRestrictionTest.java:
+	new test file testing version recognition and processing
+
 2015-09-02  Jiri Vanek  <jvanek@redhat.com>
 
 	All UrlRegEx-es got unified and correct quoting
--- a/netx/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java	Mon Sep 07 20:11:55 2015 +0200
+++ b/netx/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImpl.java	Tue Sep 08 13:38:12 2015 +0200
@@ -46,6 +46,7 @@
 import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteAppletAction;
 import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletActionEntry;
 import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletActionStorage;
+import net.sourceforge.jnlp.util.FileUtils;
 import net.sourceforge.jnlp.util.lockingfile.LockingReaderWriter;
 import net.sourceforge.jnlp.util.lockingfile.StorageIoException;
 import net.sourceforge.jnlp.util.logging.OutputController;
@@ -53,10 +54,12 @@
 public class UnsignedAppletActionStorageImpl extends LockingReaderWriter implements UnsignedAppletActionStorage {
 
     protected List<UnsignedAppletActionEntry> items;
-
-    public UnsignedAppletActionStorageImpl(String location) {
-        this(new File(location));
-    }
+    private String readVersion = null;
+    public static final String versionPreffix="#VERSION ";
+    public static final String BACKUP_SUFFIX = "-backup";
+    public static final int currentVersion = 2;
+    private int lineCounter = 0;
+    private boolean loadingDisabled = false;
 
     public UnsignedAppletActionStorageImpl(File location) {
         super(location);
@@ -85,12 +88,29 @@
     @Override
     protected void readLine(String line) {
         if (line.trim().length() != 0) {
-            this.items.add(UnsignedAppletActionEntry.createFromString(line));
+            lineCounter++;
+            if (line.startsWith(versionPreffix) && line.trim().split("\\s+").length > 1) {
+                if (readVersion == null) {
+                    readVersion = line.trim();
+                    actOnVersionLoad();
+                }
+            } else {
+                if (lineCounter>0 && readVersion == null){
+                    actOnNoVersionLoad();
+                }
+                if (!loadingDisabled) {
+                    this.items.add(UnsignedAppletActionEntry.createFromString(line));
+                }
+            }
         }
     }
 
     @Override
     public void writeContent(BufferedWriter bw) throws IOException {
+        lineCounter = 0;
+        readVersion = null;
+        bw.write(versionPreffix + currentVersion + " - note, do not edit or modify this line. It may cause removal of this file.");
+        bw.newLine();
         for (UnsignedAppletActionEntry item : items) {
             try{
                 item.write(bw);
@@ -257,4 +277,45 @@
     public UnsignedAppletActionEntry getMatchingItemByBases(String documentBase, String codeBase, Integer id) {
         return getMatchingItem(documentBase, codeBase, null, id);
     }
+
+    private void actOnVersionLoad() {
+        String versionS = readVersion.split("\\s+")[1];
+        int version = 0;
+        try{
+            version = Integer.valueOf(versionS);
+        } catch (NumberFormatException e){
+            OutputController.getLogger().log(e);
+        }
+        if (version < 2){
+            OutputController.getLogger().log("Stoping laoding of vulnereable "+getBackingFile().getAbsolutePath()+". Will be replaced");
+            loadingDisabled = true;
+            backupOldFile(version, getBackingFile());
+        } else {
+            loadingDisabled = false;
+        }
+    }
+
+    private void actOnNoVersionLoad() {
+        readVersion=versionPreffix+"0";
+        actOnVersionLoad();
+    }
+
+    private void backupOldFile(int version, File backingFile) {
+        try {
+            File backup = new File(backingFile.getAbsolutePath() + "." + version + BACKUP_SUFFIX);
+            OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, "backuping " + getBackingFile().getAbsolutePath() + " as " + backup.getName());
+            String warning = "- !WARNING! this is automated copy of old " + backingFile.getName() + " which was removed/replaced. Before you blindly copy those items back, please note, that this file might be modified without your approval by evil attacker. It is advised to not return below lines, or verify them before returning";
+            String s = FileUtils.loadFileAsString(backingFile);
+            s.replaceFirst("\\s*", "");
+            if (s.startsWith(versionPreffix)) {
+                s = s.replaceFirst("\n", " " + warning + "\n");
+            } else {
+                s = readVersion + " " + warning + "\n" + s;
+            }
+            FileUtils.saveFile(s, backup);
+        } catch (Exception ex) {
+            OutputController.getLogger().log(OutputController.Level.WARNING_ALL, "Error during backuping: " + ex.getMessage());
+            OutputController.getLogger().log(ex);
+        }
+    }
 }
--- a/netx/net/sourceforge/jnlp/util/UrlUtils.java	Mon Sep 07 20:11:55 2015 +0200
+++ b/netx/net/sourceforge/jnlp/util/UrlUtils.java	Tue Sep 08 13:38:12 2015 +0200
@@ -57,7 +57,7 @@
             URL strippedUrl = new URL(urlParts[0]); 
             return normalizeUrl(strippedUrl, encodeFileUrls);
         } catch (IOException | URISyntaxException e) {
-            OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
+            OutputController.getLogger().log(e);
         }
         return url;
     }
--- a/tests/netx/unit/net/sourceforge/jnlp/security/SecurityDialogsTest.java	Mon Sep 07 20:11:55 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/SecurityDialogsTest.java	Tue Sep 08 13:38:12 2015 +0200
@@ -54,6 +54,7 @@
 import net.sourceforge.jnlp.security.appletextendedsecurity.AppletSecurityLevel;
 import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteAppletAction;
 import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletTrustConfirmation;
+import net.sourceforge.jnlp.security.appletextendedsecurity.impl.UnsignedAppletActionStorageImpl;
 import net.sourceforge.jnlp.security.dialogs.apptrustwarningpanel.AppTrustWarningPanel.AppSigningWarningAction;
 import org.junit.After;
 import org.junit.AfterClass;
--- a/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java	Mon Sep 07 20:11:55 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/UnsignedAppletTrustConfirmationTest.java	Tue Sep 08 13:38:12 2015 +0200
@@ -128,6 +128,7 @@
                 Boolean.FALSE,
                 0);
         String s = FileUtils.loadFileAsString(PathsAndFiles.APPLET_TRUST_SETTINGS_USER.getFile());
+        s = s.replaceAll("#.*\n", ""); 
         Assert.assertTrue(s.startsWith("A"));
         Assert.assertTrue(s.contains(url41+url42));
         Assert.assertTrue(s.contains(surl1));
@@ -137,6 +138,7 @@
                 Boolean.TRUE,
                 0);
         s = FileUtils.loadFileAsString(PathsAndFiles.APPLET_TRUST_SETTINGS_USER.getFile());
+        s = s.replaceAll("#.*\n", "");
         Assert.assertTrue(s.startsWith("N"));
         Assert.assertFalse(s.contains(url41+url42));
         Assert.assertTrue(s.contains(surl1));        
--- a/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java	Mon Sep 07 20:11:55 2015 +0200
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/UnsignedAppletActionStorageImplTest.java	Tue Sep 08 13:38:12 2015 +0200
@@ -49,6 +49,8 @@
 
 public class UnsignedAppletActionStorageImplTest {
 
+    private static final String versionLine=UnsignedAppletActionStorageImpl.versionPreffix+UnsignedAppletActionStorageImpl.currentVersion+"\n";
+    
     private static File f1;
     private static File f2;
     private static File f3;
@@ -65,9 +67,9 @@
         f1 = File.createTempFile("itwMatching", "testFile1");
         f2 = File.createTempFile("itwMatching", "testFile2");
         f3 = File.createTempFile("itwMatching", "testFile3");
-        ServerAccess.saveFile("A 123456 .* .* jar1,jar2", f1);
-        ServerAccess.saveFile("N 123456 .* \\Qbla\\E jar1,jar2", f2);
-        ServerAccess.saveFile(""
+        ServerAccess.saveFile(versionLine + "A 123456 .* .* jar1,jar2", f1);
+        ServerAccess.saveFile(versionLine + "N 123456 .* \\Qbla\\E jar1,jar2", f2);
+        ServerAccess.saveFile(versionLine
                 + "A 1 \\Qhttp://jmol.sourceforge.net/demo/atoms/\\E \\Qhttp://jmol.sourceforge.net/jmol/\\E JmolApplet0.jar\n"
                 + "N 1363278653454 \\Qhttp://www.walter-fendt.de/ph14e\\E.* \\Qhttp://www.walter-fendt.de\\E.*\n"
                 + "n 1363281783104 \\Qhttp://www.walter-fendt.de/ph14e/inclplane.htm\\E \\Qhttp://www.walter-fendt.de/ph14_jar/\\E Ph14English.jar,SchiefeEbene.jar"
@@ -80,10 +82,10 @@
         ff2 = File.createTempFile("itwMatching", "testFile2");
         ff3 = File.createTempFile("itwMatching", "testFile3");
         ff4 = File.createTempFile("itwMatching", "testFile3");
-        ServerAccess.saveFile("AXn 123456 .* .* jar1,jar2", ff1);
-        ServerAccess.saveFile("XXXXXy 123456 .* \\Qbla\\E jar1,jar2", ff2);
-        ServerAccess.saveFile("XXXXXY 123456 .* \\Qbla\\E jar1,jar2", ff4);//errornous
-        ServerAccess.saveFile(""
+        ServerAccess.saveFile(versionLine + "AXn 123456 .* .* jar1,jar2", ff1);
+        ServerAccess.saveFile(versionLine + "XXXXXy 123456 .* \\Qbla\\E jar1,jar2", ff2);
+        ServerAccess.saveFile(versionLine + "XXXXXY 123456 .* \\Qbla\\E jar1,jar2", ff4);//errornous
+        ServerAccess.saveFile(versionLine
                 + "XA 1 \\Qa\\E \\Qb\\E jar1\n"
                 + "NNA 2 \\Qc\\E \\Qd\\E\n"
                 + "nyXyn 3 \\Qe\\E \\Qf\\E j1,j2"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/netx/unit/net/sourceforge/jnlp/security/appletextendedsecurity/impl/VersionRestrictionTest.java	Tue Sep 08 13:38:12 2015 +0200
@@ -0,0 +1,339 @@
+/*   Copyright (C) 2013 Red Hat, Inc.
+
+ This file is part of IcedTea.
+
+ IcedTea is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.
+
+ IcedTea is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with IcedTea; see the file COPYING.  If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version.
+ */
+package net.sourceforge.jnlp.security.appletextendedsecurity.impl;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import net.sourceforge.jnlp.ServerAccess;
+import net.sourceforge.jnlp.security.appletextendedsecurity.AppletSecurityActions;
+import net.sourceforge.jnlp.security.appletextendedsecurity.ExecuteAppletAction;
+import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletActionEntry;
+import net.sourceforge.jnlp.security.appletextendedsecurity.UrlRegEx;
+import net.sourceforge.jnlp.util.FileUtils;
+import net.sourceforge.jnlp.util.logging.NoStdOutErrTest;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VersionRestrictionTest extends NoStdOutErrTest {
+
+    private static File testFile;
+    private static final AppletSecurityActions asa = AppletSecurityActions.fromAction(0, ExecuteAppletAction.ALWAYS);
+    private static final UrlRegEx urx = UrlRegEx.quote("http://aa.bb/");
+    private static final List<String> archs = Arrays.asList("res.jar");
+    private static final UnsignedAppletActionEntry aq = new UnsignedAppletActionEntry(asa, new Date(1l), urx, urx, archs);
+
+    
+
+    @Before
+    public void preapreNewTestFile() throws IOException {
+        testFile = File.createTempFile("itwAES", "testFile");
+        testFile.deleteOnExit();
+    }
+
+    @After
+    public void removeAllPossibleBackupFiles() throws IOException {
+        File[] f = getBackupFiles();
+        for (File file : f) {
+            file.deleteOnExit();
+        }
+        for (File file : f) {
+            file.delete();
+        }
+        checkBackupFile(false);
+    }
+
+    private File[] getBackupFiles() {
+        File[] f = testFile.getParentFile().listFiles(new FilenameFilter() {
+
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.matches(testFile.getName() + "\\.[0123456789]+" + UnsignedAppletActionStorageImpl.BACKUP_SUFFIX);
+            }
+        });
+        return f;
+    }
+
+    private void checkBackupFile(boolean created) throws IOException {
+        checkBackupFile(created, 0);
+    }
+
+    private void checkBackupFile(boolean created, int expectedVersion) throws IOException {
+        File[] f = getBackupFiles();
+        if (!created) {
+            Assert.assertEquals("no backup should exists", 0, f.length);
+        } else {
+            Assert.assertEquals("there should be exactly one backup", 1, f.length);
+            Assert.assertTrue(f[0].getName().endsWith("." + expectedVersion + UnsignedAppletActionStorageImpl.BACKUP_SUFFIX));
+            String s = FileUtils.loadFileAsString(f[0]);
+            String l[] = s.split("\\n");
+            int hc = 0;
+            for (String string : l) {
+                string = string.trim();
+                if (string.startsWith(UnsignedAppletActionStorageImpl.versionPreffix)) {
+                    hc++;
+                    if (hc == 1) {
+                        Assert.assertTrue("first header must contains warning", string.contains("!WARNING!"));
+                    } else {
+                        Assert.assertFalse("only first header can contains warning", string.contains("!WARNING!"));
+                    }
+                }
+            }
+            Assert.assertTrue("at least one header must be in backup", hc > 0);
+        }
+    }
+
+    @Test
+    public void numberFormatExceptionInOnInLoad1() throws IOException {
+        ServerAccess.saveFile("#VERSION X\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 0);
+    }
+
+    @Test
+    public void numberFormatExceptionInOnInLoad2() throws IOException {
+        ServerAccess.saveFile("#VERSION\n"
+                + "cN:N{YES}; 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 0);
+    }
+
+    @Test
+    public void numberFormatExceptionInOnInLoad3() throws IOException {
+        ServerAccess.saveFile("#VERSION \n"
+                + "cN:N{YES}; 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 0);
+    }
+
+    @Test
+    public void numberFormatExceptionInOnInLoad4() throws IOException {
+        ServerAccess.saveFile("#VERSION                \n"
+                + "cN:N{YES}; 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 0);
+    }
+
+    @Test
+    public void correctLoad() throws IOException {
+        ServerAccess.saveFile("#VERSION 2\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(2, i1.items.size());
+        checkBackupFile(false);
+    }
+
+    @Test
+    public void correctLoad2() throws IOException {
+        ServerAccess.saveFile("#VERSION 2"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(2, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(3, i1.items.size());
+        checkBackupFile(false);
+    }
+
+    @Test
+    public void correctLoad3() throws IOException {
+        ServerAccess.saveFile("\n"
+                + "\n"
+                + "#VERSION 2"
+                + "\n"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(2, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(3, i1.items.size());
+        checkBackupFile(false);
+    }
+
+    @Test
+    public void firstVersionValidOnlyOK() throws IOException {
+        ServerAccess.saveFile("\n"
+                + "\n"
+                + "#VERSION 2"
+                + "\n"
+                + "#VERSION 1"
+                + "\n"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(2, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(3, i1.items.size());
+        checkBackupFile(false);
+    }
+
+    @Test
+    public void firstVersionValidOnlyBad() throws IOException {
+        ServerAccess.saveFile("\n"
+                + "\n"
+                + "#VERSION 1"
+                + "\n"
+                + "#VERSION 2"
+                + "\n"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 1);
+    }
+
+    @Test
+    public void laterVersionIgnored() throws IOException {
+        ServerAccess.saveFile("\n"
+                + "\n"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "#VERSION 2\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true);
+    }
+
+    @Test
+    public void incorrectLoad() throws IOException {
+        ServerAccess.saveFile("#VERSION 1\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 1);
+    }
+
+    @Test
+    public void incorrectLoad1() throws IOException {
+        ServerAccess.saveFile("#VERSION2\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 0);
+    }
+
+    @Test
+    public void incorrectLoad2() throws IOException {
+        ServerAccess.saveFile("#VERSION 1"
+                + "\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true, 1);
+    }
+
+    @Test
+    public void noVersionNoLoad() throws IOException {
+        ServerAccess.saveFile("\n"
+                + "N 1 \\Qhttp://some.url/\\E \\Qhttp://some.url/\\E jar.jar"
+                + "\n"
+                + "N 1 \\Qhttp://some2.url/\\E \\Qhttp://some2.url/\\E jar.jar", testFile);
+        UnsignedAppletActionStorageImpl i1 = new UnsignedAppletActionStorageImpl(testFile);
+        i1.readContents();
+        Assert.assertEquals(0, i1.items.size());
+        i1.add(aq);
+        i1.readContents();
+        Assert.assertEquals(1, i1.items.size());
+        checkBackupFile(true);
+    }
+}