Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Upcoming Open-TYNDP Release

* Change GitHub issue templates to comply with ISO security checks (https://github.com/open-energy-transition/open-tyndp/pull/714, https://github.com/open-energy-transition/open-tyndp/pull/730).

* Add validation of Zenodo API base URL and deposition ID in ``create_zenodo_deposition_cli`` to close URL-manipulation finding (https://github.com/open-energy-transition/open-tyndp/pull/780).


Upcoming PyPSA-Eur Release
================
Expand Down
23 changes: 23 additions & 0 deletions utils/create_zenodo_deposition_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@ def get_archive_folders(dataset):
return sorted([f.name for f in archive_path.iterdir() if f.is_dir()])


def validate_api_url() -> None:
Comment thread
tgilon marked this conversation as resolved.
Outdated
"""
Validate the configured Zenodo API base URL against the allowlist.

Ensures the scheme and authority match a known Zenodo API endpoint before it
is used to build request URLs, guarding against URL manipulation. Raises a
``ValueError`` if the URL is not recognised.
"""
if ZENODO_API_URL not in API_URLS.values():
raise ValueError(
f"Invalid Zenodo API URL: {ZENODO_API_URL!r}. "
f"Expected one of {sorted(API_URLS.values())}."
)


def create_zenodo_deposition(metadata: dict, files: list[Path]) -> requests.Response:
"""
Create a new Zenodo deposition with the specified metadata and files.
Expand Down Expand Up @@ -310,6 +325,13 @@ def publish_zenodo_deposition(deposition_id: int) -> requests.Response:
Response
The response from the Zenodo API after publishing the deposition.
"""
# Validate id to be positive integer (rejecting bool, int-like strings and traversals)
if (
isinstance(deposition_id, bool)
or not isinstance(deposition_id, int)
or deposition_id <= 0
):
raise ValueError(f"Invalid Zenodo deposition ID: {deposition_id!r}")
r = requests.post(
f"{ZENODO_API_URL}/deposit/depositions/{deposition_id}/actions/publish",
params={"access_token": ZENODO_API_KEY},
Expand Down Expand Up @@ -453,6 +475,7 @@ def main(
"""
global ZENODO_API_URL
ZENODO_API_URL = API_URLS["sandbox"] if sandbox else API_URLS["production"]
validate_api_url()

if sandbox:
typer.secho(
Expand Down
Loading