From 9cf2885e079e1115f50c9297fa25902e78000232 Mon Sep 17 00:00:00 2001 From: Bhautik Date: Sat, 19 Sep 2026 02:30:21 +0530 Subject: [PATCH] fix(security): redact delegated token in formatted output --- CHANGELOG.md | 1 + src/CreateOS.Sandbox/Models.cs | 5 ++++- tests/CreateOS.Sandbox.Tests/SandboxInstanceTests.cs | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c14673..3b6b06d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Redact plaintext sandbox access tokens from response string output to prevent accidental disclosure in logs. - Set the next NuGet version and default user agent to `0.1.3`. - Added delegated sandbox access token lifecycle methods and a separate token scoped sandbox handle. diff --git a/src/CreateOS.Sandbox/Models.cs b/src/CreateOS.Sandbox/Models.cs index a6d18da..be8f680 100644 --- a/src/CreateOS.Sandbox/Models.cs +++ b/src/CreateOS.Sandbox/Models.cs @@ -131,7 +131,10 @@ public sealed record SandboxData public sealed record SandboxAccessTokenCreateResponse( string Token, bool Enabled, [property: JsonPropertyName("created_at")] DateTimeOffset CreatedAt, - [property: JsonPropertyName("rotated_at")] DateTimeOffset? RotatedAt = null); + [property: JsonPropertyName("rotated_at")] DateTimeOffset? RotatedAt = null) +{ + public override string ToString() => "SandboxAccessTokenCreateResponse { Token = [REDACTED] }"; +} /// Delegated token state without plaintext credential material. public sealed record SandboxAccessTokenMetadata( diff --git a/tests/CreateOS.Sandbox.Tests/SandboxInstanceTests.cs b/tests/CreateOS.Sandbox.Tests/SandboxInstanceTests.cs index 0120307..359a01f 100644 --- a/tests/CreateOS.Sandbox.Tests/SandboxInstanceTests.cs +++ b/tests/CreateOS.Sandbox.Tests/SandboxInstanceTests.cs @@ -91,6 +91,7 @@ public async Task AccessTokenLifecycleUsesScopedCredential() var owner = Instance(transport, ingress: false); var created = await owner.CreateAccessTokenAsync(); Assert.Equal("skp_sb_first", created.Token); + Assert.DoesNotContain(created.Token, created.ToString()); Assert.Equal("skp_sb...irst", (await owner.GetAccessTokenAsync()).TokenHint); var worker = owner.WithAccessToken(created.Token); Assert.NotSame(owner.Files, worker.Files);