Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 23 additions & 0 deletions src/Runner.Sdk/Util/UrlUtil.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;

namespace GitHub.Runner.Sdk
{
Expand All @@ -21,6 +22,28 @@ public static bool IsHostedServer(UriBuilder gitHubUrl)
gitHubUrl.Host.EndsWith(".ghe.com", StringComparison.OrdinalIgnoreCase);
}

// For GitHub Enterprise Cloud with data residency, we allow fallback to GitHub.com for Actions resolution
public static bool IsGHECDRFallbackToDotcom(UriBuilder gitHubUrl, ActionDownloadInfo downloadInfo)
{
string pattern = @"^https?:\/\/api\.github\.com\/repos\/[^\/]+\/[^\/]+\/(tar|zip)ball\/[a-zA-Z0-9._\/-]+$";
#if OS_WINDOWS
if (downloadInfo.ZipballUrl != null && !Regex.IsMatch(downloadInfo.ZipballUrl.ToString(), pattern))
#else
if (downloadInfo.TarballUrl != null && !Regex.IsMatch(downloadInfo.TarballUrl.ToString(), pattern))
#endif
{
return false;
}

if (gitHubUrl.Host.EndsWith(".ghe.localhost", StringComparison.OrdinalIgnoreCase) ||
gitHubUrl.Host.EndsWith(".ghe.com", StringComparison.OrdinalIgnoreCase))
{
return true;
}

return false;
}

public static Uri GetCredentialEmbeddedUrl(Uri baseUrl, string username, string password)
{
ArgUtil.NotNull(baseUrl, nameof(baseUrl));
Expand Down
22 changes: 20 additions & 2 deletions src/Runner.Worker/ActionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -739,13 +739,31 @@ private async Task BuildActionContainerAsync(IExecutionContext executionContext,
ArgUtil.NotNull(actionDownloadInfos.Actions, nameof(actionDownloadInfos.Actions));
var defaultAccessToken = executionContext.GetGitHubContext("token");

// Get GitHub URL for OnPrem fallback logic
UriBuilder gitHubUrl = null;
var serverUrl = executionContext.GetGitHubContext("server_url");
if (!string.IsNullOrEmpty(serverUrl))
{
gitHubUrl = new UriBuilder(serverUrl);
}
else
{
// Fallback to runner settings if GitHub context doesn't have server_url
var configurationStore = HostContext.GetService<IConfigurationStore>();
var runnerSettings = configurationStore.GetSettings();
if (!string.IsNullOrEmpty(runnerSettings.GitHubUrl))
{
gitHubUrl = new UriBuilder(runnerSettings.GitHubUrl);
}
}

foreach (var actionDownloadInfo in actionDownloadInfos.Actions.Values)
{
// Add secret
HostContext.SecretMasker.AddValue(actionDownloadInfo.Authentication?.Token);

// Default auth token
if (string.IsNullOrEmpty(actionDownloadInfo.Authentication?.Token))
// Use default auth token unless falling back from OnPrem
if (string.IsNullOrEmpty(actionDownloadInfo.Authentication?.Token) && !UrlUtil.IsGHECDRFallbackToDotcom(gitHubUrl, actionDownloadInfo))
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method IsGHECDRFallbackToDotcom is called with a potentially null gitHubUrl parameter. This will cause a null reference exception if both serverUrl and runnerSettings.GitHubUrl are null or empty.

Suggested change
if (string.IsNullOrEmpty(actionDownloadInfo.Authentication?.Token) && !UrlUtil.IsGHECDRFallbackToDotcom(gitHubUrl, actionDownloadInfo))
if (string.IsNullOrEmpty(actionDownloadInfo.Authentication?.Token) && (gitHubUrl == null || !UrlUtil.IsGHECDRFallbackToDotcom(gitHubUrl, actionDownloadInfo)))

Copilot uses AI. Check for mistakes.
{
actionDownloadInfo.Authentication = new WebApi.ActionDownloadAuthentication { Token = defaultAccessToken };
}
Expand Down
Loading