You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2014/03/27 00:20:47 UTC
svn commit: r1582114 - in /jackrabbit/commons/filevault/trunk/vault-core/src:
main/java/org/apache/jackrabbit/vault/packaging/
main/java/org/apache/jackrabbit/vault/packaging/impl/
test/java/org/apache/jackrabbit/vault/packaging/integration/
Author: tripod
Date: Wed Mar 26 23:20:47 2014
New Revision: 1582114
URL: http://svn.apache.org/r1582114
Log:
JCRVLT-42 Do not allow creation of packages with illegal JCR names
- added checks for valid names when creating or uploading or renaming packages
- filter out packages with illegal names in list
Added:
jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestPackageCreation.java
Modified:
jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageId.java
jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java
Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageId.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageId.java?rev=1582114&r1=1582113&r2=1582114&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageId.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/PackageId.java Wed Mar 26 23:20:47 2014
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.vault.packaging;
+import org.apache.jackrabbit.util.XMLChar;
import org.apache.jackrabbit.vault.util.Text;
/**
@@ -117,8 +118,8 @@ public class PackageId implements Compar
this.version = Version.create(str.toString());
}
this.str = getString(group, this.name, version);
-
}
+
/**
* Creates a new package id
* @param path path of the package
@@ -395,4 +396,169 @@ public class PackageId implements Compar
return b.toString();
}
+ /**
+ * Checks if this package id is valid in respect to JCR names.
+ * @return {@code true} if the names are valid
+ */
+ public boolean isValid() {
+ return PackageId.isValid(group, name, version == null ? null : version.toString());
+ }
+
+ /**
+ * Checks if the package id is valid in respect to JCR names.
+ * @param group the package group name
+ * @param name the package name
+ * @param version the (optional) version
+ * @return {@code true} if the names are valid
+ */
+ public static boolean isValid(String group, String name, String version) {
+ try {
+ assertValidJcrName(name);
+ if (version != null && !version.isEmpty()) {
+ assertValidJcrName(version);
+ }
+ for (String groupSegment: Text.explode(group, '/')) {
+ assertValidJcrName(groupSegment);
+ }
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ // the code below is copied from org.apache.jackrabbit.spi.commons.conversion.NameParser
+
+ // constants for parser
+ private static final int STATE_PREFIX_START = 0;
+ private static final int STATE_PREFIX = 1;
+ private static final int STATE_NAME_START = 2;
+ private static final int STATE_NAME = 3;
+ private static final int STATE_URI_START = 4;
+ private static final int STATE_URI = 5;
+
+ /**
+ * Parses the <code>jcrName</code> (either qualified or expanded) and validates it.
+ * @throws java.lang.IllegalArgumentException if the name is not valid
+ */
+ private static void assertValidJcrName(String jcrName) throws IllegalArgumentException {
+ // trivial check
+ int len = jcrName == null ? 0 : jcrName.length();
+ if (len == 0) {
+ throw new IllegalArgumentException("empty name");
+ }
+ if (".".equals(jcrName) || "..".equals(jcrName)) {
+ throw new IllegalArgumentException(jcrName);
+ }
+
+ // parse the name
+ String prefix;
+ int nameStart = 0;
+ int state = STATE_PREFIX_START;
+ boolean trailingSpaces = false;
+
+ for (int i = 0; i < len; i++) {
+ char c = jcrName.charAt(i);
+ if (c == ':') {
+ if (state == STATE_PREFIX_START) {
+ throw new IllegalArgumentException("Prefix must not be empty");
+ } else if (state == STATE_PREFIX) {
+ if (trailingSpaces) {
+ throw new IllegalArgumentException("Trailing spaces not allowed");
+ }
+ prefix = jcrName.substring(0, i);
+ if (!XMLChar.isValidNCName(prefix)) {
+ throw new IllegalArgumentException("Invalid name prefix: "+ prefix);
+ }
+ state = STATE_NAME_START;
+ } else if (state == STATE_URI) {
+ // ignore -> validation of uri later on.
+ } else {
+ throw new IllegalArgumentException("'" + c + "' not allowed in name");
+ }
+ trailingSpaces = false;
+ } else if (c == ' ') {
+ if (state == STATE_PREFIX_START || state == STATE_NAME_START) {
+ throw new IllegalArgumentException("'" + c + "' not valid name start");
+ }
+ trailingSpaces = true;
+ } else if (Character.isWhitespace(c) || c == '[' || c == ']' || c == '*' || c == '|') {
+ throw new IllegalArgumentException("'" + c + "' not allowed in name");
+ } else if (c == '/') {
+ if (state == STATE_URI_START) {
+ state = STATE_URI;
+ } else if (state != STATE_URI) {
+ throw new IllegalArgumentException("'" + c + "' not allowed in name");
+ }
+ trailingSpaces = false;
+ } else if (c == '{') {
+ if (state == STATE_PREFIX_START) {
+ state = STATE_URI_START;
+ } else if (state == STATE_URI_START || state == STATE_URI) {
+ // second '{' in the uri-part -> no valid expanded jcr-name.
+ // therefore reset the nameStart and change state.
+ state = STATE_NAME;
+ nameStart = 0;
+ } else if (state == STATE_NAME_START) {
+ state = STATE_NAME;
+ nameStart = i;
+ }
+ trailingSpaces = false;
+ } else if (c == '}') {
+ if (state == STATE_URI_START || state == STATE_URI) {
+ String tmp = jcrName.substring(1, i);
+ if (tmp.length() == 0 || tmp.indexOf(':') != -1) {
+ // The leading "{...}" part is empty or contains
+ // a colon, so we treat it as a valid namespace URI.
+ // More detailed validity checks (is it well formed,
+ // registered, etc.) are not needed here.
+ state = STATE_NAME_START;
+ } else if (tmp.equals("internal")) {
+ // As a special Jackrabbit backwards compatibility
+ // feature, support {internal} as a valid URI prefix
+ state = STATE_NAME_START;
+ } else if (tmp.indexOf('/') == -1) {
+ // The leading "{...}" contains neither a colon nor
+ // a slash, so we can interpret it as a a part of a
+ // normal local name.
+ state = STATE_NAME;
+ nameStart = 0;
+ } else {
+ throw new IllegalArgumentException(
+ "The URI prefix of the name " + jcrName
+ + " is neither a valid URI nor a valid part"
+ + " of a local name.");
+ }
+ } else if (state == STATE_PREFIX_START) {
+ state = STATE_PREFIX; // prefix start -> validation later on will fail.
+ } else if (state == STATE_NAME_START) {
+ state = STATE_NAME;
+ nameStart = i;
+ }
+ trailingSpaces = false;
+ } else {
+ if (state == STATE_PREFIX_START) {
+ state = STATE_PREFIX; // prefix start
+ } else if (state == STATE_NAME_START) {
+ state = STATE_NAME;
+ nameStart = i;
+ } else if (state == STATE_URI_START) {
+ state = STATE_URI;
+ }
+ trailingSpaces = false;
+ }
+ }
+
+ // take care of qualified jcrNames starting with '{' that are not having
+ // a terminating '}' -> make sure there are no illegal characters present.
+ if (state == STATE_URI && (jcrName.indexOf(':') > -1 || jcrName.indexOf('/') > -1)) {
+ throw new IllegalArgumentException("Local name may not contain ':' nor '/'");
+ }
+
+ if (nameStart == len || state == STATE_NAME_START) {
+ throw new IllegalArgumentException("Local name must not be empty");
+ }
+ if (trailingSpaces) {
+ throw new IllegalArgumentException("Trailing spaces not allowed");
+ }
+ }
}
\ No newline at end of file
Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java?rev=1582114&r1=1582113&r2=1582114&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java Wed Mar 26 23:20:47 2014
@@ -37,6 +37,8 @@ import javax.jcr.Session;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.spi.commons.conversion.NameParser;
+import org.apache.jackrabbit.spi.commons.conversion.PathParser;
import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.fs.config.MetaInf;
@@ -208,6 +210,9 @@ public class JcrPackageManagerImpl exten
bin.dispose();
throw new IOException("Package does not contain a path specification or valid package id.");
}
+ if (!pid.isValid()) {
+ throw new RepositoryException("Unable to create package. Illegal package name.");
+ }
// create parent node
String path = pid.getInstallationPath() + ".zip";
@@ -285,6 +290,10 @@ public class JcrPackageManagerImpl exten
}
pid = new PackageId(nameHint);
}
+ if (!pid.isValid()) {
+ throw new RepositoryException("Unable to create package. Illegal package name.");
+ }
+
// create parent node
String path = pid.getInstallationPath() + ".zip";
String parentPath = Text.getRelativeParent(path, 1);
@@ -344,6 +353,7 @@ public class JcrPackageManagerImpl exten
throws RepositoryException, IOException {
return create(group, name, null);
}
+
/**
* {@inheritDoc}
*/
@@ -354,6 +364,9 @@ public class JcrPackageManagerImpl exten
if (ext.equals("zip") || ext.equals("jar")) {
name = name.substring(0, name.length() - 4);
}
+ if (!PackageId.isValid(group, name, version)) {
+ throw new RepositoryException("Unable to create package. Illegal package name.");
+ }
PackageId pid = new PackageId(group, name, version);
Node folder = mkdir(Text.getRelativeParent(pid.getInstallationPath(), 1), false);
try {
@@ -394,6 +407,10 @@ public class JcrPackageManagerImpl exten
if (pack.getSize() > 0 && !pack.getDefinition().isUnwrapped()) {
throw new PackageException("Package definition not unwrapped.");
}
+ if (!PackageId.isValid(group, name, version)) {
+ throw new RepositoryException("Unable to rename package. Illegal package name.");
+ }
+
JcrPackageDefinition def = pack.getDefinition();
PackageId id = def.getId();
PackageId newId = new PackageId(
@@ -734,6 +751,11 @@ public class JcrPackageManagerImpl exten
}
JcrPackageImpl pack = new JcrPackageImpl(child);
if (pack.isValid()) {
+ // skip packages with illegal names
+ JcrPackageDefinition jDef = pack.getDefinition();
+ if (jDef != null && !jDef.getId().isValid()) {
+ continue;
+ }
if (filter == null || filter.contains(child.getPath())) {
if (!built || pack.getSize() > 0) {
packages.add(pack);
Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestPackageCreation.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestPackageCreation.java?rev=1582114&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestPackageCreation.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/TestPackageCreation.java Wed Mar 26 23:20:47 2014
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.packaging.integration;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+/**
+ * <code>TestPackageInstall</code>...
+ */
+public class TestPackageCreation extends IntegrationTestBase {
+
+ public static String[] GROUP_NAMES = {"foo", "foo-zoo", "foo.zoo", "jcr:foo", "foo/zoo"};
+ public static String[] ILLEGAL_GROUP_NAMES = {"foo ", " foo", ":foo"};
+
+ public static String[] PACKAGE_NAMES = {"bar", "bar.zar", "bar-zar", "jcr:bar"};
+ public static String[] ILLEGAL_PACKAGE_NAMES = {"bar ", " bar", ":bar", "jcr/bar"};
+
+ public static String[] ILLEGAL_VERSION_NAMES = {"1.0 ", " 1.0", ":1.0", "jcr:1.0", "1/0"};
+
+ @Test
+ public void testCreateGroup() throws RepositoryException, IOException, PackageException {
+ for (String name: GROUP_NAMES) {
+ packMgr.create(name, "bar");
+ assertNodeExists("/etc/packages/" + name + "/bar.zip");
+ }
+ }
+
+ @Test
+ public void testCreate() throws RepositoryException, IOException, PackageException {
+ for (String name: PACKAGE_NAMES) {
+ packMgr.create("foo", name);
+ assertNodeExists("/etc/packages/foo/" + name + ".zip");
+ }
+ }
+
+ @Test
+ public void testCreateWithVersion() throws RepositoryException, IOException, PackageException {
+ packMgr.create("foo", "bar", "3.1.2");
+ assertNodeExists("/etc/packages/foo/bar-3.1.2.zip");
+ }
+
+ @Test
+ public void testCreateIllegalGroup() throws RepositoryException, IOException, PackageException {
+ for (String name: ILLEGAL_GROUP_NAMES) {
+ try {
+ packMgr.create(name, "bar", "3.1.2");
+ fail("Illegal group name must fail: " + name);
+ } catch (RepositoryException e) {
+ // ok
+ }
+ }
+ }
+
+ @Test
+ public void testCreateIllegalName() throws RepositoryException, IOException, PackageException {
+ for (String name: ILLEGAL_PACKAGE_NAMES) {
+ try {
+ packMgr.create("foo", name, "3.1.2");
+ fail("Illegal package name must fail: " + name);
+ } catch (RepositoryException e) {
+ // ok
+ }
+ }
+ }
+
+ @Test
+ public void testCreateIllegalVersions() throws RepositoryException, IOException, PackageException {
+ for (String name: ILLEGAL_VERSION_NAMES) {
+ try {
+ packMgr.create("foo", "bar", name);
+ fail("Illegal version must fail: " + name);
+ } catch (RepositoryException e) {
+ // ok
+ }
+ }
+ }
+
+ @Test
+ public void testUploadIllegal() throws RepositoryException, IOException, PackageException {
+ try {
+ packMgr.upload(getStream("testpackages/tmp_illegal.zip"), false);
+ fail("Uploading a package with an illegal name must fail.");
+ } catch (RepositoryException e) {
+ // ok
+ }
+ }
+
+}
\ No newline at end of file