Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-42680] Prevent infinite loop when a Folder is a child …
…of an AbstractFolder
  • Loading branch information
stephenc committed Mar 16, 2017
1 parent f78d9b4 commit 22f56ef
Showing 1 changed file with 83 additions and 30 deletions.
Expand Up @@ -24,6 +24,7 @@

package com.cloudbees.hudson.plugins.folder.relocate;

import com.cloudbees.hudson.plugins.folder.AbstractFolder;
import com.cloudbees.hudson.plugins.folder.Folder;
import hudson.Extension;
import hudson.model.AbstractItem;
Expand Down Expand Up @@ -51,7 +52,10 @@
@Extension(ordinal=-1000) public final class StandardHandler extends RelocationHandler {

public HandlingMode applicability(Item item) {
if (item instanceof TopLevelItem && item instanceof AbstractItem && item.getParent() instanceof DirectlyModifiableTopLevelItemGroup && !validDestinations(item).isEmpty()) {
if (item instanceof TopLevelItem
&& item instanceof AbstractItem
&& item.getParent() instanceof DirectlyModifiableTopLevelItemGroup
&& hasValidDestination(item)) {
return HandlingMode.HANDLE;
} else {
return HandlingMode.SKIP;
Expand All @@ -73,7 +77,54 @@ private static <I extends AbstractItem & TopLevelItem> I doMove(Item item, Direc
return Items.move((I) item, destination);
}

@Override public List<? extends ItemGroup<?>> validDestinations(Item item) {
public boolean hasValidDestination(Item item) {
Jenkins instance = Jenkins.getActiveInstance();
if (permitted(item, instance) && instance.getItem(item.getName()) == null) {
// we can move to the root if there is none with the same name.
return true;
}
ITEM: for (Item g : instance.getAllItems()) {
if (g instanceof DirectlyModifiableTopLevelItemGroup) {
DirectlyModifiableTopLevelItemGroup itemGroup = (DirectlyModifiableTopLevelItemGroup) g;
if (!permitted(item, itemGroup)) {
continue;
}
// Cannot move a folder into itself or a descendant
if (g == item) {
continue;
}
// Cannot move an item into a Folder if there is already an item with the same name
if (g instanceof Folder) {
// NOTE: test for Folder not AbstractFolder as Folder is mutable by users
Folder folder = (Folder) g;
if (folder.getItem(item.getName()) != null) {
continue;
}
}
// Cannot move a folder into a descendant
// Cannot move d1/ into say d1/d2/d3/
ItemGroup itemGroupSubElement = g.getParent();
while (itemGroupSubElement != instance) {
if (item == itemGroupSubElement) {
continue ITEM;
}
if (itemGroupSubElement instanceof Item) {
itemGroupSubElement = ((Item) itemGroupSubElement).getParent();
} else {
// should never get here as this is an ItemGroup resolved from the root of Jenkins,
// so there should be a parent chain ending at Jenkins but *if* we do end up here,
// safer to say this one is not safe to move into and break the infinite loop
continue ITEM;
}
}
return true;
}
}
return false;
}

@Override
public List<? extends ItemGroup<?>> validDestinations(Item item) {
List<DirectlyModifiableTopLevelItemGroup> result = new ArrayList<DirectlyModifiableTopLevelItemGroup>();
Jenkins instance = Jenkins.getActiveInstance();
// ROOT context is only added in case there is not any item with the same name
Expand All @@ -88,42 +139,44 @@ private static <I extends AbstractItem & TopLevelItem> I doMove(Item item, Direc
if (!permitted(item, itemGroup)) {
continue;
}
ItemGroup<?> p = itemGroup;
if (p instanceof Item) {
Item i = (Item) p;
// Cannot move a folder into itself or a descendant
if (i == item) {
continue ITEM;
}
// By default the move is a no-op in case you hit it by mistake
if (item.getParent() == i) {
result.add(itemGroup);
// Cannot move a folder into itself or a descendant
if (g == item) {
continue;
}
// By default the move is a no-op in case you hit it by mistake
if (item.getParent() == g) {
result.add(itemGroup);
}
// Cannot move an item into a Folder if there is already an item with the same name
if (g instanceof Folder) {
// NOTE: test for Folder not AbstractFolder as Folder is mutable by users
Folder folder = (Folder) g;
if (folder.getItem(item.getName()) != null) {
continue;
}
// Cannot move an item into a Folder if there is already an item with the same name
if (i instanceof Folder) {
Folder folder = (Folder) i;
if (folder.getItem(item.getName()) != null) {
continue ITEM;
}
}
// Cannot move a folder into a descendant
// Cannot move d1/ into say d1/d2/d3/
ItemGroup itemGroupSubElement = g.getParent();
while (itemGroupSubElement != instance) {
if (item == itemGroupSubElement) {
continue ITEM;
}
// Cannot move a folder into a descendant
// Cannot move d1/ into say d1/d2/d3/
ItemGroup itemGroupSubElement = i.getParent();
while (itemGroupSubElement != instance) {
if (itemGroupSubElement instanceof Folder) {
Folder currentFolder = (Folder) itemGroupSubElement;
if (item == currentFolder) {
continue ITEM;
}
itemGroupSubElement = currentFolder.getParent();
}
if (itemGroupSubElement instanceof Item) {
itemGroupSubElement = ((Item) itemGroupSubElement).getParent();
} else {
// should never get here as this is an ItemGroup resolved from the root of Jenkins,
// so there should be a parent chain ending at Jenkins but *if* we do end up here,
// safer to say this one is not safe to move into and break the infinite loop
continue ITEM;
}
result.add(itemGroup);
}
result.add(itemGroup);
}
}
return result;
}

private boolean permitted(Item item, DirectlyModifiableTopLevelItemGroup itemGroup) {
return itemGroup == item.getParent() || itemGroup.canAdd((TopLevelItem) item) && ((AccessControlled) itemGroup).hasPermission(Job.CREATE);
}
Expand Down

0 comments on commit 22f56ef

Please sign in to comment.