diff --git a/src/Kiota.Builder/Extensions/StringExtensions.cs b/src/Kiota.Builder/Extensions/StringExtensions.cs index 3f6fc87d05..7b80cf4702 100644 --- a/src/Kiota.Builder/Extensions/StringExtensions.cs +++ b/src/Kiota.Builder/Extensions/StringExtensions.cs @@ -364,6 +364,20 @@ private static string NormalizeSymbolsAfterCleanup(string original) public static string CleanupXMLString(this string? original) => SecurityElement.Escape(original) ?? string.Empty; + /// + /// Neutralizes block comment delimiters (/* and */) in schema-derived text so it + /// cannot break out of a generated block/doc comment. The delimiters are replaced (not + /// deleted): deleting a two-character sequence lets the surrounding characters join back into a + /// new delimiter (e.g. **// collapses to */). Replacing with a spaced variant + /// guarantees no residual */ can re-form. + /// + /// The original string. + /// The string with block comment delimiters neutralized. + public static string NeutralizeBlockCommentDelimiters(this string? original) + => string.IsNullOrEmpty(original) ? string.Empty : + original.Replace("/*", "//*", StringComparison.Ordinal) + .Replace("*/", "* /", StringComparison.Ordinal); + /// /// Checks if 2 strings are equal, case insensitive /// diff --git a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs index fd8d814b93..0afbbffc07 100644 --- a/src/Kiota.Builder/Writers/Java/JavaConventionService.cs +++ b/src/Kiota.Builder/Writers/Java/JavaConventionService.cs @@ -154,7 +154,7 @@ public void WriteLongDescription(CodeElement element, LanguageWriter writer, IEn internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => string.IsNullOrEmpty(originalDescription) ? originalDescription : - nonAsciiReplaceRegex().Replace(originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase).Replace("*/", string.Empty, StringComparison.OrdinalIgnoreCase), string.Empty).CleanupXMLString(); + nonAsciiReplaceRegex().Replace(originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase), string.Empty).NeutralizeBlockCommentDelimiters().CleanupXMLString(); #pragma warning disable CA1822 // Method should be static internal void AddRequestBuilderBody(CodeClass parentClass, string returnType, LanguageWriter writer, string? urlTemplateVarName = default, IEnumerable? pathParameters = default) { diff --git a/src/Kiota.Builder/Writers/Php/PhpConventionService.cs b/src/Kiota.Builder/Writers/Php/PhpConventionService.cs index b121060dcc..a7c20e7478 100644 --- a/src/Kiota.Builder/Writers/Php/PhpConventionService.cs +++ b/src/Kiota.Builder/Writers/Php/PhpConventionService.cs @@ -140,7 +140,7 @@ private string GetCollectionDocString(CodeParameter codeParameter) return codeParameter.Optional ? $"{doc}|null" : doc; } - internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase).Replace("*/", string.Empty, StringComparison.OrdinalIgnoreCase); + internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => originalDescription.Replace("\\", "/", StringComparison.OrdinalIgnoreCase).NeutralizeBlockCommentDelimiters(); public override bool WriteShortDescription(IDocumentedElement element, LanguageWriter writer, string prefix = "", string suffix = "") { ArgumentNullException.ThrowIfNull(writer); diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs index 7439ef1936..a623e7efba 100644 --- a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs @@ -1658,7 +1658,7 @@ public void SanitizesMethodDescriptionLinkLabel() writer.Write(method); var result = tw.ToString(); Assert.DoesNotContain("see */ more", result); - Assert.Contains("see more", result); + Assert.Contains("see * / more", result); Assert.Contains("@see (() => new JavaWriter("./", null)); } + [Theory] + [InlineData("**//", "** //")] // deletion would re-form "*/"; replacement must not + [InlineData("**\\/", "** //")] // backslash normalization must not re-form "*/" + [InlineData("*\u00e9/", "* /")] // non-ASCII strip must run before delimiter neutralization + [InlineData("*/", "* /")] + [InlineData("/*", "//*")] + [InlineData("normal description", "normal description")] + [InlineData("", "")] + public void RemoveInvalidDescriptionCharactersNeutralizesCommentBreakout(string input, string expected) + { + var result = JavaConventionService.RemoveInvalidDescriptionCharacters(input); + Assert.Equal(expected, result); + Assert.DoesNotContain("*/", result, StringComparison.Ordinal); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index 2229faa142..02e516720c 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -289,7 +289,7 @@ public async Task WriteRequestExecutorAsync() Assert.Contains("public function post(): Promise", result); Assert.Contains("$requestInfo = $this->createPostRequestInformation();", result); Assert.DoesNotContain("@link https://learn.microsoft.com/ Learning */ docs", result); - Assert.Contains("@link https://learn.microsoft.com/ Learning docs", result); + Assert.Contains("@link https://learn.microsoft.com/ Learning * / docs", result); Assert.Contains("'401' => [Error401::class, 'createFromDiscriminatorValue']", result); Assert.Contains("$result = $this->requestAdapter->sendPrimitiveAsync($requestInfo, StreamInterface::class, $errorMappings);", result); Assert.Contains("return $result;", result); diff --git a/tests/Kiota.Builder.Tests/Writers/Php/PhpWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/PhpWriterTests.cs index fb027c8e77..d17af3ff87 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/PhpWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/PhpWriterTests.cs @@ -32,6 +32,19 @@ public void WriteSomething() Assert.Throws(() => new PhpWriter(null, "graph")); Assert.Throws(() => new PhpWriter("./", null)); } + [Theory] + [InlineData("**//", "** //")] // deletion would re-form "*/"; replacement must not + [InlineData("**\\/", "** //")] // backslash normalization must not re-form "*/" + [InlineData("*/", "* /")] + [InlineData("/*", "//*")] + [InlineData("legit /* nested */ text", "legit //* nested * / text")] + [InlineData("normal description", "normal description")] + public void RemoveInvalidDescriptionCharactersNeutralizesCommentBreakout(string input, string expected) + { + var result = PhpConventionService.RemoveInvalidDescriptionCharacters(input); + Assert.Equal(expected, result); + Assert.DoesNotContain("*/", result, StringComparison.Ordinal); + } public void Dispose() { tw?.Dispose();