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 dae28ab27..6ba326c00 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.Config; 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.RefUpdate; 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 class ReceivePack { return ip.getNewObjectIds(); } + /** + * Configure this receive pack instance to ensure that the provided + * objects are visible to the user. + *

+ * 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. + *

+ * 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 @@ public class ReceivePack { 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 @@ public class ReceivePack { } for (final Ref ref : refs.values()) ow.markUninteresting(ow.parseAny(ref.getObjectId())); - ow.checkConnectivity(); + + ObjectIdSubclassMap provided = + new ObjectIdSubclassMap(); + 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() {