Compare commits

...

17 Commits

Author SHA1 Message Date
dependabot[bot]
a13198eb83 Bump github/codeql-action from 4 to 4.37.3
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4...v4.37.3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-31 04:54:37 +00:00
Tingluo Huang
b7fd7da153 Fix null ref exception when websocket client retries. (#4589) 2026-07-30 19:54:20 -04:00
Tingluo Huang
ed0bf12a66 Implement VSock secret notifier. (#4565) 2026-07-30 11:57:39 -04:00
dependabot[bot]
ec6b92b5dc Bump eslint-plugin-github from 6.1.0 to 6.1.2 in /src/Misc/expressionFunc/hashFiles (#4590)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-30 10:22:14 +01:00
dependabot[bot]
34ef7f24f8 Bump lint-staged from 16.4.0 to 17.2.0 in /src/Misc/expressionFunc/hashFiles (#4580)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-29 15:16:01 +00:00
dependabot[bot]
b8dd9a3e3c Bump System.Formats.Asn1 and System.Security.Cryptography.Pkcs (#4584)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-29 12:14:51 +00:00
dependabot[bot]
cc01a0d090 Bump undici from 6.24.1 to 6.27.0 in /src/Misc/expressionFunc/hashFiles (#4522)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-29 13:06:16 +01:00
dependabot[bot]
03a0707cbe Bump @typescript-eslint/parser from 8.63.0 to 8.65.0 in /src/Misc/expressionFunc/hashFiles (#4582)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 11:23:54 +01:00
Francesco Renzi
e1d1844725 Mask and sanitize debugger welcome message before DAP console output (#4577) 2026-07-28 11:31:59 +00:00
dependabot[bot]
44b796f3c8 Bump @typescript-eslint/parser from 8.59.0 to 8.63.0 in /src/Misc/expressionFunc/hashFiles (#4543)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-28 12:01:20 +01:00
dependabot[bot]
6bb3178b59 Bump eslint-plugin-github from 4.10.2 to 6.1.0 in /src/Misc/expressionFunc/hashFiles (#4549)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-28 11:27:28 +01:00
dependabot[bot]
148a3bb616 Bump Microsoft.DevTunnels.Connections from 1.3.48 to 1.3.50 (#4566)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-28 11:22:36 +01:00
dependabot[bot]
e18d1a9845 Bump actions/stale from 10 to 11 (#4576)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 11:21:36 +01:00
dependabot[bot]
14de40d73a Bump Azure.Storage.Blobs from 12.27.0 to 12.29.1 (#4545)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-22 13:05:20 -04:00
dependabot[bot]
8bd0a334d7 Bump actions/setup-node from 6 to 7 (#4554)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-07-22 09:39:46 +01:00
github-actions[bot]
f898ef14a5 Update dotnet sdk to latest version @8.0.423 (#4561)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-21 14:33:13 +00:00
Luke Tomlinson
de4c2885af Preparing runner release 2.336.0 (#4563)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-20 13:33:01 -04:00
26 changed files with 3358 additions and 3340 deletions

View File

@@ -4,7 +4,7 @@
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/dotnet": {
"version": "8.0.422"
"version": "8.0.423"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20"

View File

@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
- uses: actions/stale@v11
with:
close-issue-message: "This issue does not seem to be a problem with the runner application, it concerns the GitHub actions platform more generally. Could you please post your feedback on the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions) which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃"
exempt-issue-labels: "keep"

View File

@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
- uses: actions/stale@v11
with:
close-issue-message: "Thank you for your interest in the runner application and taking the time to provide your valuable feedback. We kindly ask you to redirect this feedback to the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions) which our team actively monitors and would be a better place to start a discussion for new feature requests in GitHub Actions. For more information on this policy please [read our contribution guidelines](https://github.com/actions/runner#contribute). 😃"
exempt-issue-labels: "keep"

View File

@@ -27,7 +27,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v4.37.3
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
@@ -38,4 +38,4 @@ jobs:
working-directory: src
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v4.37.3

View File

@@ -31,7 +31,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v7
with:
node-version: "20"

View File

@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v7
with:
node-version: "20"
- name: NPM install and audit fix with TypeScript auto-repair

View File

@@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@v7
with:
node-version: "20"

View File

@@ -7,7 +7,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
- uses: actions/stale@v11
with:
stale-issue-message: "This issue is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 15 days."
close-issue-message: "This issue was closed because it has been stalled for 15 days with no activity."

View File

@@ -1,40 +1,32 @@
## What's Changed
* Bump System.ServiceProcess.ServiceController from 10.0.6 to 10.0.7 by @dependabot[bot] in https://github.com/actions/runner/pull/4370
* Bump @actions/glob from 0.6.1 to 0.7.0 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/4367
* feat: propagate actions dependencies by @nodeselector in https://github.com/actions/runner/pull/4372
* Not retry and report action download 403. by @TingluoHuang in https://github.com/actions/runner/pull/4391
* Update setup job starting logs by @GitPaulo in https://github.com/actions/runner/pull/4383
* fix: expand commit hash regex to support SHA-256 (64-char) hashes by @yaananth in https://github.com/actions/runner/pull/4347
* Move dap setup to setup job step by @rentziass in https://github.com/actions/runner/pull/4403
* Add support for Ubuntu 26.04 (liblttng-ust1t64, libicu77-80) by @dvaldivia in https://github.com/actions/runner/pull/4394
* Update dotnet sdk to latest version @8.0.421 by @github-actions[bot] in https://github.com/actions/runner/pull/4428
* Update Docker to v29.5.0 and Buildx to v0.34.0 by @github-actions[bot] in https://github.com/actions/runner/pull/4425
* Execute debugger REPL commands inside job container by @rentziass in https://github.com/actions/runner/pull/4420
* Send welcome message in debugger console on connect by @rentziass in https://github.com/actions/runner/pull/4419
* Update snapshot-if context and functions by @drielenr in https://github.com/actions/runner/pull/4443
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4452
* Allow disable node v8 maglev jit compiler on node24. by @TingluoHuang in https://github.com/actions/runner/pull/4447
* Update Node 24 default date to June 16th, 2026 by @salmanmkc in https://github.com/actions/runner/pull/4462
* Populate telemetry for non-action post-job steps by @drielenr in https://github.com/actions/runner/pull/4463
* Add SDK types and results plumbing for background step control by @lokesh755 in https://github.com/actions/runner/pull/4472
* Add job execution view model by @rentziass in https://github.com/actions/runner/pull/4470
* Add thread-safety locks to StepsContext by @lokesh755 in https://github.com/actions/runner/pull/4475
* Add background step deferral infrastructure and metadata plumbing by @lokesh755 in https://github.com/actions/runner/pull/4479
* Wire job execution view into DAP by @rentziass in https://github.com/actions/runner/pull/4471
* Background steps execution engine by @lokesh755 in https://github.com/actions/runner/pull/4476
* Update Docker to v29.5.2 and Buildx to v0.34.1 by @github-actions[bot] in https://github.com/actions/runner/pull/4451
* BrokerServer should not retry on 401. by @TingluoHuang in https://github.com/actions/runner/pull/4445
* Add new env var to allow single-prefix multiline logs on stdout by @nuclearpidgeon in https://github.com/actions/runner/pull/4424
* Bump Microsoft.DevTunnels.Connections from 1.3.39 to 1.3.48 by @dependabot[bot] in https://github.com/actions/runner/pull/4441
* Bump System.Formats.Asn1 and System.Security.Cryptography.Pkcs by @dependabot[bot] in https://github.com/actions/runner/pull/4369
* Canceled background steps should not impact job result by @lokesh755 in https://github.com/actions/runner/pull/4482
* Report actions archive size in telemetry. by @TingluoHuang in https://github.com/actions/runner/pull/4509
* Bump actions/checkout from 6 to 7 by @dependabot[bot] in https://github.com/actions/runner/pull/4511
* Update Docker to v29.6.0 and Buildx to v0.35.0 by @github-actions[bot] in https://github.com/actions/runner/pull/4516
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4519
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4530
* feat: enhance telemetry for action download resolution and failures by @TingluoHuang in https://github.com/actions/runner/pull/4536
* Update Docker version to 29.6.1 by @AllanGuigou in https://github.com/actions/runner/pull/4539
* feat: add self-repository action reference syntax by @nodeselector in https://github.com/actions/runner/pull/4457
* Update dotnet sdk to latest version @8.0.422 by @github-actions[bot] in https://github.com/actions/runner/pull/4504
* Link config.sh and installdependencies.sh in docs by @Wuodan in https://github.com/actions/runner/pull/4526
* Add support for $GITHUB_ARTIFACTS environment files by @bdehamer in https://github.com/actions/runner/pull/4527
* feat: expose effective cache-mode to steps via ACTIONS_CACHE_MODE by @philip-gai in https://github.com/actions/runner/pull/4538
* Setup Job: announce when running with locked dependencies by @nodeselector in https://github.com/actions/runner/pull/4546
* Setup Job: reword locked-dependencies log line to use lockfile language by @nodeselector in https://github.com/actions/runner/pull/4550
* Wait for worker to finish during cancel by @TingluoHuang in https://github.com/actions/runner/pull/4553
* do not cap migrated setting retry is exception is session conflict by @aiqiaoy in https://github.com/actions/runner/pull/4557
* Allow checking DNS with api.gihub.com. by @TingluoHuang in https://github.com/actions/runner/pull/4547
* Exit ephemeral runners on broker acknowledge job-not-found by @rentziass in https://github.com/actions/runner/pull/4540
* Cleanup session files on get message or session deleted error by @nikola-jokic in https://github.com/actions/runner/pull/4551
* Recreate session on RunnerSessionInvalid from broker by @luketomlinson in https://github.com/actions/runner/pull/4556
## New Contributors
* @GitPaulo made their first contribution in https://github.com/actions/runner/pull/4383
* @dvaldivia made their first contribution in https://github.com/actions/runner/pull/4394
* @drielenr made their first contribution in https://github.com/actions/runner/pull/4443
* @nuclearpidgeon made their first contribution in https://github.com/actions/runner/pull/4424
* @Wuodan made their first contribution in https://github.com/actions/runner/pull/4526
* @bdehamer made their first contribution in https://github.com/actions/runner/pull/4527
* @philip-gai made their first contribution in https://github.com/actions/runner/pull/4538
**Full Changelog**: https://github.com/actions/runner/compare/v2.334.0...v2.335.0
**Full Changelog**: https://github.com/actions/runner/compare/v2.335.0...v2.336.0
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.

File diff suppressed because it is too large Load Diff

View File

@@ -38,13 +38,13 @@
"@stylistic/eslint-plugin": "^5.10.0",
"@types/node": "^22.0.0",
"@typescript-eslint/eslint-plugin": "^8.59.0",
"@typescript-eslint/parser": "^8.59.0",
"@typescript-eslint/parser": "^8.65.0",
"@vercel/ncc": "^0.38.3",
"eslint": "^8.47.0",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-github": "^6.1.2",
"eslint-plugin-prettier": "^5.0.0",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"lint-staged": "^17.2.0",
"prettier": "^3.0.3",
"typescript": "^6.0.3"
}

View File

@@ -61,7 +61,7 @@ namespace GitHub.Runner.Common
if (!string.IsNullOrEmpty(liveConsoleFeedUrl))
{
_liveConsoleFeedUrl = liveConsoleFeedUrl;
InitializeWebsocketClient(liveConsoleFeedUrl, token, TimeSpan.Zero, retryConnection: true);
InitializeWebsocketClient(liveConsoleFeedUrl, TimeSpan.Zero, retryConnection: true);
}
}
@@ -164,9 +164,9 @@ namespace GitHub.Runner.Common
return ValueTask.CompletedTask;
}
private void InitializeWebsocketClient(string liveConsoleFeedUrl, string accessToken, TimeSpan delay, bool retryConnection = false)
private void InitializeWebsocketClient(string liveConsoleFeedUrl, TimeSpan delay, bool retryConnection = false)
{
if (string.IsNullOrEmpty(accessToken))
if (string.IsNullOrEmpty(_token))
{
Trace.Info($"No access token from server");
return;
@@ -179,12 +179,7 @@ namespace GitHub.Runner.Common
}
Trace.Info($"Creating websocket client ..." + liveConsoleFeedUrl);
this._websocketClient = new ClientWebSocket();
this._websocketClient.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}");
var userAgentValues = new List<ProductInfoHeaderValue>();
userAgentValues.AddRange(UserAgentUtility.GetDefaultRestUserAgent());
userAgentValues.AddRange(HostContext.UserAgents);
this._websocketClient.Options.SetRequestHeader("User-Agent", string.Join(" ", userAgentValues.Select(x => x.ToString())));
this._websocketClient = CreateWebSocketClient();
// during initialization, retry upto 3 times to setup connection
this._websocketConnectTask = ConnectWebSocketClient(liveConsoleFeedUrl, delay, retryConnection);
@@ -201,8 +196,15 @@ namespace GitHub.Runner.Common
{
Trace.Info($"Attempting to start websocket client with delay {delay}.");
await Task.Delay(delay);
using var connectTimeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await this._websocketClient.ConnectAsync(new Uri(feedStreamUrl), connectTimeoutTokenSource.Token);
using (var connectTimeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
if (this._websocketClient == null)
{
this._websocketClient = CreateWebSocketClient();
}
await this._websocketClient.ConnectAsync(new Uri(feedStreamUrl), connectTimeoutTokenSource.Token);
}
Trace.Info($"Successfully started websocket client.");
connected = true;
}
@@ -211,6 +213,7 @@ namespace GitHub.Runner.Common
Trace.Info("Exception caught during websocket client connect, retry connection.");
Trace.Error(ex);
retries++;
this._websocketClient?.Dispose();
this._websocketClient = null;
_lastConnectionFailure = DateTime.Now;
}
@@ -259,7 +262,7 @@ namespace GitHub.Runner.Common
Trace.Info($"Websocket is not open, let's attempt to connect back again with random backoff {delay} ms.");
Trace.Verbose(ex.ToString());
retries++;
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, delay);
InitializeWebsocketClient(_liveConsoleFeedUrl, delay);
}
}
}
@@ -274,13 +277,24 @@ namespace GitHub.Runner.Common
if (_lastConnectionFailure.HasValue && DateTime.Now > _lastConnectionFailure.Value.AddMinutes(10))
{
// Some minutes passed since we retried last time, try connection again
InitializeWebsocketClient(_liveConsoleFeedUrl, _token, TimeSpan.Zero);
InitializeWebsocketClient(_liveConsoleFeedUrl, TimeSpan.Zero);
}
}
return delivered;
}
private ClientWebSocket CreateWebSocketClient()
{
var client = new ClientWebSocket();
client.Options.SetRequestHeader("Authorization", $"Bearer {_token}");
var userAgentValues = new List<ProductInfoHeaderValue>();
userAgentValues.AddRange(UserAgentUtility.GetDefaultRestUserAgent());
userAgentValues.AddRange(HostContext.UserAgents);
client.Options.SetRequestHeader("User-Agent", string.Join(" ", userAgentValues.Select(x => x.ToString())));
return client;
}
private void CloseWebSocket(WebSocketCloseStatus closeStatus, CancellationToken cancellationToken)
{
try

View File

@@ -0,0 +1,232 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using GitHub.DistributedTask.Logging;
using GitHub.Runner.Sdk;
using Newtonsoft.Json;
namespace GitHub.Runner.Common
{
[ServiceLocator(Default = typeof(VSockSecretNotifier))]
public interface IVSockSecretNotifier : IRunnerService, IAsyncDisposable
{
bool TryStartNotifier();
void NotifyNewSecret(NewSecretEventArgs newSecret);
}
public sealed class VSockSecretNotifier : RunnerService, IVSockSecretNotifier
{
private Socket _vsock = null;
private CancellationTokenSource _cancellationTokenSource = null;
private Task _secretNotificationTask = null;
private Channel<byte[]> _channel = Channel.CreateUnbounded<byte[]>(new UnboundedChannelOptions() { SingleReader = true });
public bool TryStartNotifier()
{
if (_vsock != null)
{
Trace.Verbose("VSocket is already connected.");
return true;
}
// `GITHUB_ACTIONS_RUNNER_VSOCK_CID_PORT` is expected to be in the format "CID:PORT", e.g. "2:9999".
string vsockCidPort = Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_VSOCK_CID_PORT");
if (string.IsNullOrEmpty(vsockCidPort))
{
Trace.Verbose("VSocket CID/Port environment variable is not set.");
return false;
}
string[] parts = vsockCidPort.Split(':', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
{
Trace.Verbose("VSocket CID/Port environment variable is not in the correct format.");
return false;
}
uint cid, port;
if (!uint.TryParse(parts[0], out cid) || !uint.TryParse(parts[1], out port))
{
Trace.Verbose("VSocket CID/Port environment variable contains invalid numbers.");
return false;
}
Trace.Info($"Attempting to start VSocket secret notifier with CID: {cid}, Port: {port}.");
try
{
SafeSocketHandle nativeSocket = NativeSocket((int)(AddressFamily)40, (int)SocketType.Stream, 0);
if (nativeSocket.IsInvalid)
{
int error = Marshal.GetLastPInvokeError();
nativeSocket.Dispose();
throw new SocketException(error);
}
_vsock = new Socket(nativeSocket);
_vsock.Connect(new HostVsockEndPoint(cid, port));
}
catch (Exception ex)
{
Trace.Error($"Failed to create and connect VSocket: {ex}");
_vsock?.Dispose();
_vsock = null;
return false;
}
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken);
_secretNotificationTask = ProcessSecretChannel();
Trace.Info($"VSocket secret notifier started successfully.");
return true;
}
public void NotifyNewSecret(NewSecretEventArgs newSecret)
{
if (_vsock == null)
{
Trace.Verbose("VSocket is not connected, skipping secret notification.");
return;
}
byte[] payloadBytes = Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(new { RunnerSecrets = newSecret }, Formatting.None));
byte[] lengthPrefix = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(payloadBytes.Length));
byte[] fullPayload = new byte[lengthPrefix.Length + payloadBytes.Length];
Buffer.BlockCopy(lengthPrefix, 0, fullPayload, 0, lengthPrefix.Length);
Buffer.BlockCopy(payloadBytes, 0, fullPayload, lengthPrefix.Length, payloadBytes.Length);
// we don't need to check return since unbounded channel will always accept the item.
_channel.Writer.TryWrite(fullPayload);
}
public async ValueTask DisposeAsync()
{
if (_vsock != null && _secretNotificationTask != null)
{
_cancellationTokenSource?.Cancel();
try
{
await _secretNotificationTask;
}
catch (Exception ex)
{
Trace.Error($"Secret notification task finished with error: {ex}");
}
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_vsock?.Dispose();
_vsock = null;
}
}
private async Task ProcessSecretChannel()
{
try
{
while (!_cancellationTokenSource.Token.IsCancellationRequested &&
await _channel.Reader.WaitToReadAsync(_cancellationTokenSource.Token))
{
while (_channel.Reader.TryRead(out var payload))
{
try
{
// Socket.SendAsync on a stream socket may send fewer bytes than requested,
// so keep sending until the entire payload has been written.
int totalSent = 0;
while (totalSent < payload.Length)
{
int bytesSent = await _vsock.SendAsync(payload.AsMemory(totalSent), SocketFlags.None, _cancellationTokenSource.Token);
if (bytesSent == 0)
{
throw new SocketException((int)SocketError.ConnectionReset);
}
totalSent += bytesSent;
}
}
catch (OperationCanceledException)
{
Trace.Info("Secret notification task was canceled.");
}
catch (Exception ex)
{
Trace.Error($"Failed to notify new secret over VSocket: {ex}");
}
}
}
}
catch (OperationCanceledException)
{
Trace.Info("Secret notification task was canceled.");
}
catch (Exception ex)
{
Trace.Error($"Failed to process secret channel: {ex}");
}
_channel.Writer.TryComplete();
}
[DllImport("libc", SetLastError = true, EntryPoint = "socket")]
private static extern SafeSocketHandle NativeSocket(int domain, int type, int protocol);
}
internal sealed class HostVsockEndPoint : EndPoint
{
private const int SocketAddressSize = 16;
private readonly uint _cid;
private readonly uint _port;
public HostVsockEndPoint(uint cid, uint port)
{
_cid = cid;
_port = port;
}
public override AddressFamily AddressFamily => (AddressFamily)40;
public override SocketAddress Serialize()
{
SocketAddress socketAddress = new SocketAddress(AddressFamily.Unspecified, SocketAddressSize);
// sockaddr_vm layout: family(0-1), reserved1(2-3), port(4-7), cid(8-11)
ushort family = (ushort)AddressFamily;
socketAddress[0] = (byte)(family & 0xFF);
socketAddress[1] = (byte)((family >> 8) & 0xFF);
socketAddress[2] = 0;
socketAddress[3] = 0;
socketAddress[4] = (byte)(_port & 0xFF);
socketAddress[5] = (byte)((_port >> 8) & 0xFF);
socketAddress[6] = (byte)((_port >> 16) & 0xFF);
socketAddress[7] = (byte)((_port >> 24) & 0xFF);
socketAddress[8] = (byte)(_cid & 0xFF);
socketAddress[9] = (byte)((_cid >> 8) & 0xFF);
socketAddress[10] = (byte)((_cid >> 16) & 0xFF);
socketAddress[11] = (byte)((_cid >> 24) & 0xFF);
return socketAddress;
}
public override EndPoint Create(SocketAddress socketAddress)
{
uint port = (uint)socketAddress[4]
| ((uint)socketAddress[5] << 8)
| ((uint)socketAddress[6] << 16)
| ((uint)socketAddress[7] << 24);
uint cid = (uint)socketAddress[8]
| ((uint)socketAddress[9] << 8)
| ((uint)socketAddress[10] << 16)
| ((uint)socketAddress[11] << 24);
return new HostVsockEndPoint(cid, port);
}
}
}

View File

@@ -1026,7 +1026,9 @@ namespace GitHub.Runner.Worker.Dap
{
if (!string.IsNullOrEmpty(debuggerConfig.WelcomeMessage))
{
SendOutput("console", debuggerConfig.WelcomeMessage);
// The welcome message is server-supplied and never rendered verbatim:
// mask secrets and strip control characters before it reaches the console.
SendOutput("console", SanitizeConsoleText(MaskUserVisibleText(debuggerConfig.WelcomeMessage)));
Trace.Info("Sent custom welcome message");
}
else
@@ -1772,6 +1774,30 @@ namespace GitHub.Runner.Worker.Dap
return HostContext?.SecretMasker?.MaskSecrets(value) ?? value;
}
/// <summary>
/// Removes C0/C1 control characters (except tab, carriage return and line feed) so
/// server-supplied text cannot inject ANSI escape sequences or terminal control codes
/// into the DAP console.
/// </summary>
internal static string SanitizeConsoleText(string value)
{
if (string.IsNullOrEmpty(value))
{
return value ?? string.Empty;
}
var builder = new StringBuilder(value.Length);
foreach (var character in value)
{
if (!char.IsControl(character) || character == '\t' || character == '\r' || character == '\n')
{
builder.Append(character);
}
}
return builder.ToString();
}
/// <summary>
/// Creates a DAP response with common fields pre-populated.
/// </summary>

View File

@@ -36,6 +36,11 @@ namespace GitHub.Runner.Worker.Dap
/// Optional welcome message content for the debugger console. Only used when
/// <see cref="OverrideWelcomeMessage"/> is true.
/// </summary>
/// <remarks>
/// Server-supplied and treated as untrusted: it is masked with the runner's
/// secret masker and stripped of control characters before being written to
/// the debugger console.
/// </remarks>
public string WelcomeMessage { get; }
/// <summary>Whether the tunnel configuration is complete and valid.</summary>

View File

@@ -23,7 +23,7 @@
<PackageReference Include="System.ServiceProcess.ServiceController" Version="10.0.3" />
<PackageReference Include="System.Threading.Channels" Version="10.0.3" />
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
<PackageReference Include="Microsoft.DevTunnels.Connections" Version="1.3.48" />
<PackageReference Include="Microsoft.DevTunnels.Connections" Version="1.3.50" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,15 +1,14 @@
using GitHub.DistributedTask.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines;
using GitHub.Runner.Common.Util;
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Services.WebApi;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using System.Text;
using Newtonsoft.Json;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Worker
{
@@ -46,6 +45,7 @@ namespace GitHub.Runner.Worker
var jobRunner = HostContext.CreateService<IJobRunner>();
var terminal = HostContext.GetService<ITerminal>();
await using (var secretNotifier = HostContext.GetService<IVSockSecretNotifier>())
using (var channel = HostContext.CreateService<IProcessChannel>())
using (var jobRequestCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken))
using (var channelTokenSource = new CancellationTokenSource())
@@ -86,6 +86,14 @@ namespace GitHub.Runner.Worker
HostContext.WritePerfCounter($"WorkerJobMessageReceived_{jobMessage.RequestId.ToString()}");
// Initialize the secret masker and set the thread culture.
if (Constants.Runner.Platform == Constants.OSPlatform.Linux &&
secretNotifier.TryStartNotifier())
{
HostContext.SecretMasker.NewSecretAdded += (sender, e) =>
{
secretNotifier.NotifyNewSecret(e);
};
}
InitializeSecretMasker(jobMessage);
SetCulture(jobMessage);

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
namespace GitHub.DistributedTask.Logging
{
@@ -11,5 +13,41 @@ namespace GitHub.DistributedTask.Logging
void AddValueEncoder(ValueEncoder encoder);
ISecretMasker Clone();
String MaskSecrets(String input);
public event EventHandler<NewSecretEventArgs> NewSecretAdded;
}
public abstract class NewSecretEventArgs : EventArgs
{
public abstract String Type { get; }
}
[DataContract]
public sealed class NewRegexSecretEventArgs : NewSecretEventArgs
{
[DataMember]
public override String Type => "regex";
public NewRegexSecretEventArgs(String pattern)
{
Pattern = pattern;
}
[DataMember]
public String Pattern { get; private set; }
}
[DataContract]
public sealed class NewVariableSecretEventArgs : NewSecretEventArgs
{
[DataMember]
public override String Type => "variable";
public NewVariableSecretEventArgs(List<string> values)
{
Values.AddRange(values);
}
[DataMember]
public List<string> Values { get; private set; } = new List<string>();
}
}

View File

@@ -10,6 +10,8 @@ namespace GitHub.DistributedTask.Logging
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class SecretMasker : ISecretMasker, IDisposable
{
public event EventHandler<NewSecretEventArgs> NewSecretAdded;
public SecretMasker()
{
m_originalValueSecrets = new HashSet<ValueSecret>();
@@ -66,6 +68,8 @@ namespace GitHub.DistributedTask.Logging
m_lock.ExitWriteLock();
}
}
NewSecretAdded?.Invoke(this, new NewRegexSecretEventArgs(pattern));
}
/// <summary>
@@ -133,6 +137,9 @@ namespace GitHub.DistributedTask.Logging
m_lock.ExitWriteLock();
}
}
// valueSecrets contains all the values run through the encoders.
NewSecretAdded?.Invoke(this, new NewVariableSecretEventArgs(valueSecrets.Select(x => x.m_value).ToList()));
}
/// <summary>

View File

@@ -271,7 +271,7 @@ namespace GitHub.DistributedTask.Pipelines
/// Optional welcome message shown in the debugger console when a client connects.
/// Only used when the <c>actions_runner_override_debugger_welcome_message</c>
/// feature flag is set to <c>true</c> in the job variables. With the flag set,
/// a non-empty value is shown as-is and a null or empty value suppresses the
/// a non-empty value is shown and a null or empty value suppresses the
/// default welcome message. When the flag is not set, the runner shows its
/// built-in help text and this field is ignored.
/// </summary>

View File

@@ -18,19 +18,19 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.27.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.29.1" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="10.0.7" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="10.0.10" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="10.0.3" />
<PackageReference Include="Minimatch" Version="2.0.0" />
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
<PackageReference Include="System.Formats.Asn1" Version="10.0.7" />
<PackageReference Include="System.Formats.Asn1" Version="10.0.10" />
</ItemGroup>
<ItemGroup>

View File

@@ -1422,6 +1422,96 @@ namespace GitHub.Runner.Common.Tests.Worker
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task WelcomeMessageMasksSecrets()
{
using (var hc = CreateTestContext())
{
hc.SecretMasker.AddValue("super-secret-token");
var port = GetFreePort();
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var jobContext = CreateJobContextWithTunnel(cts.Token, port,
overrideWelcomeMessage: true,
welcomeMessage: "Welcome! Token: super-secret-token");
await _debugger.StartAsync(jobContext.Object);
using var client = await ConnectClientAsync(port);
var stream = client.GetStream();
await SendRequestAsync(stream, new Request
{
Seq = 1,
Type = "request",
Command = "configurationDone"
});
var configDoneResponse = await ReadDapMessageAsync(stream, TimeSpan.FromSeconds(5));
Assert.Contains("\"command\":\"configurationDone\"", configDoneResponse);
var welcomeMsg = await ReadDapMessageAsync(stream, TimeSpan.FromSeconds(5));
Assert.Contains("\"event\":\"output\"", welcomeMsg);
Assert.DoesNotContain("super-secret-token", welcomeMsg);
Assert.Contains("***", welcomeMsg);
await _debugger.StopAsync();
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task WelcomeMessageStripsControlCharacters()
{
using (CreateTestContext())
{
var port = GetFreePort();
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var jobContext = CreateJobContextWithTunnel(cts.Token, port,
overrideWelcomeMessage: true,
welcomeMessage: "Wel\u001b[31mcome\u0007\u0000\u009bhere\nnext\tline");
await _debugger.StartAsync(jobContext.Object);
using var client = await ConnectClientAsync(port);
var stream = client.GetStream();
await SendRequestAsync(stream, new Request
{
Seq = 1,
Type = "request",
Command = "configurationDone"
});
var configDoneResponse = await ReadDapMessageAsync(stream, TimeSpan.FromSeconds(5));
Assert.Contains("\"command\":\"configurationDone\"", configDoneResponse);
var welcomeMsg = await ReadDapMessageAsync(stream, TimeSpan.FromSeconds(5));
Assert.Contains("\"event\":\"output\"", welcomeMsg);
var output = JObject.Parse(welcomeMsg)["body"]["output"].ToString();
Assert.Equal("Wel[31mcomehere\nnext\tline", output);
await _debugger.StopAsync();
}
}
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData(null, "")]
[InlineData("", "")]
[InlineData("plain text", "plain text")]
[InlineData("keep\r\nnewlines\tand tabs", "keep\r\nnewlines\tand tabs")]
[InlineData("esc\u001bape", "escape")]
[InlineData("bell\u0007null\u0000del\u007f", "bellnulldel")]
[InlineData("c1\u0080\u009fchars", "c1chars")]
public void SanitizeConsoleTextRemovesControlCharacters(string input, string expected)
{
Assert.Equal(expected, DapDebugger.SanitizeConsoleText(input));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]

View File

@@ -16,11 +16,13 @@ namespace GitHub.Runner.Common.Tests.Worker
{
private Mock<IProcessChannel> _processChannel;
private Mock<IJobRunner> _jobRunner;
private Mock<IVSockSecretNotifier> _vsockSecretNotifier;
public WorkerL0()
{
_processChannel = new Mock<IProcessChannel>();
_jobRunner = new Mock<IJobRunner>();
_vsockSecretNotifier = new Mock<IVSockSecretNotifier>();
}
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string jobName)
@@ -88,6 +90,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var worker = new GitHub.Runner.Worker.Worker();
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
hc.SetSingleton<IVSockSecretNotifier>(_vsockSecretNotifier.Object);
worker.Initialize(hc);
var jobMessage = CreateJobRequestMessage("job1");
var arWorkerMessages = new WorkerMessage[]
@@ -139,6 +142,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var worker = new GitHub.Runner.Worker.Worker();
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
hc.SetSingleton<IVSockSecretNotifier>(_vsockSecretNotifier.Object);
worker.Initialize(hc);
var jobMessage = CreateJobRequestMessage("job1");
var cancelMessage = CreateJobCancelMessage(jobMessage.JobId);

View File

@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="8.0.422"
DOTNETSDK_VERSION="8.0.423"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion)

View File

@@ -1,5 +1,5 @@
{
"sdk": {
"version": "8.0.422"
"version": "8.0.423"
}
}

View File

@@ -1 +1 @@
2.335.0
2.336.0