Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web UI #634

Open
wants to merge 86 commits into
base: main
Choose a base branch
from
Open

Web UI #634

wants to merge 86 commits into from

Conversation

hasan7n
Copy link
Contributor

@hasan7n hasan7n commented Dec 21, 2024

Comments/discussions to check:

To be merged before merging to main: #618, which depends on #615

@hasan7n hasan7n requested a review from a team as a code owner December 21, 2024 21:09
@hasan7n hasan7n requested a deployment to testing-external-code December 21, 2024 21:09 — with GitHub Actions Waiting
Copy link
Contributor

MLCommons CLA bot:
Thank you very much for your submission, we really appreciate it. Before we can accept your contribution, we ask that you sign the MLCommons CLA (Apache 2). Please use this [Google form] (https://forms.gle/Ew1KkBVpyeJDuRw67) to initiate authorization. If you are from an MLCommons member organization, we will request that you be added to the CLA. If you are not from a member organization, we will email you a CLA to sign. For any questions, please contact [email protected].
3 out of 4 committers have signed the MLCommons CLA.
@aristizabal95
@mhmdk0
@hasan7n
@VukW
You can retrigger this bot by commenting recheck in this Pull Request

full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to ensure that the full_path is normalized before performing any checks. This will remove any ".." segments and ensure that the path is correctly contained within the BASE_DIR. We will use os.path.normpath to normalize the path and then check if the normalized path starts with the BASE_DIR.

Suggested changeset 1
cli/medperf/web_ui/api/routes.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/api/routes.py b/cli/medperf/web_ui/api/routes.py
--- a/cli/medperf/web_ui/api/routes.py
+++ b/cli/medperf/web_ui/api/routes.py
@@ -18,11 +18,10 @@
 ):
-    full_path = os.path.join(BASE_DIR, path)
-    os.path.join(BASE_DIR, path)
-
-    if not os.path.exists(full_path) or not os.path.isdir(full_path):
-        raise HTTPException(status_code=404, detail="Directory not found")
-
-    # Ensure path is within the base directory
-    if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
-        raise HTTPException(status_code=403, detail="Access denied")
+    full_path = os.path.normpath(os.path.join(BASE_DIR, path))
+
+    # Ensure path is within the base directory
+    if not full_path.startswith(BASE_DIR):
+        raise HTTPException(status_code=403, detail="Access denied")
+
+    if not os.path.exists(full_path) or not os.path.isdir(full_path):
+        raise HTTPException(status_code=404, detail="Directory not found")
 
EOF
@@ -18,11 +18,10 @@
):
full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

# Ensure path is within the base directory
if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")
full_path = os.path.normpath(os.path.join(BASE_DIR, path))

# Ensure path is within the base directory
if not full_path.startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to ensure that the full_path is normalized before checking if it is within the BASE_DIR. This can be done using os.path.normpath to remove any ".." segments that could allow directory traversal. After normalizing the path, we should check if it starts with the BASE_DIR.

  1. Normalize the full_path using os.path.normpath.
  2. Check if the normalized full_path starts with BASE_DIR to ensure it is within the base directory.
  3. Update the existing checks to use the normalized path.
Suggested changeset 1
cli/medperf/web_ui/api/routes.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/api/routes.py b/cli/medperf/web_ui/api/routes.py
--- a/cli/medperf/web_ui/api/routes.py
+++ b/cli/medperf/web_ui/api/routes.py
@@ -18,11 +18,9 @@
 ):
-    full_path = os.path.join(BASE_DIR, path)
-    os.path.join(BASE_DIR, path)
-
-    if not os.path.exists(full_path) or not os.path.isdir(full_path):
-        raise HTTPException(status_code=404, detail="Directory not found")
-
-    # Ensure path is within the base directory
-    if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
-        raise HTTPException(status_code=403, detail="Access denied")
+    full_path = os.path.normpath(os.path.join(BASE_DIR, path))
+
+    if not full_path.startswith(BASE_DIR):
+        raise HTTPException(status_code=403, detail="Access denied")
+
+    if not os.path.exists(full_path) or not os.path.isdir(full_path):
+        raise HTTPException(status_code=404, detail="Directory not found")
 
EOF
@@ -18,11 +18,9 @@
):
full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

# Ensure path is within the base directory
if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")
full_path = os.path.normpath(os.path.join(BASE_DIR, path))

if not full_path.startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

# List directories inside the path
folders = []
for item in os.listdir(full_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to ensure that the full_path is properly validated and sanitized before it is used in any file system operations. The best way to do this is to normalize the path using os.path.normpath and then check that the normalized path starts with the BASE_DIR. This will prevent any path traversal attacks and ensure that the path is within the intended directory.

Suggested changeset 1
cli/medperf/web_ui/api/routes.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/api/routes.py b/cli/medperf/web_ui/api/routes.py
--- a/cli/medperf/web_ui/api/routes.py
+++ b/cli/medperf/web_ui/api/routes.py
@@ -18,11 +18,10 @@
 ):
-    full_path = os.path.join(BASE_DIR, path)
-    os.path.join(BASE_DIR, path)
-
-    if not os.path.exists(full_path) or not os.path.isdir(full_path):
-        raise HTTPException(status_code=404, detail="Directory not found")
-
-    # Ensure path is within the base directory
-    if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
-        raise HTTPException(status_code=403, detail="Access denied")
+    full_path = os.path.normpath(os.path.join(BASE_DIR, path))
+
+    # Ensure path is within the base directory
+    if not full_path.startswith(BASE_DIR):
+        raise HTTPException(status_code=403, detail="Access denied")
+
+    if not os.path.exists(full_path) or not os.path.isdir(full_path):
+        raise HTTPException(status_code=404, detail="Directory not found")
 
EOF
@@ -18,11 +18,10 @@
):
full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

# Ensure path is within the base directory
if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")
full_path = os.path.normpath(os.path.join(BASE_DIR, path))

# Ensure path is within the base directory
if not full_path.startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
folders = []
for item in os.listdir(full_path):
item_path = os.path.join(full_path, item)
if os.path.isdir(item_path):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to ensure that the full_path is normalized before checking if it is within the BASE_DIR. This can be done using os.path.normpath to remove any ".." segments and ensure the path is safe. Additionally, we should ensure that the item_path is also validated properly.

  1. Normalize the full_path using os.path.normpath.
  2. Check if the normalized full_path starts with BASE_DIR.
  3. Ensure that item_path is also validated properly.
Suggested changeset 1
cli/medperf/web_ui/api/routes.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/api/routes.py b/cli/medperf/web_ui/api/routes.py
--- a/cli/medperf/web_ui/api/routes.py
+++ b/cli/medperf/web_ui/api/routes.py
@@ -18,18 +18,17 @@
 ):
-    full_path = os.path.join(BASE_DIR, path)
-    os.path.join(BASE_DIR, path)
-
-    if not os.path.exists(full_path) or not os.path.isdir(full_path):
-        raise HTTPException(status_code=404, detail="Directory not found")
-
-    # Ensure path is within the base directory
-    if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
-        raise HTTPException(status_code=403, detail="Access denied")
-
-    # List directories inside the path
-    folders = []
-    for item in os.listdir(full_path):
-        item_path = os.path.join(full_path, item)
-        if os.path.isdir(item_path):
-            folders.append({"name": item, "path": os.path.relpath(item_path, BASE_DIR)})
+    full_path = os.path.normpath(os.path.join(BASE_DIR, path))
+
+    if not os.path.exists(full_path) or not os.path.isdir(full_path):
+        raise HTTPException(status_code=404, detail="Directory not found")
+
+    # Ensure path is within the base directory
+    if not full_path.startswith(BASE_DIR):
+        raise HTTPException(status_code=403, detail="Access denied")
+
+    # List directories inside the path
+    folders = []
+    for item in os.listdir(full_path):
+        item_path = os.path.normpath(os.path.join(full_path, item))
+        if item_path.startswith(full_path) and os.path.isdir(item_path):
+            folders.append({"name": item, "path": os.path.relpath(item_path, BASE_DIR)})
 
EOF
@@ -18,18 +18,17 @@
):
full_path = os.path.join(BASE_DIR, path)
os.path.join(BASE_DIR, path)

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

# Ensure path is within the base directory
if not os.path.commonpath([BASE_DIR, full_path]).startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")

# List directories inside the path
folders = []
for item in os.listdir(full_path):
item_path = os.path.join(full_path, item)
if os.path.isdir(item_path):
folders.append({"name": item, "path": os.path.relpath(item_path, BASE_DIR)})
full_path = os.path.normpath(os.path.join(BASE_DIR, path))

if not os.path.exists(full_path) or not os.path.isdir(full_path):
raise HTTPException(status_code=404, detail="Directory not found")

# Ensure path is within the base directory
if not full_path.startswith(BASE_DIR):
raise HTTPException(status_code=403, detail="Access denied")

# List directories inside the path
folders = []
for item in os.listdir(full_path):
item_path = os.path.normpath(os.path.join(full_path, item))
if item_path.startswith(full_path) and os.path.isdir(item_path):
folders.append({"name": item, "path": os.path.relpath(item_path, BASE_DIR)})

Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
# Check if user is already authenticated
if token == security_token:
# User is already authenticated, redirect to original URL
return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to validate the redirect_url parameter to ensure it does not contain an explicit host name, which could lead to open redirect vulnerabilities. We can use the urlparse function from the Python standard library to parse the URL and check that the netloc attribute is empty. Additionally, we should replace backslashes with forward slashes to handle cases where browsers accept backslashes as equivalent to forward slashes.

We will make changes to the login_form and login functions to include this validation.

Suggested changeset 1
cli/medperf/web_ui/login.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/login.py b/cli/medperf/web_ui/login.py
--- a/cli/medperf/web_ui/login.py
+++ b/cli/medperf/web_ui/login.py
@@ -1,3 +1,4 @@
-from fastapi import Request, Form, APIRouter, status, Security
-from fastapi.responses import HTMLResponse, RedirectResponse
+from fastapi import Request, Form, APIRouter, status, Security
+from fastapi.responses import HTMLResponse, RedirectResponse
+from urllib.parse import urlparse
 
@@ -13,9 +14,12 @@
     request: Request,
-    redirect_url: str = "/",
-    token: str = Security(api_key_cookie)
-):
-    # Check if user is already authenticated
-    if token == security_token:
-        # User is already authenticated, redirect to original URL
-        return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
+    redirect_url: str = "/",
+    token: str = Security(api_key_cookie)
+):
+    # Validate redirect_url
+    redirect_url = redirect_url.replace('\\', '/')
+    if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme:
+        # Check if user is already authenticated
+        if token == security_token:
+            # User is already authenticated, redirect to original URL
+            return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
     else:
@@ -32,8 +36,11 @@
         token: str = Form(...),
-        redirect_url: str = Form("/"),
-):
-    if token == security_token:
-        response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
-        response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
-        return response
+        redirect_url: str = Form("/"),
+):
+    # Validate redirect_url
+    redirect_url = redirect_url.replace('\\', '/')
+    if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme:
+        if token == security_token:
+            response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
+            response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
+            return response
     else:
EOF
@@ -1,3 +1,4 @@
from fastapi import Request, Form, APIRouter, status, Security
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi import Request, Form, APIRouter, status, Security
from fastapi.responses import HTMLResponse, RedirectResponse
from urllib.parse import urlparse

@@ -13,9 +14,12 @@
request: Request,
redirect_url: str = "/",
token: str = Security(api_key_cookie)
):
# Check if user is already authenticated
if token == security_token:
# User is already authenticated, redirect to original URL
return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
redirect_url: str = "/",
token: str = Security(api_key_cookie)
):
# Validate redirect_url
redirect_url = redirect_url.replace('\\', '/')
if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme:
# Check if user is already authenticated
if token == security_token:
# User is already authenticated, redirect to original URL
return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
else:
@@ -32,8 +36,11 @@
token: str = Form(...),
redirect_url: str = Form("/"),
):
if token == security_token:
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
return response
redirect_url: str = Form("/"),
):
# Validate redirect_url
redirect_url = redirect_url.replace('\\', '/')
if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme:
if token == security_token:
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
return response
else:
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
redirect_url: str = Form("/"),
):
if token == security_token:
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix AI 22 days ago

To fix the problem, we need to validate the redirect_url parameter to ensure it does not contain an explicit host name, which could lead to an open redirect vulnerability. We can use the urlparse function from the Python standard library to parse the URL and check that the netloc attribute is empty. Additionally, we should replace backslashes with forward slashes to handle browser quirks.

  1. Import the urlparse function from the urllib.parse module.
  2. Replace backslashes in the redirect_url with forward slashes.
  3. Check that the netloc attribute of the parsed URL is empty.
  4. If the redirect_url is not valid, redirect to a default safe URL (e.g., "/").
Suggested changeset 1
cli/medperf/web_ui/login.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/cli/medperf/web_ui/login.py b/cli/medperf/web_ui/login.py
--- a/cli/medperf/web_ui/login.py
+++ b/cli/medperf/web_ui/login.py
@@ -1,49 +1,51 @@
-from fastapi import Request, Form, APIRouter, status, Security
-from fastapi.responses import HTMLResponse, RedirectResponse
-
-from medperf.web_ui.auth import security_token, AUTH_COOKIE_NAME
-from medperf.web_ui.common import templates, api_key_cookie
-
-router = APIRouter()
-
-
-# Login page GET endpoint
[email protected]("/login", response_class=HTMLResponse)
-async def login_form(
-    request: Request,
-    redirect_url: str = "/",
-    token: str = Security(api_key_cookie)
-):
-    # Check if user is already authenticated
-    if token == security_token:
-        # User is already authenticated, redirect to original URL
-        return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
-    else:
-        # User is not authenticated, show login form
-        return templates.TemplateResponse(
-            "login.html", {"request": request, "redirect_url": redirect_url}
-        )
-
-
-# Login page POST endpoint
[email protected]("/login")
-async def login(
-        request: Request,
-        token: str = Form(...),
-        redirect_url: str = Form("/"),
-):
-    if token == security_token:
-        response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
-        response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
-        return response
-    else:
-        return templates.TemplateResponse(
-            "login.html",
-            {
-                "request": request,
-                "redirect_url": redirect_url,
-                "error": "Invalid token",
-            },
-        )
-
-
+from fastapi import Request, Form, APIRouter, status, Security
+from fastapi.responses import HTMLResponse, RedirectResponse
+from urllib.parse import urlparse
+from medperf.web_ui.auth import security_token, AUTH_COOKIE_NAME
+from medperf.web_ui.common import templates, api_key_cookie
+
+router = APIRouter()
+
+
+# Login page GET endpoint
[email protected]("/login", response_class=HTMLResponse)
+async def login_form(
+    request: Request,
+    redirect_url: str = "/",
+    token: str = Security(api_key_cookie)
+):
+    # Check if user is already authenticated
+    if token == security_token:
+        # User is already authenticated, redirect to original URL
+        return RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
+    else:
+        # User is not authenticated, show login form
+        return templates.TemplateResponse(
+            "login.html", {"request": request, "redirect_url": redirect_url}
+        )
+
+
+# Login page POST endpoint
[email protected]("/login")
+async def login(
+        request: Request,
+        token: str = Form(...),
+        redirect_url: str = Form("/"),
+):
+    redirect_url = redirect_url.replace('\\', '/')
+    if not urlparse(redirect_url).netloc:
+        if token == security_token:
+            response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
+            response.set_cookie(key=AUTH_COOKIE_NAME, value=token)
+            return response
+        else:
+            return templates.TemplateResponse(
+                "login.html",
+                {
+                    "request": request,
+                    "redirect_url": redirect_url,
+                    "error": "Invalid token",
+                },
+            )
+    else:
+        return RedirectResponse(url="/", status_code=status.HTTP_302_FOUND)
EOF
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
):
if token == security_token:
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
response.set_cookie(key=AUTH_COOKIE_NAME, value=token)

Check warning

Code scanning / CodeQL

Failure to use secure cookies Medium

Cookie is added without the Secure and HttpOnly attributes properly set.
):
if token == security_token:
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND)
response.set_cookie(key=AUTH_COOKIE_NAME, value=token)

Check warning

Code scanning / CodeQL

Construction of a cookie using user-supplied input Medium

Cookie is constructed from a
user-supplied input
.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants