Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which specific packages are sealed when sealing a .jar?

Tags:

java

security

When sealing a .jar file (the whole .jar, not specific packages), which packages are actually sealed? Is it only the packages that contain .class files, or does it also include parent packages and sub-packages?

To give an example, say I have a .jar containing the single .class file com.company.city.london.class, is it only the com.company.city package that is sealed?

Would the JVM allow me to create and instantiate the class com.company.city.building.house.class outside of the .jar?

Would the JVM allow me to create and instantiate the class com.company.world.class outside of the .jar?

like image 359
Mark Douglas Avatar asked Feb 23 '17 11:02

Mark Douglas


1 Answers

OK, after writing a test application, I have the answers. And they weren't quite what I had expected after reading the documentation.

I have the following two classes packaged into a .jar that has been sealed:

TestClass.java:

package com.company.testjar;

public class TestClass {
}

TestClass2.java:

package com.company.testjar2;

public class TestClass2 {
}

The .jar manifest looks like this:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.3
Created-By: 1.8.0_40-b26 (Oracle Corporation)
Implementation-Vendor: Company
Implementation-Title: Test Jar
Implementation-Version: 6.4.0.0
Sealed: true

According to the documentation, sealing the entire .jar seals ALL packages within the .jar. However, this statement is ambiguous as I found out.

I then wrote some JUnit test cases to see what other classes I could define without causing issues with the sealed .jar.

For my unit tests, I also added the following three test classes. Note that these are NOT defined in the .jar, but do use the same package structure - this is important for the tests.

Bogus.java:

package com.company.testjar;

public class Bogus {
}

SubBogus.java

package com.company.testjar.subpackage;

public class SubBogus {
}

ParentBogus.java:

package com.company;

public class ParentBogus {
}

The JUnit tests:

package com.company.test;

import static org.junit.Assert.*;

import org.junit.Test;

import com.company.ParentBogus;
import com.company.testjar.Bogus;
import com.company.testjar.TestClass;
import com.company.testjar.subpackage.SubBogus;
import com.company.testjar2.TestClass2;

/**
 * A set of tests for testing the effects of .jar sealing.
 * 
 * These tests rely on a built .jar named TestJar.jar which is built from the command line.
 * 
 * Only one of these tests can be run at a time because one a package has been loaded, it cannot
 * be unloaded again. Because of this, each test must be run separately.
 */
public class TestPackages {

    @Test
    public void SealedFail1() {
        // Instantiate instance of TestClass from sealed .jar.
        TestClass t = new TestClass();

        // Following should blow up because package com.company.testjar has already
        // been loaded by instantiating TestClass above.
        try {
            new Bogus();

            // Should not get here. Throw if we do.
            assertFalse(true);
        } catch (SecurityException ex) {
            // Expected.
        }
    }

    @Test
    public void SealedFail2() {
        Bogus b = new Bogus();

        // Following should blow up because package com.company.testjar has already
        // been loaded by instantiating Bogus above.
        try {
            new TestClass();

            // Should not get here. Throw if we do.
            assertFalse(true);
        } catch (SecurityException ex) {
            // Expected.
        }
    }

    /**
     * Test to see if instantiating object from package in a sealed .jar will effectively
     * load and seal all packages in that .jar.
     */
    @Test
    public void SealedFail3() {
        // Instantiate object from com.company.testjar2 package. This package will now be
        // loaded and sealed.
        TestClass2 t2 = new TestClass2();

        // Instantiate object from com.company.testjar package NOT from sealed .jar.
        // This should work because this package has not been sealed yet!
        Bogus b = new Bogus();

        // This should now throw because the com.company.testjar package has already
        // been loaded by instantiating Bogus above, and the TestClass is from that
        // same package from the sealed .jar.
        try {
            new TestClass();

            // Should not get here. Throw if we do.
            assertFalse(true);
        } catch (SecurityException ex) {
            // Expected.
        }
    }

    /**
     * Test to see if instantiating an object from a sealed .jar prevents us from then
     * instantiating an instance of an object from a sub-package NOT defined in the
     * same .jar.
     */
    @Test
    public void SubPackage() {
        // Instantiate instance of TestClass from sealed .jar. Loads and seals the
        // com.company.testjar package.
        TestClass t = new TestClass();

        // Now attempt to instantiate an instance of an object from a sub-package of
        // com.company.testjar which is not defined in the same .jar.
        new SubBogus();
    }

    /**
     * Test to see if instantiating an object from a sealed .jar prevents us from then
     * instantiating an instance of an object from a parent package NOT defined in the
     * same .jar.
     */
    @Test
    public void ParentPackage() {
        // Instantiate instance of TestClass from sealed .jar. Loads and seals the
        // com.company.testjar package.
        TestClass t = new TestClass();

        // Now attempt to instantiate an instance of an object from the parent-package of
        // com.company.testjar which is not defined in the same .jar.
        new ParentBogus();
    }
}

The individual tests should be run independently because once a package has been loaded, I'm not unloading it again and would affect the result of the tests. If you run all the tests in once go, there will be failures because packages are loaded by the first test and stay loaded.

All of the tests pass when run individually.

From these tests, we can determine the following:

  1. Sealing an entire .jar does not seal the empty parent packages. So the following empty packages are not sealed : 'com' and 'com.company'. Only packages that contain classes are sealed. See test ParentPackage().
  2. If you load a package from a .jar by instantiating a class from it, and then attempt load a class from the same package external to the .jar, this will fail. See test SealedFail1().
  3. If you load a package externally from the .jar by instantiating a class that shares the same package name as a .class within the .jar, attempting to then instantiate a class from the sealed .jar from that same package will fail. See test SealedFail2().
  4. Instantiating an object from a sealed .jar only loads (and seals) the package that that specific class is located in. No other packages from the same .jar are loaded at the same time. See test SealedFail3().
  5. You can successfully instantiate objects defined in a sub-package of an already sealed and loaded package from a .jar without issue. See test SubPackage().
like image 196
Mark Douglas Avatar answered Oct 22 '22 05:10

Mark Douglas