Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ public static class Endpoints
public const string InfoRefs = "/info/refs?service=git-upload-pack";
}

public static class WellKnownObjects
{
// SKETCH (design proposal): the git empty-tree object. Its SHA is a fixed constant
// that every git server recognizes, so it is a safe, well-formed target for a
// credential probe - the probe URL never depends on possibly-corrupt request input.
public const string EmptyTreeSha = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
}

public static class SpecialGitFiles
{
public const string GitAttributes = ".gitattributes";
Expand Down
52 changes: 52 additions & 0 deletions GVFS/GVFS.Common/Http/GitObjectsHttpRequestor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,58 @@ public GitObjectsHttpRequestor(ITracer tracer, Enlistment enlistment, CacheServe

public CacheServerInfo CacheServer { get; private set; }

/// <summary>
/// SKETCH (design proposal). Probe the cache server's own objects endpoint with a
/// known-good, well-formed SHA (the git empty tree). This exercises the SAME host and
/// auth path that returned the 400, so the probe's status cleanly separates
/// "credential is bad" (401/302) from "the request was malformed" (any other status).
/// A 404 here still proves auth succeeded - we reached "not found" past the auth gate.
/// </summary>
/// <summary>
/// SKETCH (design proposal). Probe the objects endpoint of the SAME host that returned the
/// 400 (cache server or origin) with a known-good, well-formed SHA (the git empty tree), so
/// the probe exercises the same auth path. A 404 here still proves auth succeeded - we
/// reached "not found" past the auth gate.
/// </summary>
protected override Uri GetCredentialProbeUri(Uri failedRequestUri)
{
string objectsEndpoint = null;

if (this.CacheServer != null &&
!string.IsNullOrEmpty(this.CacheServer.ObjectsEndpointUrl) &&
HostMatches(failedRequestUri, this.CacheServer.ObjectsEndpointUrl))
{
objectsEndpoint = this.CacheServer.ObjectsEndpointUrl;
}
else if (!string.IsNullOrEmpty(this.enlistment.RepoUrl))
{
// The 400 came from origin (or the host could not be matched to the cache server).
objectsEndpoint = this.enlistment.RepoUrl + GVFSConstants.Endpoints.GVFSObjects;
}

if (string.IsNullOrEmpty(objectsEndpoint))
{
return null;
}

try
{
return new Uri(objectsEndpoint.TrimEnd('/') + "/" + GVFSConstants.WellKnownObjects.EmptyTreeSha);
}
catch (UriFormatException)
{
// A malformed endpoint cannot be probed; caller treats null as "do not reject".
return null;
}
}

private static bool HostMatches(Uri uri, string candidateUrl)
{
return uri != null &&
Uri.TryCreate(candidateUrl, UriKind.Absolute, out Uri candidate) &&
string.Equals(uri.Host, candidate.Host, StringComparison.OrdinalIgnoreCase);
}

public virtual List<GitObjectSize> QueryForFileSizes(IEnumerable<string> objectIds, CancellationToken cancellationToken)
{
long requestId = HttpRequestor.GetNewRequestId();
Expand Down
Loading
Loading