diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index dae28ab27108e6c10384f1c0d7f8ce7904de321d..6ba326c00b313e72c7d68ce7d1417e079eed17f3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -70,6 +70,7 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
 import org.eclipse.jgit.lib.PackLock;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -77,6 +78,7 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.Config.SectionParser;
 import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevFlag;
 import org.eclipse.jgit.revwalk.RevObject;
@@ -184,6 +186,8 @@ public class ReceivePack {
 
 	private boolean needBaseObjectIds;
 
+	private boolean ensureObjectsProvidedVisible;
+
 	/**
 	 * Create a new pack receive for an open repository.
 	 *
@@ -288,6 +292,26 @@ public final Set<ObjectId> getNewObjectIds() {
 		return ip.getNewObjectIds();
 	}
 
+	/**
+	 * Configure this receive pack instance to ensure that the provided
+	 * objects are visible to the user.
+	 * <p>
+	 * By default, a receive pack assumes that its user will only provide
+	 * references to objects that it can see. Setting this flag to {@code true}
+	 * will add an additional check that verifies that the objects that were
+	 * provided are reachable by a tree or a commit that the user can see.
+	 * <p>
+	 * This option is useful when the code doesn't trust the client not to
+	 * provide a forged SHA-1 reference to an object in an attempt to access
+	 * parts of the DAG that they aren't allowed to see, via the configured
+	 * {@link RefFilter}.
+	 *
+	 * @param b {@code true} to enable the additional check.
+	 */
+	public void setEnsureProvidedObjectsVisible(boolean b) {
+		this.ensureObjectsProvidedVisible = b;
+	}
+
 	/**
 	 * @return true if this class expects a bi-directional pipe opened between
 	 *         the client and itself. The default is true.
@@ -777,8 +801,9 @@ private void receivePack() throws IOException {
 
 		ip = IndexPack.create(db, rawIn);
 		ip.setFixThin(true);
-		ip.setNeedNewObjectIds(needNewObjectIds);
-		ip.setNeedBaseObjectIds(needBaseObjectIds);
+		ip.setNeedNewObjectIds(needNewObjectIds || ensureObjectsProvidedVisible);
+		ip.setNeedBaseObjectIds(needBaseObjectIds
+				|| ensureObjectsProvidedVisible);
 		ip.setObjectChecking(isCheckReceivedObjects());
 		ip.index(NullProgressMonitor.INSTANCE);
 
@@ -802,7 +827,34 @@ private void checkConnectivity() throws IOException {
 		}
 		for (final Ref ref : refs.values())
 			ow.markUninteresting(ow.parseAny(ref.getObjectId()));
-		ow.checkConnectivity();
+
+		ObjectIdSubclassMap<ObjectId> provided =
+			new ObjectIdSubclassMap<ObjectId>();
+		if (ensureObjectsProvidedVisible) {
+			for (ObjectId id : getBaseObjectIds()) {
+				   RevObject b = ow.lookupAny(id, Constants.OBJ_BLOB);
+				   if (!b.has(RevFlag.UNINTERESTING))
+				     throw new MissingObjectException(b, b.getType());
+			}
+			for (ObjectId id : getNewObjectIds()) {
+				provided.add(id);
+			}
+		}
+
+		RevCommit c;
+		while ((c = ow.next()) != null) {
+			if (ensureObjectsProvidedVisible && !provided.contains(c))
+				throw new MissingObjectException(c, Constants.TYPE_COMMIT);
+		}
+
+		RevObject o;
+		while ((o = ow.nextObject()) != null) {
+			if (o instanceof RevBlob && !db.hasObject(o))
+				throw new MissingObjectException(o, Constants.TYPE_BLOB);
+
+			if (ensureObjectsProvidedVisible && !provided.contains(o))
+				throw new MissingObjectException(o, Constants.TYPE_BLOB);
+		}
 	}
 
 	private void validateCommands() {