Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions backend/hf_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ async def _make_request(client, image_bytes, labels):
raise ExternalAPIException("Hugging Face API", str(e)) from e

def _prepare_image_bytes(image: Union[Image.Image, bytes]) -> bytes:
if isinstance(image, bytes):
return image
img_byte_arr = io.BytesIO()
fmt = image.format if hasattr(image, 'format') and image.format else 'JPEG'
image.save(img_byte_arr, format=fmt)
return img_byte_arr.getvalue()

async def detect_vandalism_clip(image: Image.Image, client: httpx.AsyncClient = None):
"""
Detects vandalism/graffiti using Zero-Shot Image Classification with CLIP (Async).
Includes retry logic with exponential backoff for transient failures.
Expand Down
28 changes: 13 additions & 15 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import json
import os
import io
import sys

# Add the project root to sys.path so we can import 'backend' modules
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Expand Down Expand Up @@ -234,18 +235,22 @@

# Offload blocking file I/O to a thread
def save_file():
with open(image_path, "wb") as buffer:
with open(file_location, "wb") as buffer:
shutil.copyfileobj(image.file, buffer)

await asyncio.to_thread(save_file)

except Exception as e:
return JSONResponse(status_code=500, content={"message": str(e)})

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Failed uploads expose raw server exception text to callers, including possible filesystem paths. Log e server-side and return a stable generic error message instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/main.py, line 243:

<comment>Failed uploads expose raw server exception text to callers, including possible filesystem paths. Log `e` server-side and return a stable generic error message instead.</comment>

<file context>
@@ -234,18 +234,22 @@ async def create_issue(
         await asyncio.to_thread(save_file)
 
+    except Exception as e:
+        return JSONResponse(status_code=500, content={"message": str(e)})
+
     # Offload blocking DB operations to a thread
</file context>


# Offload blocking DB operations to a thread
def save_to_db():
new_issue = Issue(
description=description,
category=category,
image_path=image_path,
source="web"
image_path=file_location,
source=source,
user_email=user_email
)
db.add(new_issue)
db.commit()
Expand All @@ -254,19 +259,12 @@

new_issue = await asyncio.to_thread(save_to_db)

# Generate Action Plan (AI)
# Generate Action Plan (AI)
try:
action_plan = await generate_action_plan(description, category, file_location)

db_issue = Issue(
description=description,
category=category,
image_path=file_location,
source=source,
user_email=user_email
)
db.add(db_issue)
db.commit()
db.refresh(db_issue)
except Exception as e:
logger.error(f"Error generating action plan: {e}")
action_plan = "Action plan generation failed"

return {
"id": new_issue.id,
Expand Down
Loading