CVE-2020-15141 (Python, published 2020-08-14T17:15Z)
Description: value: In openapi-python-client before version 0.5.3, there is a path traversal vulnerability. If a user generated a client using a maliciously crafted OpenAPI document, it is possible for generated files to be placed in arbitrary locations on disk.
Severity: MEDIUM (CVSS v3 base score: 4.1)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Repository: https://github.com/triaxtec/openapi-python-client
Files changed:
end_to_end_tests/regen_golden_master.py
openapi_python_client/__init__.py
openapi_python_client/templates/endpoint_macros.pyi
openapi_python_client/utils.py
tests/test___init__.py
tests/test_templates/conftest.py
tests/test_utils.py
Commit message: Merge pull request from GHSA-7wgr-7666-7pwj

* 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>

Vulnerable code:
    from .fastapi_app import generate_openapi_json

    generate_openapi_json()
    TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "spinalcase": utils.spinal_case}
        self.project_name: str = self.project_name_override or f"{openapi.title.replace(' ', '-').lower()}-client"
headers["{{ parameter.python_name | spinalcase}}"] = {{ parameter.python_name }}
    headers["{{ parameter.python_name | spinalcase}}"] = {{ parameter.python_name }}
def snake_case(value: str) -> str:
    return stringcase.snakecase(value)
    return stringcase.pascalcase(value)
def spinal_case(value: str) -> str:
    return stringcase.spinalcase(value)
        tag_1 = mocker.MagicMock(autospec=str)
        tag_2 = mocker.MagicMock(autospec=str)
    TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "spinalcase": utils.spinal_case}
def test_spinal_case():
    assert utils.spinal_case("keep_alive") == "keep-alive"

Fixed code:
    TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case}
        self.project_name: str = self.project_name_override or f"{utils.kebab_case(openapi.title).lower()}-client"
            tag = utils.snake_case(tag)
headers["{{ parameter.python_name | kebabcase}}"] = {{ parameter.python_name }}
    headers["{{ parameter.python_name | kebabcase}}"] = {{ parameter.python_name }}
def _sanitize(value: str) -> str:
    return re.sub(r"[^\w _-]+", "", value)


def group_title(value: str) -> str:
    return value


def snake_case(value: str) -> str:
    return stringcase.snakecase(group_title(_sanitize(value)))
    return stringcase.pascalcase(_sanitize(value))
def kebab_case(value: str) -> str:
    return stringcase.spinalcase(group_title(_sanitize(value)))
        tag_1 = "a_tag"
        tag_2 = "another_tag"
    TEMPLATE_FILTERS = {"snakecase": utils.snake_case, "kebabcase": utils.kebab_case}
def test_kebab_case():
    assert utils.kebab_case("keep_alive") == "keep-alive"