From 11d4f39cab2a7de333aeb4a8f0d1feedd58b4d30 Mon Sep 17 00:00:00 2001
From: Christian Boettcher <c.boettcher@fz-juelich.de>
Date: Tue, 22 Jun 2021 14:41:15 +0200
Subject: [PATCH] add verification of oid format

---
 apiserver/main.py                           |  8 +++++++-
 apiserver/storage/JsonFileStorageAdapter.py | 11 +++++++++++
 apiserver/storage/__init__.py               |  2 +-
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/apiserver/main.py b/apiserver/main.py
index fcfbeb6..f527e4b 100644
--- a/apiserver/main.py
+++ b/apiserver/main.py
@@ -15,7 +15,7 @@ from .config import ApiserverSettings
 from .security import (ACCESS_TOKEN_EXPIRES_MINUTES, JsonDBInterface, Token,
                        User, authenticate_user, create_access_token,
                        get_current_user)
-from .storage import JsonFileStorageAdapter, LocationData, LocationDataType
+from .storage import JsonFileStorageAdapter, LocationData, LocationDataType, verify_oid
 
 
 class ReservedPaths(str, Enum):
@@ -86,6 +86,8 @@ async def list_datasets(location_data_type: LocationDataType):
 @app.get("/{location_data_type}/{dataset_id}", response_model=LocationData)
 async def get_specific_dataset(location_data_type: LocationDataType, dataset_id: str):
     """returns all information about a specific dataset, identified by id"""
+    if not verify_oid(dataset_id):
+        raise HTTPException(status_code=400, detail="Invalid OID format!")
     return adapter.get_details(location_data_type, dataset_id)
 
 @app.post("/{location_data_type}") 
@@ -101,6 +103,8 @@ async def update_specific_dataset(location_data_type: LocationDataType,
                                   dataset_id: str, dataset: LocationData,
                                   user: User = Depends(my_user)):
     """update the information about a specific dataset, identified by id"""
+    if not verify_oid(dataset_id):
+        raise HTTPException(status_code=400, detail="Invalid OID format!")
     return adapter.update_details(location_data_type, dataset_id, dataset, user.username)
 
 
@@ -109,6 +113,8 @@ async def delete_specific_dataset(location_data_type: LocationDataType,
                                   dataset_id: str,
                                   user: str = Depends(my_user)):
     """delete a specific dataset"""
+    if not verify_oid(dataset_id):
+        raise HTTPException(status_code=400, detail="Invalid OID format!")
     # TODO: 404 is the right answer? 204 could also be the right one
     return adapter.delete(location_data_type, dataset_id, user.username)
 
diff --git a/apiserver/storage/JsonFileStorageAdapter.py b/apiserver/storage/JsonFileStorageAdapter.py
index 4510694..b2197a2 100644
--- a/apiserver/storage/JsonFileStorageAdapter.py
+++ b/apiserver/storage/JsonFileStorageAdapter.py
@@ -24,6 +24,17 @@ def get_unique_id(path: str) -> str:
         oid = str(uuid.uuid4())
     return oid
 
+
+def verify_oid(oid: str, version=4):
+    """ Ensure thatthe oid is formatted as a valid oid (i.e. UUID v4).
+    If it isn't, the corresponding request could theoretically be an attempted path traversal attack (or a regular typo).
+    """
+    try:
+        uuid_obj = uuid.UUID(oid, version=version)
+    except:
+        return False
+    return str(uuid_obj) == oid
+
 class JsonFileStorageAdapter(AbstractLocationDataStorageAdapter):
     """ This stores LocationData via the StoredData Object as json files
 
diff --git a/apiserver/storage/__init__.py b/apiserver/storage/__init__.py
index 3d63565..8c48a89 100644
--- a/apiserver/storage/__init__.py
+++ b/apiserver/storage/__init__.py
@@ -1,3 +1,3 @@
-from .JsonFileStorageAdapter import JsonFileStorageAdapter
+from .JsonFileStorageAdapter import JsonFileStorageAdapter, verify_oid
 
 from .LocationStorage import LocationDataType, LocationData, AbstractLocationDataStorageAdapter
\ No newline at end of file
-- 
GitLab