diff --git a/.travis.yml b/.travis.yml
index 7d0a65e560..92bb524dcd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,6 +18,7 @@ branches:
only:
- master
- /support\/.*/
+ - fix/MNT-21009-arbitrary-codeExecution
stages:
- test
@@ -110,7 +111,7 @@ jobs:
script: travis_wait 20 mvn test -B -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- stage: release
name: "Push to Nexus"
- if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request AND commit_message !~ /\[no-release\]/
+ if: fork = false AND (branch = master OR branch =~ /support\/.*/ OR branch = fix/MNT-21009-arbitrary-codeExecution) AND type != pull_request AND commit_message !~ /\[no-release\]/
before_install:
- "cp .travis.settings.xml $HOME/.m2/settings.xml"
script:
@@ -119,4 +120,4 @@ jobs:
# Add email to link commits to user
- git config user.email "${GIT_EMAIL}"
# Skip building of release commits
- - mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform
+ - mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests -DreleaseVersion=MNT-21009 -DdevelopmentVersion=8.22-SNAPSHOT release:clean release:prepare release:perform
diff --git a/pom.xml b/pom.xml
index 92fc45cbb6..e20cc3d3fa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,6 @@
Alfresco Repository
8.24-SNAPSHOT
jar
-
org.alfresco
alfresco-super-pom
diff --git a/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java b/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java
index 120bd1f6d6..7f06ac4f95 100644
--- a/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java
+++ b/src/main/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java
@@ -79,7 +79,10 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
private static final String PATH_CLASSPATH = "classpath:";
/** Wrap Factory */
- private static final WrapFactory wrapFactory = new RhinoWrapFactory();
+ private static final WrapFactory wrapFactory = new RhinoWrapFactory();
+
+ /** Sandbox Wrap Factory */
+ private static final SandboxWrapFactory sandboxFactory = new SandboxWrapFactory();
/** Base Value Converter */
private final ValueConverter valueConverter = new ValueConverter();
@@ -453,14 +456,15 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
{
// Create a thread-specific scope from one of the shared scopes.
// See http://www.mozilla.org/rhino/scopes.html
- cx.setWrapFactory(wrapFactory);
+ cx.setWrapFactory(secure ? wrapFactory : sandboxFactory);
Scriptable scope;
if (this.shareSealedScopes)
{
Scriptable sharedScope = secure ? this.nonSecureScope : this.secureScope;
scope = cx.newObject(sharedScope);
scope.setPrototype(sharedScope);
- scope.setParentScope(null);
+ scope.setParentScope(null);
+
}
else
{
@@ -578,23 +582,44 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
/**
- * Rhino script value wraper
+ * Rhino script value wrapper
*/
private static class RhinoWrapFactory extends WrapFactory
- {
+ {
+ protected Scriptable wrapBasicJavaObject(Context cx, Scriptable scope, Object javaObject, Class> staticType)
+ {
+ return super.wrapAsJavaObject(cx, scope, javaObject, staticType);
+ }
+
/* (non-Javadoc)
* @see org.mozilla.javascript.WrapFactory#wrapAsJavaObject(org.mozilla.javascript.Context, org.mozilla.javascript.Scriptable, java.lang.Object, java.lang.Class)
*/
- public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class staticType)
- {
- if (javaObject instanceof Map && !(javaObject instanceof ScriptableHashMap))
- {
- return new NativeMap(scope, (Map)javaObject);
- }
- return super.wrapAsJavaObject(cx, scope, javaObject, staticType);
- }
+ public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class> staticType)
+ {
+ if (javaObject instanceof Map && !(javaObject instanceof ScriptableHashMap))
+ {
+ return new NativeMap(scope, (Map) javaObject);
+ }
+ return wrapBasicJavaObject(cx, scope, javaObject, staticType);
+ }
+ }
+
+ /**
+ * A {@link WrapFactory} that ensures {@link org.mozilla.javascript.NativeJavaObject} instances are of the
+ * {@link SandboxNativeJavaObject} variety.
+ */
+ private static class SandboxWrapFactory extends RhinoWrapFactory
+ {
+ /* (non-Javadoc)
+ * @see org.mozilla.javascript.WrapFactory#wrapAsJavaObject(org.mozilla.javascript.Context, org.mozilla.javascript.Scriptable, java.lang.Object, java.lang.Class)
+ */
+ @Override
+ protected Scriptable wrapBasicJavaObject(Context cx, Scriptable scope, Object javaObject, Class> staticType)
+ {
+ return new SandboxNativeJavaObject(scope, javaObject, staticType);
+ }
+
}
-
/**
* Pre initializes two scope objects (one secure and one not) with the standard objects preinitialised.
@@ -621,7 +646,7 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
cx = Context.enter();
try
{
- cx.setWrapFactory(wrapFactory);
+ cx.setWrapFactory(sandboxFactory);
this.nonSecureScope = initScope(cx, true, true);
}
finally
@@ -643,27 +668,23 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
* is to be reused.
* @return the scope object
*/
- protected Scriptable initScope(Context cx, boolean secure, boolean sealed)
- {
- Scriptable scope;
- if (secure)
- {
- // Initialise the non-secure scope
- // allow access to all libraries and objects, including the importer
- // @see http://www.mozilla.org/rhino/ScriptingJava.html
- scope = new ImporterTopLevel(cx, sealed);
- }
- else
- {
- // Initialise the secure scope
- scope = cx.initStandardObjects(null, sealed);
- // remove security issue related objects - this ensures the script may not access
- // unsecure java.* libraries or import any other classes for direct access - only
- // the configured root host objects will be available to the script writer
- scope.delete("Packages");
- scope.delete("getClass");
- scope.delete("java");
- }
- return scope;
+ protected Scriptable initScope(Context cx, boolean secure, boolean sealed)
+ {
+ Scriptable scope;
+ if (secure)
+ {
+ // Initialise the non-secure scope
+ // allow access to all libraries and objects, including the importer
+ // @see http://www.mozilla.org/rhino/ScriptingJava.html
+ scope = new ImporterTopLevel(cx, sealed);
+ }
+ else
+ {
+ // Initialise the secure scope
+ // This sets up "scope" to have access to all the standard JavaScript classes, but does not create global objects for any top-level Java packages.
+ // In addition, the "Packages," "JavaAdapter," and "JavaImporter" classes, and the "getClass" function, are not initialized.
+ scope = cx.initSafeStandardObjects(null, sealed);
+ }
+ return scope;
}
}
\ No newline at end of file
diff --git a/src/main/java/org/alfresco/repo/jscript/SandboxNativeJavaObject.java b/src/main/java/org/alfresco/repo/jscript/SandboxNativeJavaObject.java
new file mode 100644
index 0000000000..6527824ba9
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/jscript/SandboxNativeJavaObject.java
@@ -0,0 +1,52 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.repo.jscript;
+
+import org.mozilla.javascript.NativeJavaObject;
+import org.mozilla.javascript.Scriptable;
+
+
+/**
+ * A sandboxed {@link NativeJavaObject} that prevents using reflection to escape a sandbox.
+ */
+public class SandboxNativeJavaObject extends NativeJavaObject
+{
+ public SandboxNativeJavaObject(Scriptable scope, Object javaObject, Class> staticType)
+ {
+ super(scope, javaObject, staticType);
+ }
+
+ @Override
+ public Object get(String name, Scriptable start)
+ {
+ if ("getClass".equals(name))
+ {
+ return NOT_FOUND;
+ }
+ return super.get(name, start);
+ }
+
+}
diff --git a/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java
index 23c703c10f..8d7fb6f282 100644
--- a/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java
+++ b/src/test/java/org/alfresco/repo/jscript/RhinoScriptTest.java
@@ -25,19 +25,24 @@
*/
package org.alfresco.repo.jscript;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import junit.framework.TestCase;
-
+import junit.framework.TestCase;
+
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.dictionary.DictionaryComponent;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
-import org.alfresco.repo.node.BaseNodeServiceTest;
-import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.node.BaseNodeServiceTest;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -49,13 +54,14 @@ import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
-import org.alfresco.test_category.OwnJVMTestsCategory;
-import org.alfresco.util.ApplicationContextHelper;
+import org.alfresco.test_category.OwnJVMTestsCategory;
+import org.alfresco.util.ApplicationContextHelper;
import org.junit.experimental.categories.Category;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
-import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContext;
+
/**
* @author Kevin Roast
@@ -358,6 +364,75 @@ public class RhinoScriptTest extends TestCase
return null;
}
});
+ }
+
+ // MNT-21009
+ public void testUnsecureScriptAddedOnRepoNode()
+ {
+
+ transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback