From e8ac630ca5470b77ef30cd36d4ec7a1f72835eee Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Mon, 31 Dec 2018 07:02:45 +0500 Subject: [PATCH] [Slim] Encode path to support non-latin characters (#1687) * [Slim] Add encodePath method * [Slim] Add tests for encodePath method * [Slim] Use unescaped path in router Both variables basePathWithoutHost and path are already urlEncoded in codegen itself. Builtin html encoding in mustache is redundant. We can use these raw codegen values with no fear. * [Slim] Refresh samples --- .../openapitools/codegen/CodegenConfig.java | 2 + .../openapitools/codegen/DefaultCodegen.java | 6 ++ .../codegen/DefaultGenerator.java | 4 +- .../languages/PhpSlimServerCodegen.java | 60 +++++++++++++++++++ .../php-slim-server/SlimRouter.mustache | 2 +- .../slim/PhpSlimServerCodegenTest.java | 47 +++++++++++++++ .../php-slim/lib/SlimRouter.php | 2 +- 7 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/PhpSlimServerCodegenTest.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index 694377a1d83..3cc6fc776b8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -81,6 +81,8 @@ public interface CodegenConfig { String escapeTextWhileAllowingNewLines(String text); + String encodePath(String text); + String escapeUnsafeCharacters(String input); String escapeReservedWord(String name); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 32a16ec7f60..e02cb08984c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -557,6 +557,12 @@ public class DefaultCodegen implements CodegenConfig { .replace("\"", "\\\"")); } + // override with any special encoding and escaping logic + @SuppressWarnings("static-method") + public String encodePath(String input) { + return escapeText(input); + } + /** * override with any special text escaping logic to handle unsafe * characters so as to avoid code injection diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 36d759d9e6c..1a0791395ed 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -537,9 +537,9 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { } }); Map operation = processOperations(config, tag, ops, allModels); - + URL url = URLPathUtils.getServerURL(openAPI); operation.put("basePath", basePath); - operation.put("basePathWithoutHost", basePathWithoutHost); + operation.put("basePathWithoutHost", config.encodePath(url.getPath()).replaceAll("/$", "")); operation.put("contextPath", contextPath); operation.put("baseName", tag); operation.put("apiPackage", config.apiPackage()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlimServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlimServerCodegen.java index d8480e4cc9e..34e971f6073 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlimServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSlimServerCodegen.java @@ -31,6 +31,13 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.net.URLEncoder; +import org.apache.commons.lang3.StringEscapeUtils; +import java.io.UnsupportedEncodingException; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Schema; public class PhpSlimServerCodegen extends AbstractPhpCodegen { private static final Logger LOGGER = LoggerFactory.getLogger(PhpSlimServerCodegen.class); @@ -179,4 +186,57 @@ public class PhpSlimServerCodegen extends AbstractPhpCodegen { operations.put(USER_CLASSNAME_KEY, classname); } + @Override + public String encodePath(String input) { + if (input == null) { + return input; + } + + // from DefaultCodegen.java + // remove \t, \n, \r + // replace \ with \\ + // replace " with \" + // outter unescape to retain the original multi-byte characters + // finally escalate characters avoiding code injection + input = super.escapeUnsafeCharacters( + StringEscapeUtils.unescapeJava( + StringEscapeUtils.escapeJava(input) + .replace("\\/", "/")) + .replaceAll("[\\t\\n\\r]", " ") + .replace("\\", "\\\\")); + // .replace("\"", "\\\"")); + + // from AbstractPhpCodegen.java + // Trim the string to avoid leading and trailing spaces. + input = input.trim(); + try { + + input = URLEncoder.encode(input, "UTF-8") + .replaceAll("\\+", "%20") + .replaceAll("\\%2F", "/") + .replaceAll("\\%7B", "{") // keep { part of complex placeholders + .replaceAll("\\%7D", "}") // } part + .replaceAll("\\%5B", "[") // [ part + .replaceAll("\\%5D", "]") // ] part + .replaceAll("\\%3A", ":") // : part + .replaceAll("\\%2B", "+") // + part + .replaceAll("\\%5C\\%5Cd", "\\\\d"); // \d part + } catch (UnsupportedEncodingException e) { + // continue + LOGGER.error(e.getMessage(), e); + } + return input; + } + + @Override + public CodegenOperation fromOperation(String path, + String httpMethod, + Operation operation, + Map schemas, + OpenAPI openAPI) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, schemas, openAPI); + op.path = encodePath(path); + return op; + } + } diff --git a/modules/openapi-generator/src/main/resources/php-slim-server/SlimRouter.mustache b/modules/openapi-generator/src/main/resources/php-slim-server/SlimRouter.mustache index c68b2b2022c..09b8925e9a3 100644 --- a/modules/openapi-generator/src/main/resources/php-slim-server/SlimRouter.mustache +++ b/modules/openapi-generator/src/main/resources/php-slim-server/SlimRouter.mustache @@ -64,7 +64,7 @@ class SlimRouter [ 'httpMethod' => '{{httpMethod}}', 'basePathWithoutHost' => '{{{basePathWithoutHost}}}', - 'path' => '{{path}}', + 'path' => '{{{path}}}', 'apiPackage' => '{{apiPackage}}', 'classname' => '{{classname}}', 'userClassname' => '{{userClassname}}', diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/PhpSlimServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/PhpSlimServerCodegenTest.java new file mode 100644 index 00000000000..329b5e9195f --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/PhpSlimServerCodegenTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.slim; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import org.openapitools.codegen.languages.PhpSlimServerCodegen; + +public class PhpSlimServerCodegenTest { + + @Test + public void testEncodePath() { + final PhpSlimServerCodegen codegen = new PhpSlimServerCodegen(); + + Assert.assertEquals(codegen.encodePath("/ ' \" =end -- \\r\\n \\n \\r/v2 *_/ ' \" =end -- \\r\\n \\n \\r/fake"), "/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr/v2%20*_/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr/fake"); + Assert.assertEquals(codegen.encodePath("/o\'\"briens/v2/o\'\"henry/fake"), "/o%27%22briens/v2/o%27%22henry/fake"); + Assert.assertEquals(codegen.encodePath("/comedians/Chris D\'Elia"), "/comedians/Chris%20D%27Elia"); + Assert.assertEquals(codegen.encodePath("/разработчики/Юрий Беленко"), "/%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87%D0%B8%D0%BA%D0%B8/%D0%AE%D1%80%D0%B8%D0%B9%20%D0%91%D0%B5%D0%BB%D0%B5%D0%BD%D0%BA%D0%BE"); + Assert.assertEquals(codegen.encodePath("/text with multilines \\\n\\\t\\\r"), "/text%20with%20multilines%20%5C%5C%20%5C%5C%20%5C%5C"); + Assert.assertEquals(codegen.encodePath("/path with argument {value}"), "/path%20with%20argument%20{value}"); + + // few examples from Slim documentation + Assert.assertEquals(codegen.encodePath("/users[/{id}]"), "/users[/{id}]"); + Assert.assertEquals(codegen.encodePath("/news[/{year}[/{month}]]"), "/news[/{year}[/{month}]]"); + Assert.assertEquals(codegen.encodePath("/news[/{params:.*}]"), "/news[/{params:.*}]"); + Assert.assertEquals(codegen.encodePath("/users/{id:[0-9]+}"), "/users/{id:[0-9]+}"); + + // from FastRoute\RouteParser\Std.php + Assert.assertEquals(codegen.encodePath("/user/{name}[/{id:[0-9]+}]"), "/user/{name}[/{id:[0-9]+}]"); + Assert.assertEquals(codegen.encodePath("/fixedRoutePart/{varName}[/moreFixed/{varName2:\\d+}]"), "/fixedRoutePart/{varName}[/moreFixed/{varName2:\\d+}]"); + } +} diff --git a/samples/server/petstore-security-test/php-slim/lib/SlimRouter.php b/samples/server/petstore-security-test/php-slim/lib/SlimRouter.php index f57b96bed30..df080b40c38 100644 --- a/samples/server/petstore-security-test/php-slim/lib/SlimRouter.php +++ b/samples/server/petstore-security-test/php-slim/lib/SlimRouter.php @@ -53,7 +53,7 @@ class SlimRouter private $operations = [ [ 'httpMethod' => 'PUT', - 'basePathWithoutHost' => '/ ' \" =end -- \\r\\n \\n \\r/v2 *_/ ' \" =end -- \\r\\n \\n \\r', + 'basePathWithoutHost' => '/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr/v2%20*_/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr', 'path' => '/fake', 'apiPackage' => 'OpenAPIServer\Api', 'classname' => 'AbstractFakeApi',