[PHP/Dart/Python] Correctly escape strings in single quotes (Fixes #17582) (#19529)

* [PHP/Dart/Python] Correctly escape strings in single quotes (Fixes #17582)

* Move escapeTextInSingleQuotes method to DefaultCodegen, add docblock
This commit is contained in:
Julian Vennen 2024-11-09 09:50:12 +01:00 committed by GitHub
parent 1bda458882
commit 6bd3d53eee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 105 additions and 67 deletions

View File

@ -1122,6 +1122,20 @@ public class DefaultCodegen implements CodegenConfig {
.replace("\"", "\\\""));
}
/**
* This method escapes text to be used in a single quoted string
* @param input the input string
* @return the escaped string
*/
public String escapeTextInSingleQuotes(String input) {
if (input == null) {
return null;
}
return escapeText(input).replace("'", "\\'");
}
/**
* Escape characters while allowing new lines
*

View File

@ -753,7 +753,7 @@ public abstract class AbstractDartCodegen extends DefaultCodegen {
"int".equalsIgnoreCase(datatype)) {
return value;
} else {
return "'" + escapeText(value) + "'";
return "'" + escapeTextInSingleQuotes(value) + "'";
}
}

View File

@ -641,9 +641,10 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg
if ("String".equalsIgnoreCase(type) || p.isString) {
if (example == null) {
example = "'" + p.paramName + "_example'";
example = "'" + escapeTextInSingleQuotes(p.paramName) + "_example'";
} else {
example = escapeText(example);
}
example = escapeText(example);
} else if ("Integer".equals(type) || "int".equals(type)) {
if (example == null) {
example = "56";
@ -660,17 +661,17 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg
if (example == null) {
example = "/path/to/file.txt";
}
example = "\"" + escapeText(example) + "\"";
example = "'" + escapeTextInSingleQuotes(example) + "'";
} else if ("\\Date".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20";
}
example = "new \\DateTime(\"" + escapeText(example) + "\")";
example = "new \\DateTime('" + escapeTextInSingleQuotes(example) + "')";
} else if ("\\DateTime".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20T19:20:30+01:00";
}
example = "new \\DateTime(\"" + escapeText(example) + "\")";
example = "new \\DateTime('" + escapeTextInSingleQuotes(example) + "')";
} else if ("object".equals(type)) {
example = "new \\stdClass";
} else if (!languageSpecificPrimitives.contains(type)) {
@ -718,7 +719,7 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg
if ("int".equals(datatype) || "float".equals(datatype)) {
return value;
} else {
return "\'" + escapeText(value) + "\'";
return "'" + escapeTextInSingleQuotes(value) + "'";
}
}
@ -830,6 +831,16 @@ public abstract class AbstractPhpCodegen extends DefaultCodegen implements Codeg
return super.escapeText(input).trim();
}
@Override
public String escapeTextInSingleQuotes(String input) {
if (input == null) {
return input;
}
// Unescape double quotes because PHP keeps the backslashes if a character does not need to be escaped
return super.escapeTextInSingleQuotes(input).replace("\\\"", "\"");
}
public void escapeMediaType(List<CodegenOperation> operationList) {
for (CodegenOperation op : operationList) {
if (!op.hasProduces) {

View File

@ -412,7 +412,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
// Enum case:
example = schema.getEnum().get(0).toString();
if (ModelUtils.isStringSchema(schema)) {
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
}
if (null == example)
LOGGER.warn("Empty enum. Cannot built an example!");
@ -511,7 +511,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
if (additional.getEnum() != null && !additional.getEnum().isEmpty()) {
theKey = additional.getEnum().get(0).toString();
if (ModelUtils.isStringSchema(additional)) {
theKey = "'" + escapeText(theKey) + "'";
theKey = "'" + escapeTextInSingleQuotes(theKey) + "'";
}
}
example = "{\n" + indentationString + theKey + " : " + toExampleValueRecursive(additional, includedSchemas, indentation + 1) + "\n" + indentationString + "}";
@ -577,7 +577,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
}
if (ModelUtils.isStringSchema(schema)) {
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
}
return example;
@ -603,7 +603,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
if (example == null) {
example = p.paramName + "_example";
}
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
} else if ("Integer".equals(type) || "int".equals(type)) {
if (example == null) {
example = "56";
@ -620,17 +620,17 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
if (example == null) {
example = "/path/to/file";
}
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
} else if ("Date".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20";
}
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
} else if ("DateTime".equalsIgnoreCase(type)) {
if (example == null) {
example = "2013-10-20T19:20:30+01:00";
}
example = "'" + escapeText(example) + "'";
example = "'" + escapeTextInSingleQuotes(example) + "'";
} else if (!languageSpecificPrimitives.contains(type)) {
// type is a model class, e.g. User
example = this.packageName + "." + type + "()";
@ -1419,7 +1419,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
if ("int".equals(datatype) || "float".equals(datatype)) {
return value;
} else {
return "\'" + escapeText(value) + "\'";
return "'" + escapeTextInSingleQuotes(value) + "'";
}
}

View File

@ -231,15 +231,6 @@ public class PhpSymfonyServerCodegen extends AbstractPhpCodegen implements Codeg
return (outputFolder + File.separator + toSrcPath(controllerPackage, srcBasePath));
}
@Override
public String escapeText(String input) {
if (input != null) {
// Trim the string to avoid leading and trailing spaces.
return super.escapeText(input).trim();
}
return input;
}
@Override
public CodegenType getTag() {
return CodegenType.SERVER;
@ -577,15 +568,6 @@ public class PhpSymfonyServerCodegen extends AbstractPhpCodegen implements Codeg
}
}
@Override
public String toEnumValue(String value, String datatype) {
if ("int".equals(datatype) || "float".equals(datatype)) {
return value;
} else {
return "\"" + escapeText(value) + "\"";
}
}
/**
* Return the regular expression/JSON schema pattern (http://json-schema.org/latest/json-schema-validation.html#anchor33)
*

View File

@ -85,4 +85,15 @@ public class DartClientCodegenTest {
}
}
@Test(description = "Enum value with quotes (#17582)")
public void testEnumPropertyWithQuotes() {
final DartClientCodegen codegen = new DartClientCodegen();
Assert.assertEquals(codegen.toEnumValue("enum-value", "string"), "'enum-value'");
Assert.assertEquals(codegen.toEnumValue("won't fix", "string"), "'won\\'t fix'");
Assert.assertEquals(codegen.toEnumValue("\"", "string"), "'\\\"'");
Assert.assertEquals(codegen.toEnumValue("1.0", "number"), "1.0");
Assert.assertEquals(codegen.toEnumValue("1", "int"), "1");
}
}

View File

@ -176,4 +176,13 @@ public class AbstractPhpCodegenTest {
CodegenProperty cp1 = cm1.vars.get(0);
Assert.assertEquals(cp1.getDefaultValue(), "'VALUE'");
}
@Test(description = "Enum value with quotes (#17582)")
public void testEnumPropertyWithQuotes() {
Assert.assertEquals(codegen.toEnumValue("enum-value", "string"), "'enum-value'");
Assert.assertEquals(codegen.toEnumValue("won't fix", "string"), "'won\\'t fix'");
Assert.assertEquals(codegen.toEnumValue("\"", "string"), "'\"'");
Assert.assertEquals(codegen.toEnumValue("1.0", "float"), "1.0");
Assert.assertEquals(codegen.toEnumValue("1", "int"), "1");
}
}

View File

@ -530,4 +530,15 @@ public class PythonClientCodegenTest {
assertFileContains(apiFile.toPath(), "_header_params['X-CUSTOM_CONSTANT_HEADER'] = 'CONSTANT_VALUE'");
assertFileContains(apiFile.toPath(), "_query_params.append(('CONSTANT_QUERY_STRING_KEY', 'CONSTANT_QUERY_STRING_VALUE'))");
}
@Test(description = "Enum value with quotes (#17582)")
public void testEnumPropertyWithQuotes() {
final PythonClientCodegen codegen = new PythonClientCodegen();
Assert.assertEquals(codegen.toEnumValue("enum-value", "string"), "'enum-value'");
Assert.assertEquals(codegen.toEnumValue("won't fix", "string"), "'won\\'t fix'");
Assert.assertEquals(codegen.toEnumValue("\"", "string"), "'\\\"'");
Assert.assertEquals(codegen.toEnumValue("1.0", "float"), "1.0");
Assert.assertEquals(codegen.toEnumValue("1", "int"), "1");
}
}

View File

@ -92,7 +92,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$body = "/path/to/file.txt"; // \Psr\Http\Message\StreamInterface
$body = '/path/to/file.txt'; // \Psr\Http\Message\StreamInterface
try {
$result = $apiInstance->testBodyApplicationOctetstreamBinary($body);
@ -148,7 +148,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$files = array("/path/to/file.txt"); // \Psr\Http\Message\StreamInterface[]
$files = array('/path/to/file.txt'); // \Psr\Http\Message\StreamInterface[]
try {
$result = $apiInstance->testBodyMultipartFormdataArrayOfBinary($files);
@ -204,7 +204,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$my_file = "/path/to/file.txt"; // \Psr\Http\Message\StreamInterface
$my_file = '/path/to/file.txt'; // \Psr\Http\Message\StreamInterface
try {
$result = $apiInstance->testBodyMultipartFormdataSingleBinary($my_file);

View File

@ -97,8 +97,8 @@ $apiInstance = new OpenAPI\Client\Api\QueryApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$datetime_query = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime
$date_query = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime
$datetime_query = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime
$date_query = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime
$string_query = 'string_query_example'; // string
try {

View File

@ -92,7 +92,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$body = "/path/to/file.txt"; // \SplFileObject
$body = '/path/to/file.txt'; // \SplFileObject
try {
$result = $apiInstance->testBodyApplicationOctetstreamBinary($body);
@ -148,7 +148,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$files = array("/path/to/file.txt"); // \SplFileObject[]
$files = array('/path/to/file.txt'); // \SplFileObject[]
try {
$result = $apiInstance->testBodyMultipartFormdataArrayOfBinary($files);
@ -204,7 +204,7 @@ $apiInstance = new OpenAPI\Client\Api\BodyApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$my_file = "/path/to/file.txt"; // \SplFileObject
$my_file = '/path/to/file.txt'; // \SplFileObject
try {
$result = $apiInstance->testBodyMultipartFormdataSingleBinary($my_file);

View File

@ -97,8 +97,8 @@ $apiInstance = new OpenAPI\Client\Api\QueryApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$datetime_query = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime
$date_query = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime
$datetime_query = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime
$date_query = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime
$string_query = 'string_query_example'; // string
try {

View File

@ -608,7 +608,7 @@ $apiInstance = new OpenAPI\Client\Api\FakeApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$body = "/path/to/file.txt"; // \SplFileObject | image to upload
$body = '/path/to/file.txt'; // \SplFileObject | image to upload
try {
$apiInstance->testBodyWithBinary($body);
@ -844,9 +844,9 @@ $int32 = 56; // int | None
$int64 = 56; // int | None
$float = 3.4; // float | None
$string = 'string_example'; // string | None
$binary = "/path/to/file.txt"; // \SplFileObject | None
$date = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$date_time = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$binary = '/path/to/file.txt'; // \SplFileObject | None
$date = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$date_time = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$password = 'password_example'; // string | None
$callback = 'callback_example'; // string | None

View File

@ -514,7 +514,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
);
$pet_id = 56; // int | ID of pet to update
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
$file = "/path/to/file.txt"; // \SplFileObject | file to upload
$file = '/path/to/file.txt'; // \SplFileObject | file to upload
try {
$result = $apiInstance->uploadFile($pet_id, $additional_metadata, $file);
@ -577,7 +577,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
$config
);
$pet_id = 56; // int | ID of pet to update
$required_file = "/path/to/file.txt"; // \SplFileObject | file to upload
$required_file = '/path/to/file.txt'; // \SplFileObject | file to upload
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
try {

View File

@ -669,7 +669,7 @@ $apiInstance = new OpenAPI\Client\Api\FakeApi(
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$body = "/path/to/file.txt"; // \SplFileObject | image to upload
$body = '/path/to/file.txt'; // \SplFileObject | image to upload
try {
$apiInstance->testBodyWithBinary($body);
@ -905,9 +905,9 @@ $int32 = 56; // int | None
$int64 = 56; // int | None
$float = 3.4; // float | None
$string = 'string_example'; // string | None
$binary = "/path/to/file.txt"; // \SplFileObject | None
$date = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$date_time = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$binary = '/path/to/file.txt'; // \SplFileObject | None
$date = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$date_time = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$password = 'password_example'; // string | None
$callback = 'callback_example'; // string | None

View File

@ -514,7 +514,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
);
$pet_id = 56; // int | ID of pet to update
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
$file = "/path/to/file.txt"; // \SplFileObject | file to upload
$file = '/path/to/file.txt'; // \SplFileObject | file to upload
try {
$result = $apiInstance->uploadFile($pet_id, $additional_metadata, $file);
@ -577,7 +577,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
$config
);
$pet_id = 56; // int | ID of pet to update
$required_file = "/path/to/file.txt"; // \SplFileObject | file to upload
$required_file = '/path/to/file.txt'; // \SplFileObject | file to upload
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
try {

View File

@ -669,7 +669,7 @@ $apiInstance = new OpenAPI\Client\Api\FakeApi(
// This is optional, `Psr18ClientDiscovery` will be used to find http client. For instance `GuzzleHttp\Client` implements that interface
new GuzzleHttp\Client()
);
$body = "/path/to/file.txt"; // \SplFileObject | image to upload
$body = '/path/to/file.txt'; // \SplFileObject | image to upload
try {
$apiInstance->testBodyWithBinary($body);
@ -905,9 +905,9 @@ $int32 = 56; // int | None
$int64 = 56; // int | None
$float = 3.4; // float | None
$string = 'string_example'; // string | None
$binary = "/path/to/file.txt"; // \SplFileObject | None
$date = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$date_time = new \DateTime("2013-10-20T19:20:30+01:00"); // \DateTime | None
$binary = '/path/to/file.txt'; // \SplFileObject | None
$date = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$date_time = new \DateTime('2013-10-20T19:20:30+01:00'); // \DateTime | None
$password = 'password_example'; // string | None
$callback = 'callback_example'; // string | None

View File

@ -468,7 +468,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
);
$pet_id = 56; // int | ID of pet to update
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
$file = "/path/to/file.txt"; // \SplFileObject | file to upload
$file = '/path/to/file.txt'; // \SplFileObject | file to upload
try {
$result = $apiInstance->uploadFile($pet_id, $additional_metadata, $file);
@ -531,7 +531,7 @@ $apiInstance = new OpenAPI\Client\Api\PetApi(
$config
);
$pet_id = 56; // int | ID of pet to update
$required_file = "/path/to/file.txt"; // \SplFileObject | file to upload
$required_file = '/path/to/file.txt'; // \SplFileObject | file to upload
$additional_metadata = 'additional_metadata_example'; // string | Additional data to pass to server
try {

View File

@ -255,7 +255,7 @@ class PetController extends Controller
$asserts = [];
$asserts[] = new Assert\NotNull();
$asserts[] = new Assert\All([
new Assert\Choice([ "available", "pending", "sold" ])
new Assert\Choice([ 'available', 'pending', 'sold' ])
]);
$asserts[] = new Assert\All([
new Assert\Type("string"),

View File

@ -44,9 +44,9 @@ use JMS\Serializer\Annotation\SerializedName;
*/
enum EnumStringModel: string
{
case AVAILABLE = "available";
case PENDING = "pending";
case SOLD = "sold";
case AVAILABLE = 'available';
case PENDING = 'pending';
case SOLD = 'sold';
}

View File

@ -84,7 +84,7 @@ class Order
* @SerializedName("status")
* @Type("string")
*/
#[Assert\Choice(["placed", "approved", "delivered"])]
#[Assert\Choice(['placed', 'approved', 'delivered'])]
#[Assert\Type("string")]
protected ?string $status = null;

View File

@ -98,7 +98,7 @@ class Pet
* @SerializedName("status")
* @Type("string")
*/
#[Assert\Choice(["available", "pending", "sold"])]
#[Assert\Choice(['available', 'pending', 'sold'])]
#[Assert\Type("string")]
protected ?string $status = null;