Skip to content

Commit

Permalink
Merge pull request from GHSA-7wgr-7666-7pwj
Browse files Browse the repository at this point in the history
* All strings used as file/directory names are now sanitized to address the path traversal vulnerabilities

* Switched calls to utils.spinal_case to utils.kebab_case

Co-authored-by: Ethan Mann <emann@triaxtec.com>
  • Loading branch information
dbanty and emann committed Aug 13, 2020
1 parent d6970d6 commit 3e7dfae
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 17 deletions.
3 changes: 0 additions & 3 deletions end_to_end_tests/regen_golden_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
from openapi_python_client.cli import app

if __name__ == "__main__":
from .fastapi_app import generate_openapi_json

generate_openapi_json()
runner = CliRunner()
openapi_path = Path(__file__).parent / "fastapi_app" / "openapi.json"
gm_path = Path(__file__).parent / "golden-master"
Expand Down
5 changes: 3 additions & 2 deletions openapi_python_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ def _get_document(*, url: Optional[str], path: Optional[Path]) -> Union[Dict[str


class Project:
TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "spinalcase": utils.spinal_case}
TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case}
project_name_override: Optional[str] = None
package_name_override: Optional[str] = None

def __init__(self, *, openapi: GeneratorData) -> None:
self.openapi: GeneratorData = openapi
self.env: Environment = Environment(loader=PackageLoader(__package__), trim_blocks=True, lstrip_blocks=True)

self.project_name: str = self.project_name_override or f"{openapi.title.replace(' ', '-').lower()}-client"
self.project_name: str = self.project_name_override or f"{utils.kebab_case(openapi.title).lower()}-client"
self.project_dir: Path = Path.cwd() / self.project_name

self.package_name: str = self.package_name_override or self.project_name.replace("-", "_")
Expand Down Expand Up @@ -231,6 +231,7 @@ def _build_api(self) -> None:
endpoint_template = self.env.get_template("endpoint_module.pyi")
async_endpoint_template = self.env.get_template("async_endpoint_module.pyi")
for tag, collection in self.openapi.endpoint_collections_by_tag.items():
tag = utils.snake_case(tag)
module_path = api_dir / f"{tag}.py"
module_path.write_text(endpoint_template.render(collection=collection))
async_module_path = async_api_dir / f"{tag}.py"
Expand Down
4 changes: 2 additions & 2 deletions openapi_python_client/templates/endpoint_macros.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{% if endpoint.header_parameters %}
{% for parameter in endpoint.header_parameters %}
{% if parameter.required %}
headers["{{ parameter.python_name | spinalcase}}"] = {{ parameter.python_name }}
headers["{{ parameter.python_name | kebabcase}}"] = {{ parameter.python_name }}
{% else %}
if {{ parameter.python_name }} is not None:
headers["{{ parameter.python_name | spinalcase}}"] = {{ parameter.python_name }}
headers["{{ parameter.python_name | kebabcase}}"] = {{ parameter.python_name }}
{% endif %}
{% endfor %}
{% endif %}
Expand Down
18 changes: 13 additions & 5 deletions openapi_python_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@
import stringcase


def snake_case(value: str) -> str:
def _sanitize(value: str) -> str:
return re.sub(r"[^\w _-]+", "", value)


def group_title(value: str) -> str:
value = re.sub(r"([A-Z]{2,})([A-Z][a-z]|[ -_]|$)", lambda m: m.group(1).title() + m.group(2), value.strip())
value = re.sub(r"(^|[ _-])([A-Z])", lambda m: m.group(1) + m.group(2).lower(), value)
return stringcase.snakecase(value)
return value


def snake_case(value: str) -> str:
return stringcase.snakecase(group_title(_sanitize(value)))


def pascal_case(value: str) -> str:
return stringcase.pascalcase(value)
return stringcase.pascalcase(_sanitize(value))


def spinal_case(value: str) -> str:
return stringcase.spinalcase(value)
def kebab_case(value: str) -> str:
return stringcase.spinalcase(group_title(_sanitize(value)))
4 changes: 2 additions & 2 deletions tests/test___init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,8 @@ def test__build_api(self, mocker):
from openapi_python_client import GeneratorData, Project

openapi = mocker.MagicMock(autospec=GeneratorData, title="My Test API")
tag_1 = mocker.MagicMock(autospec=str)
tag_2 = mocker.MagicMock(autospec=str)
tag_1 = "a_tag"
tag_2 = "another_tag"
collection_1 = mocker.MagicMock()
collection_2 = mocker.MagicMock()
openapi.endpoint_collections_by_tag = {tag_1: collection_1, tag_2: collection_2}
Expand Down
2 changes: 1 addition & 1 deletion tests/test_templates/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
def env() -> Environment:
from openapi_python_client import utils

TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "spinalcase": utils.spinal_case}
TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case}
env = Environment(loader=PackageLoader("openapi_python_client"), trim_blocks=True, lstrip_blocks=True)
env.filters.update(TEMPLATE_FILTERS)
return env
4 changes: 2 additions & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ def test_snake_case_from_camel():
assert utils.snake_case("httpResponseLowerCamel") == "http_response_lower_camel"


def test_spinal_case():
assert utils.spinal_case("keep_alive") == "keep-alive"
def test_kebab_case():
assert utils.kebab_case("keep_alive") == "keep-alive"

0 comments on commit 3e7dfae

Please sign in to comment.