-
Notifications
You must be signed in to change notification settings - Fork 33
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
base: main
Are you sure you want to change the base?
Conversation
MLCommons CLA bot: |
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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 1 month 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
.
-
Copy modified lines R19-R26
@@ -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") | ||
|
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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 1 month 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
.
- Normalize the
full_path
usingos.path.normpath
. - Check if the normalized
full_path
starts withBASE_DIR
to ensure it is within the base directory. - Update the existing checks to use the normalized path.
-
Copy modified lines R19-R25
@@ -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") | ||
|
|
||
# 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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 1 month 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.
-
Copy modified lines R19-R26
@@ -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") | ||
|
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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 1 month 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.
- Normalize the
full_path
usingos.path.normpath
. - Check if the normalized
full_path
starts withBASE_DIR
. - Ensure that
item_path
is also validated properly.
-
Copy modified lines R19-R33
@@ -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)}) | ||
|
# 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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 2 hours 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 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.
-
Copy modified lines R1-R3 -
Copy modified lines R13-R30 -
Copy modified lines R35-R58
@@ -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 | ||
|
||
@@ -11,14 +12,20 @@ | ||
@router.get("/login", response_class=HTMLResponse) | ||
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} | ||
) | ||
def login_form( | ||
request: Request, 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: | ||
valid_redirect_url = redirect_url | ||
else: | ||
valid_redirect_url = '/' | ||
# Check if user is already authenticated | ||
if token == security_token: | ||
# User is already authenticated, redirect to original URL | ||
return RedirectResponse(url=valid_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": valid_redirect_url} | ||
) | ||
|
||
@@ -27,19 +34,25 @@ | ||
@router.post("/login") | ||
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", | ||
}, | ||
) | ||
def login( | ||
request: Request, | ||
token: str = Form(...), | ||
redirect_url: str = Form("/"), | ||
): | ||
# Validate redirect_url | ||
redirect_url = redirect_url.replace('\\', '/') | ||
if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme: | ||
valid_redirect_url = redirect_url | ||
else: | ||
valid_redirect_url = '/' | ||
if token == security_token: | ||
response = RedirectResponse(url=valid_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": valid_redirect_url, | ||
"error": "Invalid token", | ||
}, | ||
) |
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
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix AI about 2 hours 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 browser quirks.
- Add the necessary import for
urlparse
. - Modify the
login
function to validate theredirect_url
parameter before using it in the redirect response.
-
Copy modified lines R1-R5 -
Copy modified lines R33-R40
@@ -1,6 +1,6 @@ | ||
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 | ||
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 | ||
|
||
@@ -32,6 +32,10 @@ | ||
): | ||
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 | ||
if token == security_token: | ||
redirect_url = redirect_url.replace('\\', '/') | ||
if not urlparse(redirect_url).netloc and not urlparse(redirect_url).scheme: | ||
response = RedirectResponse(url=redirect_url, status_code=status.HTTP_302_FOUND) | ||
else: | ||
response = RedirectResponse(url='/', status_code=status.HTTP_302_FOUND) | ||
response.set_cookie(key=AUTH_COOKIE_NAME, value=token) | ||
return response | ||
else: |
): | ||
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
): | ||
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
Comments/discussions to check:
To be merged before merging to main: #618, which depends on #615