Skip to content
Open
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 @@ -3,14 +3,15 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System.Text.RegularExpressions;
using System;
using Datadog.Trace.Util;

namespace Datadog.Trace.RemoteConfigurationManagement
{
internal sealed record RemoteConfigurationPath
{
private static readonly Regex PathRegex = new Regex("^(datadog/\\d+|employee)/([^/]+)/([^/]+)/[^/]+$", RegexOptions.Compiled);
private const string DatadogPrefix = "datadog/";
private const string EmployeePrefix = "employee/";

private RemoteConfigurationPath(string path, string product, string id)
{
Expand All @@ -27,14 +28,72 @@ private RemoteConfigurationPath(string path, string product, string id)

public static RemoteConfigurationPath FromPath(string path)
{
var matcher = PathRegex.Match(path);
if (!matcher.Success)
// Previous regex: ^(datadog/\d+|employee)/([^/]+)/([^/]+)/[^/]+$
// Determine prefix and find the start of the product segment.
// "datadog/{digits}/{product}/{id}/{filename}"
// "employee/{product}/{id}/{filename}"
int productStart;

if (path is null)
{
ThrowHelper.ThrowException("Error parsing path: path is null");
return default; // unreachable, satisfies compiler
}
else if (path.StartsWith(DatadogPrefix, StringComparison.Ordinal))
{
// Find the slash after the digits: "datadog/{digits}/"
var digitStart = DatadogPrefix.Length;
var secondSlash = path.IndexOf('/', digitStart);
if (secondSlash < 0 || secondSlash == digitStart)
{
ThrowHelper.ThrowException($"Error parsing path: {path}");
}

// Verify all characters between are digits
for (var i = digitStart; i < secondSlash; i++)
{
if (path[i] is < '0' or > '9')

Choose a reason for hiding this comment

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

P2 Badge Preserve Unicode digit handling for datadog org segment

The new parser narrows org-id validation to ASCII ('0'..'9'), but the previous implementation used \d in .NET regex, which accepts all Unicode decimal digits. That is a behavioral regression: paths like datadog/١/PRODUCT/id/file used to parse and now throw, which causes RcmSubscriptionManager.ProcessResponse to fail that poll and skip applying configs whenever a non-ASCII decimal digit appears in the org segment.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm like 99% sure that was probably a bug, but we should check...

{
ThrowHelper.ThrowException($"Error parsing path: {path}");
}
}

productStart = secondSlash + 1;
}
else if (path.StartsWith(EmployeePrefix, StringComparison.Ordinal))
{
productStart = EmployeePrefix.Length;
}
else
{
ThrowHelper.ThrowException($"Error parsing path: {path}");
return default; // unreachable, satisfies compiler
}

// From productStart we need exactly: {product}/{id}/{filename}
// i.e. two more slashes, no empty segments, no trailing content after filename.
var thirdSlash = path.IndexOf('/', productStart);
if (thirdSlash < 0 || thirdSlash == productStart)
{
ThrowHelper.ThrowException($"Error parsing path: {path}");
}

var idStart = thirdSlash + 1;
var fourthSlash = path.IndexOf('/', idStart);
if (fourthSlash < 0 || fourthSlash == idStart)
{
ThrowHelper.ThrowException($"Error parsing path: {path}");
}

// Filename must be non-empty and there must be no more slashes
var filenameStart = fourthSlash + 1;
if (filenameStart >= path.Length || path.IndexOf('/', filenameStart) >= 0)
{
ThrowHelper.ThrowException($"Error parsing path: {path}");
}

var product = matcher.Groups[2].Value;
var id = matcher.Groups[3].Value;
var product = path.Substring(productStart, thirdSlash - productStart);
var id = path.Substring(idStart, fourthSlash - idStart);

return new RemoteConfigurationPath(path, product, id);
}
Expand Down
Loading
Loading