提交 9285ed86 编写于 作者: J Jesse Glick

Merge branch 'security' into security-stable-1.596

Conflicts:
	test/src/test/java/hudson/tasks/ArtifactArchiverTest.java
......@@ -33,12 +33,15 @@ import hudson.util.DirScanner;
import hudson.util.FileVisitor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import jenkins.MasterToSlaveFileCallable;
......@@ -209,12 +212,14 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
* @return a wrapper
*/
public static VirtualFile forFile(final File f) {
return new FileVF(f);
return new FileVF(f, f);
}
private static final class FileVF extends VirtualFile {
private final File f;
FileVF(File f) {
private final File root;
FileVF(File f, File root) {
this.f = f;
this.root = root;
}
@Override public String getName() {
return f.getName();
......@@ -223,46 +228,85 @@ public abstract class VirtualFile implements Comparable<VirtualFile>, Serializab
return f.toURI();
}
@Override public VirtualFile getParent() {
return forFile(f.getParentFile());
return new FileVF(f.getParentFile(), root);
}
@Override public boolean isDirectory() throws IOException {
if (isIllegalSymlink()) {
return false;
}
return f.isDirectory();
}
@Override public boolean isFile() throws IOException {
if (isIllegalSymlink()) {
return false;
}
return f.isFile();
}
@Override public boolean exists() throws IOException {
if (isIllegalSymlink()) {
return false;
}
return f.exists();
}
@Override public VirtualFile[] list() throws IOException {
if (isIllegalSymlink()) {
return new VirtualFile[0];
}
File[] kids = f.listFiles();
if (kids == null) {
return new VirtualFile[0];
}
VirtualFile[] vfs = new VirtualFile[kids.length];
for (int i = 0; i < kids.length; i++) {
vfs[i] = forFile(kids[i]);
vfs[i] = new FileVF(kids[i], root);
}
return vfs;
}
@Override public String[] list(String glob) throws IOException {
if (isIllegalSymlink()) {
return new String[0];
}
return new Scanner(glob).invoke(f, null);
}
@Override public VirtualFile child(String name) {
return forFile(new File(f, name));
return new FileVF(new File(f, name), root);
}
@Override public long length() throws IOException {
if (isIllegalSymlink()) {
return 0;
}
return f.length();
}
@Override public long lastModified() throws IOException {
if (isIllegalSymlink()) {
return 0;
}
return f.lastModified();
}
@Override public boolean canRead() throws IOException {
if (isIllegalSymlink()) {
return false;
}
return f.canRead();
}
@Override public InputStream open() throws IOException {
if (isIllegalSymlink()) {
throw new FileNotFoundException(f.getPath());
}
return new FileInputStream(f);
}
private boolean isIllegalSymlink() { // TODO JENKINS-26838
try {
String myPath = f.getCanonicalPath();
String rootPath = root.getCanonicalPath();
if (!myPath.equals(rootPath) && !myPath.startsWith(rootPath + File.separatorChar)) {
return true;
}
} catch (IOException x) {
Logger.getLogger(VirtualFile.class.getName()).log(Level.FINE, "could not determine symlink status of " + f, x);
}
return false;
}
}
/**
......
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.util;
import hudson.Util;
import hudson.model.TaskListener;
import java.io.File;
import java.io.FileNotFoundException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
public class VirtualFileTest {
@Rule public TemporaryFolder tmp = new TemporaryFolder();
//@Issue("SECURITY-162")
@Test public void outsideSymlinks() throws Exception {
File ws = tmp.newFolder("ws");
FileUtils.write(new File(ws, "safe"), "safe");
Util.createSymlink(ws, "safe", "supported", TaskListener.NULL);
File other = tmp.newFolder("other");
FileUtils.write(new File(other, "secret"), "s3cr3t");
Util.createSymlink(ws, "../other/secret", "hack", TaskListener.NULL);
VirtualFile root = VirtualFile.forFile(ws);
VirtualFile supported = root.child("supported");
assertTrue(supported.isFile());
assertTrue(supported.exists());
assertEquals("safe", IOUtils.toString(supported.open(), (String) null));
VirtualFile hack = root.child("hack");
assertFalse(hack.isFile());
assertFalse(hack.exists());
try {
hack.open();
fail();
} catch (FileNotFoundException x) {
// OK
}
}
}
\ No newline at end of file
......@@ -36,6 +36,9 @@ import hudson.model.Result;
import static hudson.tasks.LogRotatorTest.build;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jenkins.util.VirtualFile;
......@@ -125,6 +128,34 @@ public class ArtifactArchiverTest {
assertEquals("lodge", kids[0].getName());
// do not check that it .exists() since its target has not been archived
}
//@Issue("SECURITY-162")
@Test public void outsideSymlinks() throws Exception {
final FreeStyleProject p = j.createFreeStyleProject();
p.getBuildersList().add(new TestBuilder() {
@Override public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
FilePath ws = build.getWorkspace();
if (ws == null) {
return false;
}
ws.child("hack").symlinkTo(p.getConfigFile().getFile().getAbsolutePath(), listener);
return true;
}
});
p.getPublishersList().add(new ArtifactArchiver("hack", "", false, true));
FreeStyleBuild b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
List<FreeStyleBuild.Artifact> artifacts = b.getArtifacts();
assertEquals(1, artifacts.size());
FreeStyleBuild.Artifact artifact = artifacts.get(0);
assertEquals("hack", artifact.relativePath);
VirtualFile[] kids = b.getArtifactManager().root().list();
assertEquals(1, kids.length);
assertEquals("hack", kids[0].getName());
assertFalse(kids[0].isDirectory());
assertFalse(kids[0].isFile());
assertFalse(kids[0].exists());
j.createWebClient().assertFails(b.getUrl() + "artifact/hack", HttpURLConnection.HTTP_NOT_FOUND);
}
private void runNewBuildAndStartUnitlIsCreated(AbstractProject project) throws InterruptedException{
int buildNumber = project.getNextBuildNumber();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册