VYPR
Medium severity6.5NVD Advisory· Published Feb 9, 2017· Updated May 13, 2026

CVE-2016-4987

CVE-2016-4987

Description

Directory traversal vulnerability in the Image Gallery plugin before 1.4 in Jenkins allows remote attackers to list arbitrary directories and read arbitrary files via unspecified form fields.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
com.tupilabs.image_gallery:image-galleryMaven
< 1.41.4

Affected products

1

Patches

1
20f02f6d53e6

[SECURITY-278] CVE-2016-4987 prevent information disclosure

https://github.com/jenkinsci/image-gallery-pluginBruno P. KinoshitaJun 20, 2016via ghsa
2 files changed · +65 24
  • src/main/java/org/jenkinsci/plugins/imagegallery/comparative/ComparativeArchivedImagesGallery.java+28 12 modified
    @@ -23,6 +23,8 @@
      */
     package org.jenkinsci.plugins.imagegallery.comparative;
     
    +import java.nio.file.Path;
    +import java.nio.file.Paths;
     import java.util.ArrayList;
     import java.util.List;
     
    @@ -141,16 +143,30 @@ public FormValidation doCheckImageInnerWidth(StaplerRequest req, StaplerResponse
     
     	}
     
    -	protected List<String> getRelativeFrom(FilePath file, FilePath parent) {
    -		List<String> path = new ArrayList<String>();
    -		FilePath temp = file;
    -		while(temp.getParent() != null && !temp.getParent().equals(parent)) {
    -			path.add(0,temp.getParent().getName());
    -			temp = temp.getParent();
    -		}
    -		path.add(file.getName());
    -		return path;
    -	}
    -	
    -	
    +    /**
    +     * <p>Retrieve an array with the directories required to go from parent to file. Like in a network where you can
    +     * trace for hops.</p>
    +     *
    +     * <p>For example, from /home/silb/archives to /home/silb/archives/images/1.png, the method will return an array
    +     * with images and 1.png.</p>
    +     * @param file target file
    +     * @param parent parent file
    +     * @return an array with directories to go from {@code parent} to {@code file}.
    +     */
    +    protected List<String> getRelativeFrom(FilePath file, FilePath parent) {
    +        String fileRemote = file.getRemote();
    +        String parentRemote = parent.getRemote();
    +        Path filePath = Paths.get(fileRemote);
    +        Path parentPath = Paths.get(parentRemote);
    +        Path relativePath = parentPath.relativize(filePath);
    +        String[] paths = relativePath.toString().split("/");
    +        List<String> list = new ArrayList<String>();
    +        list.add("/");
    +        for (String path : paths) {
    +            if (!path.trim().equals("")) {
    +                list.add(path);
    +            }
    +        }
    +        return list;
    +    }
     }
    
  • src/main/java/org/jenkinsci/plugins/imagegallery/comparative/InFolderComparativeArchivedImagesGallery.java+37 12 modified
    @@ -23,20 +23,23 @@
      */
     package org.jenkinsci.plugins.imagegallery.comparative;
     
    -import hudson.Extension;
    -import hudson.FilePath;
    -import hudson.Util;
    -import hudson.model.BuildListener;
    -import hudson.model.Result;
    -import hudson.model.AbstractBuild;
    -
     import java.io.File;
     import java.io.IOException;
    +import java.nio.file.Path;
    +import java.nio.file.Paths;
     import java.util.List;
     
     import org.apache.commons.lang.StringUtils;
     import org.kohsuke.stapler.DataBoundConstructor;
     
    +import hudson.AbortException;
    +import hudson.Extension;
    +import hudson.FilePath;
    +import hudson.Util;
    +import hudson.model.AbstractBuild;
    +import hudson.model.BuildListener;
    +import hudson.model.Result;
    +
     /**
      * An image gallery of archived artifacts Comparing same files in different folders.
      *
    @@ -48,7 +51,7 @@ public class InFolderComparativeArchivedImagesGallery extends ComparativeArchive
     	/*
          * serial version UID.
          */
    -    private static final long serialVersionUID = 5537875107916417554L;
    +    private static final long serialVersionUID = 5537875107916417555L;
     
         /**
     	 * Constructor called from jelly.
    @@ -93,15 +96,22 @@ public String getDisplayName() {
     	public boolean createImageGallery(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException, IOException {
     		listener.getLogger().append("Creating archived images gallery.");
     		if (build.getHasArtifacts()) {
    -			File artifactsDir = build.getArtifactsDir().getAbsoluteFile();
    -			FilePath artifactsPath = new FilePath(new File(artifactsDir.getAbsoluteFile(), getBaseRootFolder()));
    +            File artifactsRootDir = build.getArtifactsDir().getAbsoluteFile();
    +            File artifactsDir = new File(artifactsRootDir, getBaseRootFolder());
    +
    +            // abort build if the gallery is using a file outside of the project artifacts directory
    +            if (!isChild(artifactsRootDir, artifactsDir)) {
    +                throw new AbortException("Invalid base root folder: " + getBaseRootFolder());
    +            }
    +
    +            FilePath artifactsPath = new FilePath(artifactsDir);
                 FilePath[] files = artifactsPath.list("**");
     
                 if (files != null && files.length > 0) {
                 	FilePairTree tree = new FilePairTree();
                     for (FilePath path : files) {
                             List<String> folder = getRelativeFrom(path.getParent(), artifactsPath);
    -                        List<String> artifactsRelativeFile = getRelativeFrom(path, artifactsPath.getParent());
    +                        List<String> artifactsRelativeFile = getRelativeFrom(path, artifactsPath);
                             tree.addToBranch(folder, new FilePair(path.getName(), StringUtils.join(artifactsRelativeFile, '/')));
                     }
                     String title = Util.replaceMacro(build.getEnvironment(listener).expand(getTitle()), build.getBuildVariableResolver());
    @@ -118,7 +128,22 @@ public boolean createImageGallery(AbstractBuild<?, ?> build, BuildListener liste
     		return true;
     	}
     
    -	public Object readResolve() {
    +    /**
    +     * Verify that the {@code child} file is a child node of the {@code parent} directory tree. File names are
    +     * normalised before the comparison (i.e. ../ and ./ are evaluated). Uses Java 8 {@link java.nio.file.Path}
    +     * methods.
    +     *
    +     * @param parent parent file
    +     * @param child child file
    +     * @return {@code true} iff child is child directory of parent directory tree
    +     */
    +    private boolean isChild(File parent, File child) {
    +        Path parentPath = Paths.get(parent.getAbsolutePath()).normalize().toAbsolutePath();
    +        Path childPath = Paths.get(child.getAbsolutePath()).normalize().toAbsolutePath();
    +        return childPath.startsWith(parentPath);
    +    }
    +
    +    public Object readResolve() {
     	    Integer imageWidth = getImageWidth();
     	    String width = getImageWidthText();
     	    if (imageWidth != null && imageWidth > 0) {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.