avoid code injection in php api client

This commit is contained in:
wing328
2016-06-27 21:51:27 +08:00
parent 31092585f0
commit 1638adb79e
49 changed files with 608 additions and 599 deletions

View File

@@ -57,6 +57,8 @@ public interface CodegenConfig {
String escapeText(String text);
String escapeUnsafeCharacters(String input);
String escapeReservedWord(String name);
String getTypeDeclaration(Property p);

View File

@@ -330,13 +330,29 @@ public class DefaultCodegen {
// override with any special text escaping logic
@SuppressWarnings("static-method")
public String escapeText(String input) {
if (input != null) {
// remove \t, \n, \r
// repalce \ with \\
// repalce " with \"
// outter unescape to retain the original multi-byte characters
return StringEscapeUtils.unescapeJava(StringEscapeUtils.escapeJava(input).replace("\\/", "/")).replaceAll("[\\t\\n\\r]"," ").replace("\\", "\\\\").replace("\"", "\\\"");
if (input == null) {
return input;
}
// remove \t, \n, \r
// repalce \ with \\
// repalce " with \"
// outter unescape to retain the original multi-byte characters
// finally escalate characters avoiding code injection
return escapeUnsafeCharacters(StringEscapeUtils.unescapeJava(StringEscapeUtils.escapeJava(input).replace("\\/", "/")).replaceAll("[\\t\\n\\r]"," ").replace("\\", "\\\\").replace("\"", "\\\""));
}
/**
* override with any special text escaping logic to handle unsafe
* characters so as to avoid code injection
* @param input String to be cleaned up
* @return string with unsafe characters removed or escaped
*/
public String escapeUnsafeCharacters(String input) {
// doing nothing by default and code generator should implement
// the logic to prevent code injection
// later we'll make this method abstract to make sure
// code generator implements this method
return input;
}

View File

@@ -144,10 +144,10 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
if (swagger.getInfo() != null) {
Info info = swagger.getInfo();
if (info.getTitle() != null) {
config.additionalProperties().put("appName", info.getTitle());
config.additionalProperties().put("appName", config.escapeText(info.getTitle()));
}
if (info.getVersion() != null) {
config.additionalProperties().put("appVersion", info.getVersion());
config.additionalProperties().put("appVersion", config.escapeText(info.getVersion()));
}
if (info.getDescription() != null) {
config.additionalProperties().put("appDescription",
@@ -155,25 +155,25 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
}
if (info.getContact() != null) {
Contact contact = info.getContact();
config.additionalProperties().put("infoUrl", contact.getUrl());
config.additionalProperties().put("infoUrl", config.escapeText(contact.getUrl()));
if (contact.getEmail() != null) {
config.additionalProperties().put("infoEmail", contact.getEmail());
config.additionalProperties().put("infoEmail", config.escapeText(contact.getEmail()));
}
}
if (info.getLicense() != null) {
License license = info.getLicense();
if (license.getName() != null) {
config.additionalProperties().put("licenseInfo", license.getName());
config.additionalProperties().put("licenseInfo", config.escapeText(license.getName()));
}
if (license.getUrl() != null) {
config.additionalProperties().put("licenseUrl", license.getUrl());
config.additionalProperties().put("licenseUrl", config.escapeText(license.getUrl()));
}
}
if (info.getVersion() != null) {
config.additionalProperties().put("version", info.getVersion());
config.additionalProperties().put("version", config.escapeText(info.getVersion()));
}
if (info.getTermsOfService() != null) {
config.additionalProperties().put("termsOfService", info.getTermsOfService());
config.additionalProperties().put("termsOfService", config.escapeText(info.getTermsOfService()));
}
}
@@ -184,7 +184,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
StringBuilder hostBuilder = new StringBuilder();
String scheme;
if (swagger.getSchemes() != null && swagger.getSchemes().size() > 0) {
scheme = swagger.getSchemes().get(0).toValue();
scheme = config.escapeText(swagger.getSchemes().get(0).toValue());
} else {
scheme = "https";
}

View File

@@ -662,4 +662,10 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig {
}
return objs;
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "");
}
}

View File

@@ -85,11 +85,15 @@ use \{{invokerPackage}}\ObjectSerializer;
/**
* Operation {{{operationId}}}
*
* {{{summary}}}.
*/
{{#allParams}} // * @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}
/**
* {{{summary}}}
*
{{#description}}
* {{.}}
*
{{/description}}
{{#allParams}}
* @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}
* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
* @throws \{{invokerPackage}}\ApiException on non-2xx response
*/
@@ -99,21 +103,25 @@ use \{{invokerPackage}}\ObjectSerializer;
return $response;
}
/**
* Operation {{{operationId}}}WithHttpInfo
*
* {{{summary}}}.
*/
{{#allParams}} // * @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}
/**
* {{{summary}}}
*
{{#description}}
* {{.}}
*
{{/description}}
{{#allParams}}
* @param {{dataType}} ${{paramName}} {{description}} {{#required}}(required){{/required}}{{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}
* @return Array of {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}null{{/returnType}}, HTTP status code, HTTP response headers (array of strings)
* @throws \{{invokerPackage}}\ApiException on non-2xx response
*/
public function {{operationId}}WithHttpInfo({{#allParams}}${{paramName}}{{^required}} = null{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{
{{#allParams}}{{#required}}
{{#allParams}}
{{#required}}
// verify the required parameter '{{paramName}}' is set
if (${{paramName}} === null) {
throw new \InvalidArgumentException('Missing the required parameter ${{paramName}} when calling {{operationId}}');
@@ -148,7 +156,6 @@ use \{{invokerPackage}}\ObjectSerializer;
{{/hasValidation}}
{{/allParams}}
// parse inputs
$resourcePath = "{{path}}";
$httpBody = '';
@@ -161,7 +168,8 @@ use \{{invokerPackage}}\ObjectSerializer;
}
$headerParams['Content-Type'] = $this->apiClient->selectHeaderContentType(array({{#consumes}}'{{{mediaType}}}'{{#hasMore}},{{/hasMore}}{{/consumes}}));
{{#queryParams}}// query params
{{#queryParams}}
// query params
{{#collectionFormat}}
if (is_array(${{paramName}})) {
${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}', true);
@@ -169,8 +177,10 @@ use \{{invokerPackage}}\ObjectSerializer;
{{/collectionFormat}}
if (${{paramName}} !== null) {
$queryParams['{{baseName}}'] = $this->apiClient->getSerializer()->toQueryValue(${{paramName}});
}{{/queryParams}}
{{#headerParams}}// header params
}
{{/queryParams}}
{{#headerParams}}
// header params
{{#collectionFormat}}
if (is_array(${{paramName}})) {
${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}');
@@ -178,8 +188,10 @@ use \{{invokerPackage}}\ObjectSerializer;
{{/collectionFormat}}
if (${{paramName}} !== null) {
$headerParams['{{baseName}}'] = $this->apiClient->getSerializer()->toHeaderValue(${{paramName}});
}{{/headerParams}}
{{#pathParams}}// path params
}
{{/headerParams}}
{{#pathParams}}
// path params
{{#collectionFormat}}
if (is_array(${{paramName}})) {
${{paramName}} = $this->apiClient->getSerializer()->serializeCollection(${{paramName}}, '{{collectionFormat}}');
@@ -191,11 +203,13 @@ use \{{invokerPackage}}\ObjectSerializer;
$this->apiClient->getSerializer()->toPathValue(${{paramName}}),
$resourcePath
);
}{{/pathParams}}
}
{{/pathParams}}
// default format to json
$resourcePath = str_replace("{format}", "json", $resourcePath);
{{#formParams}}// form params
{{#formParams}}
// form params
if (${{paramName}} !== null) {
{{#isFile}}
// PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
@@ -209,12 +223,14 @@ use \{{invokerPackage}}\ObjectSerializer;
{{^isFile}}
$formParams['{{baseName}}'] = $this->apiClient->getSerializer()->toFormValue(${{paramName}});
{{/isFile}}
}{{/formParams}}
}
{{/formParams}}
{{#bodyParams}}// body params
$_tempBody = null;
if (isset(${{paramName}})) {
$_tempBody = ${{paramName}};
}{{/bodyParams}}
}
{{/bodyParams}}
// for model (json/xml)
if (isset($_tempBody)) {
@@ -222,19 +238,26 @@ use \{{invokerPackage}}\ObjectSerializer;
} elseif (count($formParams) > 0) {
$httpBody = $formParams; // for HTTP post (form)
}
{{#authMethods}}{{#isApiKey}}
{{#authMethods}}
{{#isApiKey}}
// this endpoint requires API key authentication
$apiKey = $this->apiClient->getApiKeyWithPrefix('{{keyParamName}}');
if (strlen($apiKey) !== 0) {
{{#isKeyInHeader}}$headerParams['{{keyParamName}}'] = $apiKey;{{/isKeyInHeader}}{{#isKeyInQuery}}$queryParams['{{keyParamName}}'] = $apiKey;{{/isKeyInQuery}}
}{{/isApiKey}}
{{#isBasic}}// this endpoint requires HTTP basic authentication
}
{{/isApiKey}}
{{#isBasic}}
// this endpoint requires HTTP basic authentication
if (strlen($this->apiClient->getConfig()->getUsername()) !== 0 or strlen($this->apiClient->getConfig()->getPassword()) !== 0) {
$headerParams['Authorization'] = 'Basic ' . base64_encode($this->apiClient->getConfig()->getUsername() . ":" . $this->apiClient->getConfig()->getPassword());
}{{/isBasic}}{{#isOAuth}}// this endpoint requires OAuth (access token)
}
{{/isBasic}}
{{#isOAuth}}
// this endpoint requires OAuth (access token)
if (strlen($this->apiClient->getConfig()->getAccessToken()) !== 0) {
$headerParams['Authorization'] = 'Bearer ' . $this->apiClient->getConfig()->getAccessToken();
}{{/isOAuth}}
}
{{/isOAuth}}
{{/authMethods}}
// make the API Call
try {
@@ -268,6 +291,7 @@ use \{{invokerPackage}}\ObjectSerializer;
throw $e;
}
}
{{/operation}}
}
{{/operations}}

View File

@@ -24,8 +24,6 @@ namespace {{modelPackage}};
use \ArrayAccess;
/**
* {{classname}} Class Doc Comment
*

View File

@@ -2,11 +2,12 @@
{{#appName}}
* {{{appName}}}
*
{{/appName}} */
{{/appName}}
{{#appDescription}}
//* {{{appDescription}}}
* {{{appDescription}}}
*
{{/appDescription}}
/* {{#version}}OpenAPI spec version: {{{version}}}{{/version}}
* {{#version}}OpenAPI spec version: {{{version}}}{{/version}}
* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}}
* Generated by: https://github.com/swagger-api/swagger-codegen.git
*

View File

@@ -1,16 +1,16 @@
swagger: '2.0'
info:
description: 'This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: " \ '
version: 1.0.0
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'
description: 'This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: " \ */ =end'
version: 1.0.0 */ =end
title: Swagger Petstore */ =end
termsOfService: 'http://swagger.io/terms/ */ =end'
contact:
email: apiteam@swagger.io
email: apiteam@swagger.io */ =end
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
host: petstore.swagger.io
basePath: /v2
name: Apache 2.0 */ =end
url: 'http://www.apache.org/licenses/LICENSE-2.0.html */ =end'
host: petstore.swagger.io */ =end
basePath: /v2 */ =end
tags:
- name: pet
description: Everything about your Pets
@@ -25,7 +25,7 @@ tags:
description: Find out more about our store
url: 'http://swagger.io'
schemes:
- http
- http */ end
paths:
/pet:
post:
@@ -561,6 +561,26 @@ paths:
description: User not found
/fake:
put:
tags:
- fake
summary: To test code injection */ =end
descriptions: To test code injection */ =end
operationId: testCodeInject */ =end
consumes:
- application/json
- '*/ =end'
produces:
- application/json
- '*/ end'
parameters:
- name: test code inject */ =end
type: string
in: formData
description: To test code injection */ =end
responses:
'400':
description: To test code injection */ =end
get:
tags:
- fake