Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ private static ILspTransport CreateIpcTransport(IServiceProvider serviceProvider

if (useNamedPipes)
{
logger.LogInformation($"Creating IPC: namedpipe={namedPipeInfo}");
logger.LogInformation("Creating IPC: namedpipe");
transport = new NamedPipeIpc(namedPipeInfo, logger);
}
else
{
logger.LogInformation($"Creating IPC: unixdomainsockets={namedPipeInfo}");
logger.LogInformation("Creating IPC: unixdomainsockets");
transport = new UnixDomainSocketsIPC(namedPipeInfo, logger);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,12 @@ public async Task HandleNotificationAsync(DidChangeWatchedFilesParams request, R
var fileInfo = _fileProvider.GetFileInfo(filePath);
if (!fileInfo.Exists)
{
// File name may be an agent/component name (EUII); keep it out of telemetry
// (preserved for local debugging via LogSensitiveInformation).
_logger.LogWarning(
$"Can't process '{changeFileEvent.Type}' event for a watched file: " +
"The file does not exist.");
_logger.LogSensitiveInformation(
$"Can't process '{changeFileEvent.Type}' event for '{filePath.FileName}': " +
"The file does not exist.");
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ public override bool TryGetLanguageForRequest(string methodName, object? seriali

if (!lspWorkspaceManager.TryGetLanguageForDocument(typedLspUri, out var languageObject))
{
Logger.LogError($"Failed to get language for {typedLspUri.Raw}");
// The raw URI is a full file path (EUII); keep it out of telemetry while
// preserving the full detail for local debugging.
Logger.LogError("Failed to get language for document.");
Logger.LogSensitiveInformation($"Failed to get language for {typedLspUri.Raw}");
language = null;
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ private static void LogUnsupportedSchemeOnce(string scheme, string sampleRaw, IL
{
// Truncate sample for logging
var truncatedSample = sampleRaw.Length > 100 ? sampleRaw[..97] + "..." : sampleRaw;
logger.LogInformation($"UnsupportedSchemeObserved: scheme='{scheme}', sample='{truncatedSample}'");
// The sample may be a full file path or URI (EUII); only the scheme is safe for telemetry.
logger.LogSensitiveInformation(
$"UnsupportedSchemeObserved: scheme='{scheme}', sample='{truncatedSample}'",
$"UnsupportedSchemeObserved: scheme='{scheme}'");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.PowerPlatformLS.Contracts.Internal.Common;
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

/// <summary>
Expand All @@ -22,6 +23,11 @@ public LoggingHttpHandler(ILogger<LoggingHttpHandler> logger)

private static int _requestId;

// Environment/bot identifiers (GUIDs) in request paths are PII; redact them from telemetry.
private static readonly Regex GuidPattern = new(
"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
RegexOptions.Compiled);

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
int httpId = Interlocked.Increment(ref _requestId);
Expand Down Expand Up @@ -54,7 +60,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
private static string GetPathAndQuery(Uri? uri)
{
if (uri == null) return string.Empty;
return uri.PathAndQuery;
return GuidPattern.Replace(uri.PathAndQuery, "{id}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static (int Code, string Message) Handle(Exception ex, ILspLogger logger,

// User-recoverable: the user can fix this by performing an action (resync, reclone, etc.).
FileNotFoundException fnf =>
LogTypedError(logger, 400, fnf),
LogTypedSensitiveError(logger, 400, fnf, "File not found."),

DirectoryNotFoundException dnf =>
LogTypedError(logger, 400, dnf),
LogTypedSensitiveError(logger, 400, dnf, "Directory not found."),

// User validation: caller explicitly threw to signal bad input.
InvalidOperationException ioe =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public async Task FileFilter_OnDidChangeWatchedFiles_Async()
}
else
{
Assert.Single(logs.Warning.Where(x => x == $"Can't process 'Changed' event for '{entry.filepath.Split('/')[^1]}': The file does not exist."));
// The full message (with file name) is routed through LogSensitiveInformation,
// which surfaces at Info level in Debug/test builds; the Warning is now file-name-free.
Assert.Single(logs.Info.Where(x => x == $"Can't process 'Changed' event for '{entry.filepath.Split('/')[^1]}': The file does not exist."));
Comment on lines +73 to +75
}
Comment on lines +73 to 76
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@ public void GetPathAndQuery_Returns_Empty_For_Null_Uri()
Assert.Equal(string.Empty, result);
}

[Fact]
public void GetPathAndQuery_Redacts_Guids_In_Path()
{
var method = typeof(LoggingHttpHandler).GetMethod("GetPathAndQuery", BindingFlags.NonPublic | BindingFlags.Static);

Assert.NotNull(method);
var uri = new Uri("https://contoso.api.crm.dynamics.com/api/botmanagement/v1/environments/00b0eda3-d061-ea9f-b86f-0fd3f5628590/bots/09031e3d-436d-f111-ab0e-7ced8dfef2b5/content/botcomponents");
var result = (string)method!.Invoke(null, [uri])!;

Assert.Equal("/api/botmanagement/v1/environments/{id}/bots/{id}/content/botcomponents", result);
Assert.DoesNotContain("00b0eda3", result);
Assert.DoesNotContain("09031e3d", result);
}

[Fact]
public async Task SendAsync_Includes_RequestId_From_LspRequestContext()
{
Expand Down