Skip to content

Commit

Permalink
Merge pull request #31 from Gaoithe/fix-jenkins-23234_ignore_symlinks
Browse files Browse the repository at this point in the history
[JENKINS-23234] Prevent infinite recursion on symlink resolution during CVS checkout
  • Loading branch information
mc1arke committed Nov 10, 2014
2 parents 7c5f8e9 + 273ee9b commit 496b0f2
Showing 1 changed file with 52 additions and 8 deletions.
60 changes: 52 additions & 8 deletions src/main/java/hudson/scm/AbstractCvs.java
Expand Up @@ -75,6 +75,8 @@
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

Expand Down Expand Up @@ -273,7 +275,7 @@ public Boolean invoke(final File workspace, final VirtualChannel channel) throws
try {
File moduleDir = new File(workspace, moduleName);
if (moduleDir.isDirectory()) {
pruneEmptyDirectories(moduleDir);
pruneEmptyDirectories(moduleDir,listener);
}
} catch (IOException e) {
e.printStackTrace(listener.error("CVS empty directory cleanup failed: " + e.getMessage()));
Expand Down Expand Up @@ -312,7 +314,7 @@ public Boolean invoke(final File workspace, final VirtualChannel channel) throws
* Cannot easily call the method reflectively on the {@link Command} since {@link CheckoutCommand} has a different signature for it.
* Pending a fix in the client library, do it ourselves when necessary.
*/
private static void pruneEmptyDirectories(File d) throws IOException {
private static void pruneEmptyDirectories(File d, final TaskListener listener) throws IOException {
File[] kids = d.listFiles();
if (kids == null) {
throw new IOException("could not examine " + d);
Expand All @@ -325,7 +327,13 @@ private static void pruneEmptyDirectories(File d) throws IOException {
// not CVS-controlled, ignore
continue;
}
pruneEmptyDirectories(kid);

if (isSymLink(kid,listener)) {
listener.getLogger().println("pruneEmptyDirectories. prevent potential infinate loop, ignoring symlink:" + kid);
continue;
}

pruneEmptyDirectories(kid,listener);
File[] subkids = kid.listFiles();
if (subkids != null && subkids.length == 1) {
// Just CVS.
Expand Down Expand Up @@ -810,7 +818,7 @@ protected void postCheckout(AbstractBuild<?, ?> build, File changelogFile, CvsRe
}

// add the current workspace state as an action
build.getActions().add(new CvsRevisionState(calculateWorkspaceState(workspace, repositories, flatten, envVars)));
build.getActions().add(new CvsRevisionState(calculateWorkspaceState(workspace, repositories, flatten, envVars, listener)));

// add the tag action to the build
build.getActions().add(new CvsTagAction(build, this));
Expand Down Expand Up @@ -850,7 +858,12 @@ private void cleanup(File directory, AdminHandler adminHandler) throws IOExcepti

File[] innerFiles = directory.listFiles();
if (null != innerFiles) {

for (File innerFile : innerFiles) {
if (isSymLink(innerFile,listener)) {
listener.getLogger().println("cleanup. prevent potential infinate loop, ignoring symlink:" + innerFile);
continue;
}
if (innerFile.isDirectory() && !innerFile.getName().equals("CVS")) {
cleanup(innerFile, adminHandler);
}
Expand All @@ -877,15 +890,16 @@ protected Date getCheckoutDate(AbstractBuild<?, ?> build) {

private Map<CvsRepository, List<CvsFile>> calculateWorkspaceState(final FilePath workspace,
final CvsRepository[] repositories,
final boolean flatten, final EnvVars envVars)
final boolean flatten, final EnvVars envVars,
final TaskListener listener)
throws IOException, InterruptedException {
Map<CvsRepository, List<CvsFile>> workspaceState = new HashMap<CvsRepository, List<CvsFile>>();

for (CvsRepository repository : repositories) {
List<CvsFile> cvsFiles = new ArrayList<CvsFile>();
for (CvsRepositoryItem item : repository.getRepositoryItems()) {
for (CvsModule module : item.getModules()) {
cvsFiles.addAll(getCvsFiles(workspace, module, flatten, envVars));
cvsFiles.addAll(getCvsFiles(workspace, module, flatten, envVars, listener));
}
}
workspaceState.put(repository, cvsFiles);
Expand All @@ -894,8 +908,34 @@ private Map<CvsRepository, List<CvsFile>> calculateWorkspaceState(final FilePath
return workspaceState;
}

/**
* Check if the given file is a symbolic link. Useful for preventing CSV recursing into directories infinitely.
* @param file name of the file to test
* @return whether the file if believed to be a symlink or not
*/
public static boolean isSymLink(File file, final TaskListener listener) {
if (file == null) {
return false;
}
try {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
File canonDir = file.getParentFile().getCanonicalFile();
canon = new File(canonDir, file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
} catch (IOException ex) {
ex.printStackTrace(listener.error("Ignoring exception when checking for symlink. file:" +
file + " exception:" + ex.getMessage()));
}
return false;
}


private List<CvsFile> getCvsFiles(final FilePath workspace, final CvsModule module, final boolean flatten,
final EnvVars envVars)
final EnvVars envVars, final TaskListener listener)
throws IOException, InterruptedException {
FilePath targetWorkspace;
if (flatten) {
Expand Down Expand Up @@ -947,7 +987,11 @@ public List<CvsFile> buildFileList(final File moduleLocation, final String prefi
if (directoryFiles != null) {
for (File file : directoryFiles) {
if (file.isDirectory()) {
fileList.addAll(buildFileList(file, prefix + "/" + file.getName()));
if (!isSymLink(file,listener)) {
fileList.addAll(buildFileList(file, prefix + "/" + file.getName()));
} else {
listener.getLogger().println("buildFileList. prevent potential infinate loop, ignoring symlink:" + file);
}
}
}
}
Expand Down

0 comments on commit 496b0f2

Please sign in to comment.