Skip to content

Commit f5b3e5a

Browse files
committed
fix issues with incorrectly escaped values
1 parent f9446c2 commit f5b3e5a

144 files changed

Lines changed: 418 additions & 354 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class CodegenOperation {
3333
hasErrorResponseObject; // if 4xx, 5xx responses have at least one error object defined
3434
public CodegenProperty returnProperty;
3535
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
36-
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
36+
returnContainer, summary, unescapedSummary, unescapedNotes, notes, baseName, defaultResponse;
3737
public CodegenDiscriminator discriminator;
3838
public List<Map<String, String>> consumes, produces, prioritizedContentTypes;
3939
public List<CodegenServer> servers = new ArrayList<CodegenServer>();

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
3434
isCollectionFormatMulti, isPrimitiveType, isModel, isExplode, isDeepObject, isMatrix, isAllowEmptyValue,
3535
isFormStyle, isSpaceDelimited, isPipeDelimited;
3636
public String baseName, paramName, dataType, datatypeWithEnum, dataFormat, contentType,
37-
collectionFormat, description, unescapedDescription, baseType, defaultValue, enumDefaultValue, enumName, style;
37+
collectionFormat, description, unescapedDescription, baseType, defaultValue, unescapedDefaultValue, enumDefaultValue, enumName, style;
3838

3939
public String nameInLowerCase; // property name in lower case
4040
public String nameInCamelCase; // property name in camel case (e.g. modifiedDate)

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
8181
@Getter @Setter
8282
public String unescapedDescription;
8383

84+
/**
85+
* The default value string without escape characters; use this in string literal contexts
86+
* (e.g. inside "..." or """...""") together with an appropriate escaping lambda.
87+
* Unlike {@code defaultValue}, this is never passed through escapeText() or escapeUnsafeCharacters().
88+
*/
89+
@Getter @Setter
90+
public String unescapedDefaultValue;
91+
8492
/**
8593
* maxLength validation for strings, see http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1
8694
*/

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties {
3333
public boolean is4xx;
3434
public boolean is5xx;
3535
public String message;
36+
public String unescapedMessage;
3637
public List<Map<String, Object>> examples;
3738
public String dataType;
3839
public String baseType;

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,17 @@ protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {
476476
.put("trimLineBreaks", new TrimLineBreaksLambda())
477477
.put("trimWhitespace", new TrimWhitespaceLambda())
478478
.put("trimTrailingWithNewLine", new TrimTrailingWhiteSpaceLambda(true))
479-
.put("trimTrailing", new TrimTrailingWhiteSpaceLambda(false));
479+
.put("trimTrailing", new TrimTrailingWhiteSpaceLambda(false))
480+
// Escapes text for use inside a Markdown table cell.
481+
// \\ must be replaced first to avoid double-escaping subsequent replacements.
482+
// $ is escaped to prevent LaTeX math mode ($...$) in markdown renderers that support it.
483+
.put("escapeMarkdown", (fragment, writer) -> writer.write(fragment.execute()
484+
.replace("\\", "\\\\")
485+
.replace("$", "\\$")
486+
.replace("|", "\\|")
487+
.replace("\r\n", " ")
488+
.replace("\n", " ")
489+
.replace("\r", " ")));
480490
}
481491

482492
private void registerMustacheLambdas() {
@@ -4315,6 +4325,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
43154325

43164326
// set the default value
43174327
property.defaultValue = toDefaultValue(property, p);
4328+
property.unescapedDefaultValue = p.getDefault() != null ? String.valueOf(p.getDefault()) : null;
43184329
property.defaultValueWithParam = toDefaultValueWithParam(name, p);
43194330

43204331
LOGGER.debug("debugging from property return: {}", property);
@@ -4738,6 +4749,7 @@ public CodegenOperation fromOperation(String path,
47384749
}
47394750

47404751
op.summary = escapeText(operation.getSummary());
4752+
op.unescapedSummary = operation.getSummary();
47414753
op.unescapedNotes = operation.getDescription();
47424754
op.notes = escapeText(operation.getDescription());
47434755
op.hasConsumes = false;
@@ -5092,6 +5104,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
50925104
}
50935105
r.schema = responseSchema;
50945106
r.message = escapeText(response.getDescription());
5107+
r.unescapedMessage = response.getDescription();
50955108

50965109
// adding examples to API responses
50975110
Map<String, Example> examples = ExamplesUtils.getExamplesFromResponse(openAPI, response);
@@ -5641,6 +5654,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set<String> imports)
56415654

56425655
// set default value
56435656
codegenParameter.defaultValue = toDefaultParameterValue(codegenProperty, parameterSchema);
5657+
codegenParameter.unescapedDefaultValue = parameterSchema.getDefault() != null ? String.valueOf(parameterSchema.getDefault()) : null;
56445658

56455659
finishUpdatingParameter(codegenParameter, parameter);
56465660
return codegenParameter;
@@ -7543,6 +7557,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set
75437557

75447558
// set default value
75457559
codegenParameter.defaultValue = toDefaultParameterValue(codegenProperty, propertySchema);
7560+
codegenParameter.unescapedDefaultValue = propertySchema.getDefault() != null ? String.valueOf(propertySchema.getDefault()) : null;
75467561

75477562
if (ModelUtils.isFileSchema(ps) && !ModelUtils.isStringSchema(ps)) {
75487563
// swagger v2 only, type file
@@ -8089,6 +8104,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
80898104
codegenParameter.baseName = "UNKNOWN_BASE_NAME";
80908105
codegenParameter.paramName = "UNKNOWN_PARAM_NAME";
80918106
codegenParameter.description = escapeText(body.getDescription());
8107+
codegenParameter.unescapedDescription = body.getDescription();
80928108
codegenParameter.required = body.getRequired() != null ? body.getRequired() : Boolean.FALSE;
80938109
codegenParameter.isBodyParam = Boolean.TRUE;
80948110
if (body.getExtensions() != null) {

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,20 @@ protected void updateModelForObject(CodegenModel m, Schema schema) {
12741274
@Override
12751275
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
12761276
return super.addMustacheLambdas()
1277-
.put("escapeDollar", new EscapeChar("(?<!\\\\)\\$", "\\\\\\$"));
1277+
.put("escapeDollar", new EscapeChar("(?<!\\\\)\\$", "\\\\\\$"))
1278+
// Replaces each $ with ${'$'} for use inside """...""" triple-quoted Kotlin strings,
1279+
// where backslash escapes are not available.
1280+
.put("escapeDollarInMultiline", new EscapeChar("\\$", "\\${'\\$'}"))
1281+
// Full escaping for raw values going into "..." double-quoted Kotlin strings.
1282+
// Handles \, $, " and also literal newline/tab/carriage-return chars.
1283+
// \\ replacement must be first to avoid double-escaping subsequent insertions.
1284+
.put("escapeInNormalString", (fragment, writer) -> writer.write(fragment.execute()
1285+
.replace("\\", "\\\\")
1286+
.replace("$", "\\$")
1287+
.replace("\"", "\\\"")
1288+
.replace("\n", "\\n")
1289+
.replace("\r", "\\r")
1290+
.replace("\t", "\\t")));
12781291
}
12791292

12801293
protected interface DataTypeAssigner {

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -962,17 +962,13 @@ public void processOpts() {
962962
// spring uses the jackson lib, and we disallow configuration.
963963
additionalProperties.put("jackson", "true");
964964

965-
// add lambda for mustache templates
966-
additionalProperties.put("lambdaEscapeInNormalString",
967-
(Mustache.Lambda) (fragment, writer) -> writer.write(fragment.execute().replaceAll("([$\"\\\\])", "\\\\$1")));
968-
additionalProperties.put("lambdaRemoveLineBreak",
969-
(Mustache.Lambda) (fragment, writer) -> writer.write(fragment.execute().replaceAll("[\\r\\n]", "")));
970965
}
971966

972967
@Override
973968
protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {
974969
return super.addMustacheLambdas()
975-
.put("escapeDoubleQuote", new EscapeLambda("\"", "\\\""));
970+
.put("escapeDoubleQuote", new EscapeLambda("\"", "\\\""))
971+
.put("removeLineBreak", (fragment, writer) -> writer.write(fragment.execute().replaceAll("[\\r\\n]", "")));
976972
}
977973

978974
@Override
@@ -1445,6 +1441,7 @@ private void markPropertyAsInherited(CodegenModel model, String baseName, String
14451441
p.defaultValue = dataType + "." + toEnumVarName(discriminatorValue, dataType);
14461442
} else {
14471443
p.defaultValue = "\"" + escapeText(discriminatorValue) + "\"";
1444+
p.unescapedDefaultValue = discriminatorValue;
14481445
}
14491446
}
14501447
});

modules/openapi-generator/src/main/resources/kotlin-client/api_doc.mustache

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# {{classname}}{{#description}}
2-
{{.}}{{/description}}
2+
{{#lambda.escapeMarkdown}}{{{.}}}{{/lambda.escapeMarkdown}}{{/description}}
33

44
All URIs are relative to *{{basePath}}*
55

66
| Method | HTTP request | Description |
77
| ------------- | ------------- | ------------- |
8-
{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} |
8+
{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#lambda.escapeMarkdown}}{{{unescapedSummary}}}{{/lambda.escapeMarkdown}} |
99
{{/operation}}{{/operations}}
1010

1111
{{#operations}}
1212
{{#operation}}
1313
<a id="{{operationId}}"></a>
1414
# **{{operationId}}**
15-
> {{#returnType}}{{.}}{{#nullableReturnType}}?{{/nullableReturnType}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
15+
> {{#returnType}}{{#lambda.escapeMarkdown}}{{{.}}}{{/lambda.escapeMarkdown}}{{#nullableReturnType}}?{{/nullableReturnType}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
1616

17-
{{summary}}{{#notes}}
17+
{{#lambda.escapeMarkdown}}{{{unescapedSummary}}}{{/lambda.escapeMarkdown}}{{#unescapedNotes}}
1818

19-
{{.}}{{/notes}}
19+
{{#lambda.escapeMarkdown}}{{{unescapedNotes}}}{{/lambda.escapeMarkdown}}{{/unescapedNotes}}
2020

2121
### Example
2222
```kotlin
@@ -27,7 +27,7 @@ All URIs are relative to *{{basePath}}*
2727
{{! TODO: Auth method documentation examples}}
2828
val apiInstance = {{{classname}}}()
2929
{{#allParams}}
30-
val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}}
30+
val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{#lambda.escapeMarkdown}}{{{unescapedDescription}}}{{/lambda.escapeMarkdown}}
3131
{{/allParams}}
3232
try {
3333
{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}
@@ -50,7 +50,7 @@ This endpoint does not need any parameter.
5050
| Name | Type | Description | Notes |
5151
| ------------- | ------------- | ------------- | ------------- |
5252
{{/-last}}
53-
| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} |
53+
| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{#lambda.escapeMarkdown}}{{{unescapedDescription}}}{{/lambda.escapeMarkdown}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{#lambda.escapeMarkdown}}{{{.}}}{{/lambda.escapeMarkdown}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{#lambda.escapeMarkdown}}{{{.}}}{{/lambda.escapeMarkdown}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} |
5454
{{/allParams}}
5555

5656
### Return type

modules/openapi-generator/src/main/resources/kotlin-client/class_doc.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
## Properties
44
| Name | Type | Description | Notes |
55
| ------------ | ------------- | ------------- | ------------- |
6-
{{#vars}}| **{{name}}** | {{#isEnum}}[**inline**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} |
6+
{{#vars}}| **{{name}}** | {{#isEnum}}[**inline**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}}{{/isEnum}} | {{#lambda.escapeMarkdown}}{{{unescapedDescription}}}{{/lambda.escapeMarkdown}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} |
77
{{/vars}}
88
{{#vars}}{{#isEnum}}
99

1010
<a id="{{{datatypeWithEnum}}}"></a>{{!NOTE: see java's resources "pojo_doc.mustache" once enums are fully implemented}}
1111
## Enum: {{baseName}}
1212
| Name | Value |
1313
| ---- | ----- |{{#allowableValues}}
14-
| {{name}} | {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}} |
14+
| {{name}} | {{#values}}{{#lambda.escapeMarkdown}}{{{.}}}{{/lambda.escapeMarkdown}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}} |
1515
{{/isEnum}}{{/vars}}

modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@SerializedName("{{{vendorExtensions.x-base-name-literal}}}")
1010
{{/gson}}
1111
{{#jackson}}
12-
@get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}")
12+
@get:JsonProperty("{{#lambda.escapeInNormalString}}{{{baseName}}}{{/lambda.escapeInNormalString}}")
1313
{{/jackson}}
1414
{{#kotlinx_serialization}}
1515
{{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}")
@@ -21,4 +21,4 @@
2121
{{#deprecated}}
2222
@Deprecated(message = "This property is deprecated.")
2323
{{/deprecated}}
24-
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}
24+
{{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{#isString}}{{^isEnum}}"{{#lambda.escapeInNormalString}}{{{unescapedDefaultValue}}}{{/lambda.escapeInNormalString}}"{{/isEnum}}{{#isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isString}}{{^isString}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/isString}}{{/defaultValue}}

0 commit comments

Comments
 (0)