Skip to content

Conversation

@Aditya30ag
Copy link
Contributor

@Aditya30ag Aditya30ag commented Oct 27, 2025

Bugs Fixed in Devr.AI Project

This document lists the bugs that have been identified and fixed in the Devr.AI project.

✅ Fixed Bugs

1. Console.error Statements in Production Code

Severity: Medium
Location: Frontend
Files Affected:

  • frontend/src/App.tsx
  • frontend/src/lib/api.ts
  • frontend/src/components/integration/BotIntegration.tsx

Issue:
Console.error statements were left in production code, which can expose sensitive error information in browser console and impact performance.

Fix Applied:

  • Removed console.error from session check in App.tsx
  • Removed console.error from logout handler in App.tsx
  • Removed console.error from API client interceptor in api.ts
  • Removed console.error from healthCheck method in api.ts
  • Removed console.error from BotIntegration.tsx and added proper error handling

Impact: Improved security and performance by removing unnecessary console logging.


2. Missing exc_info in Backend Exception Logging

Severity: Medium
Location: Backend
Files Affected:

  • backend/app/database/weaviate/client.py
  • backend/app/core/orchestration/queue_manager.py

Issue:
Exception logging without exc_info=True makes debugging difficult as stack traces are not captured.

Fix Applied:

  • Added exc_info=True to all exception logging statements in Weaviate client
  • Added exc_info=True to RabbitMQ connection error logging

Impact: Better debugging capabilities with full stack traces in logs.


3. Improved Error Handling in BotIntegration

Severity: Low
Location: Frontend
Files Affected:

  • frontend/src/components/integration/BotIntegration.tsx

Issue:
Error in loadIntegrationStatus was only logged but didn't reset component state, potentially leaving UI in inconsistent state.

Fix Applied:

  • Added proper state reset (setIsConnected(false), setIntegration(null)) in catch block
  • Removed console.error and added descriptive comment

Impact: More robust error handling and consistent UI state.


Summary by CodeRabbit

  • Refactor

    • Improved error logging to include full tracebacks for better diagnostics.
    • Switched to per-request client lifecycles and tightened exception handling.
  • Bug Fixes / Behavior

    • Messages now persist and are serialized as JSON for more reliable delivery.
    • Authentication now deduplicates 401 handling, prevents redirect loops, and clears sessions reliably.
    • UI integration and logout flows now fail silently while correctly updating visible connection state.
  • New Feature

    • Login now respects an encoded return URL and redirects back after sign-in.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 27, 2025

📝 Walkthrough

Walkthrough

Updates refine error logging and client lifecycles: backend adds exception tracebacks, enforces queue connectivity, serializes/persity messages, and converts Weaviate client to per-context usage; frontend silences several console.error calls, tightens 401 handling, and adds returnUrl redirect support on login.

Changes

Cohort / File(s) Summary
Backend — queue & logging
backend/app/core/orchestration/queue_manager.py
Added connectivity guard (raise if no/closed channel), serialize priority as string, publish using persistent Message with application/json, and enable exc_info=True on failure logs.
Backend — weaviate client
backend/app/database/weaviate/client.py
Removed module-level cached client; get_client() returns a per-context weaviate.use_async_with_local() instance; narrowed exception handling to weaviate.exceptions.WeaviateBaseError; added exc_info=True logging; removed global singleton export.
Frontend — app & integration
frontend/src/App.tsx, frontend/src/components/integration/BotIntegration.tsx
Removed console.error calls in auto-login and logout error handlers; BotIntegration catch now silently resets isConnected and clears integration state.
Frontend — API client & login
frontend/src/lib/api.ts, frontend/src/components/pages/LoginPage.tsx
Deduplicated/async 401 handling with sign-out and redirect to /login?returnUrl=...; PUBLIC_PATHS bypass; healthCheck logs only in DEV; LoginPage reads returnUrl and navigates to decoded path when valid.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Producer
    participant QueueMgr
    participant Broker
    Producer->>QueueMgr: enqueue(message, priority)
    alt no channel / channel closed
        QueueMgr-->>Producer: raise ConnectivityError
    else channel available
        QueueMgr->>QueueMgr: serialize priority as string
        QueueMgr->>Broker: publish(Message persistent, content-type=application/json)
        Broker-->>QueueMgr: ack
    end
Loading
sequenceDiagram
    autonumber
    actor Worker
    participant QueueMgr
    participant Processor
    Worker->>QueueMgr: receive message (loop)
    QueueMgr->>Processor: process(message)
    alt processing error
        Processor-->>QueueMgr: error
        QueueMgr-->>QueueMgr: log error (exc_info=True)
        QueueMgr->>Worker: nack / handle failure
    else success
        Processor-->>QueueMgr: success
        QueueMgr->>Worker: ack
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • chandansgowda
  • smokeyScraper

Poem

🐇 I hop through logs both near and far,

Tracebacks glinting like a shard of star,
Messages marked persistent and true,
Frontend hushes, redirects pursue,
A tidy burrow of code — fresh and new.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is vague and overly broad, using generic terminology like 'bug fixes' without specifying which bugs or the primary focus area. Provide a more specific title that highlights the primary change, such as 'fix: add stack trace logging and improve error handling' or 'fix: remove console logging and fix 401 race condition'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/app/core/orchestration/queue_manager.py (1)

82-91: Blocker: Enum is not JSON‑serializable in queue_item.

json.dumps(queue_item) will fail because QueuePriority is an Enum.

Apply this diff:

-        queue_item = {
+        queue_item = {
             "id": message.get("id", f"msg_{datetime.now().timestamp()}"),
-            "priority": priority,
+            "priority": priority.value,
             "data": message
         }
backend/app/database/weaviate/client.py (1)

8-16: Client lifetime: cached global is closed each use.

get_client caches _client, but the context manager closes it in finally. Next use reuses a closed client. Either don’t cache or reset the cache after closing.

Option A (simplest): drop caching.

-_client = None
-
-
-def get_client():
-    """Get or create the global Weaviate client instance."""
-    global _client
-    if _client is None:
-        _client = weaviate.use_async_with_local()
-    return _client
+def get_client():
+    """Create a new async Weaviate client instance per context use."""
+    return weaviate.use_async_with_local()

Option B: keep caching but reset after close (see next comment for diff).

🧹 Nitpick comments (13)
frontend/src/lib/api.ts (1)

164-171: Empty catch block hinders debugging and observability.

The empty catch block makes it impossible to debug why health checks fail in production. While returning false on errors is appropriate, completely suppressing error details contradicts the PR's goal of "better debugging through full stack traces."

Consider logging errors in development or using structured logging:

     async healthCheck(): Promise<boolean> {
         try {
             const response = await this.client.get('/v1/health');
             return response.status === 200;
-        } catch {
+        } catch (error) {
+            // Log in development for debugging
+            if (import.meta.env.DEV) {
+                console.error('Health check failed:', error);
+            }
             return false;
         }
     }

Alternatively, integrate a proper observability service (e.g., Sentry) for production error tracking.

frontend/src/components/integration/BotIntegration.tsx (1)

49-53: Silent error handling removes user feedback and debugging visibility.

While resetting state (lines 51-52) correctly ensures UI consistency, the completely silent error handling provides no feedback to users about why integration status couldn't be loaded. Users cannot distinguish between "integration not connected" and "failed to check status due to network/API error." The component already uses toast (imported on line 3), but doesn't leverage it here.

Consider providing user feedback while maintaining state reset:

-    } catch {
+    } catch (error) {
         // Silently handle integration status loading errors
         setIsConnected(false);
         setIntegration(null);
+        // Optionally notify user of loading failure
+        if (import.meta.env.DEV) {
+            console.error('Failed to load integration status:', error);
+        }
+        // Consider: toast.error('Failed to load integration status');
     }

This improves observability while preserving the defensive state-reset behavior.

backend/app/core/orchestration/queue_manager.py (6)

44-45: Good: include traceback on connection failures.

exc_info=True here is appropriate and keeps control flow unchanged. Consider standardizing this across other error logs in this module.


73-91: Guard against publishing while disconnected.

enqueue assumes self.channel is ready; calling before start() or after stop() will raise.

Apply this diff:

     async def enqueue(self,
                       message: Dict[str, Any],
                       priority: QueuePriority = QueuePriority.MEDIUM,
                       delay: float = 0):
         """Add a message to the queue"""
 
         if delay > 0:
             await asyncio.sleep(delay)
 
+        if not self.channel or self.channel.is_closed:
+            raise RuntimeError("Queue channel is not connected. Call start() before enqueue().")

112-118: Add traceback to processing errors and avoid f-strings in logs.

Include exc_info and defer string formatting to logger.

Apply this diff:

-                        except Exception as e:
-                            logger.error(f"Error processing message: {e}")
+                        except Exception as e:
+                            logger.error("Error processing message: %s", e, exc_info=True)
                             await message.nack(requeue=False)

122-124: Add traceback for worker loop errors.

This helps diagnose queue.get/JSON errors.

Apply this diff:

-                except Exception as e:
-                    logger.error(f"Worker {worker_name} error: {e}")
+                except Exception as e:
+                    logger.error("Worker %s error: %s", worker_name, e, exc_info=True)

141-142: Consider DLQ for unknown message types.

Right now, unknown types are logged and then acked (since ack happens after _process_item). If you want post‑mortems, route unknowns to a dead‑letter exchange with a reason header instead of silently dropping.


88-91: Explicit delivery_mode required for message persistence in aio-pika 9.5.5.

aio-pika 9.5.5 defaults to DeliveryMode.NOT_PERSISTENT, meaning messages will be lost if the RabbitMQ broker crashes before workers consume them—regardless of queue durability. Apply the suggested diff to add explicit persistence:

         await self.channel.default_exchange.publish(
             aio_pika.Message(
+                body=json_message,
+                delivery_mode=aio_pika.DeliveryMode.PERSISTENT,
+                content_type="application/json",
             ),
             routing_key=self.queues[priority]
         )
backend/app/database/weaviate/client.py (5)

26-27: Good: add traceback; also switch to parameterized logging.

Use logger’s formatting instead of f-strings (addresses RUF010, avoids eager str()).

Apply this diff:

-        logger.error(f"Weaviate client error: {str(e)}", exc_info=True)
+        logger.error("Weaviate client error: %s", e, exc_info=True)

31-32: Likewise here: parameterized logging.

Keeps consistency and satisfies the linter.

Apply this diff:

-        except Exception as e:
-            logger.warning(f"Error closing Weaviate client: {str(e)}", exc_info=True)
+        except Exception as e:
+            logger.warning("Error closing Weaviate client: %s", e, exc_info=True)

28-32: If you keep caching: reset the global after closing.

Prevents reuse of a closed client.

Apply this diff:

     finally:
         try:
             await client.close()
         except Exception as e:
-            logger.warning(f"Error closing Weaviate client: {str(e)}", exc_info=True)
+            logger.warning("Error closing Weaviate client: %s", e, exc_info=True)
+        finally:
+            # Ensure future calls don't reuse a closed client
+            global _client
+            _client = None

3-9: Tighten typing for the cached client.

Add Optional type to make intent explicit.

Apply this diff:

-from typing import AsyncGenerator
+from typing import AsyncGenerator, Optional
@@
-_client = None
+_client: Optional[weaviate.WeaviateClient] = None

19-27: Narrow exception handler from broad Exception to specific Weaviate exceptions.

Replace except Exception with except weaviate.exceptions.WeaviateBaseError to catch only expected client errors (e.g., WeaviateConnectionError, UnexpectedStatusCodeException, WeaviateStartUpError, WeaviateGRPCUnavailableError). All Weaviate-specific exceptions inherit from weaviate.exceptions.WeaviateBaseError, which will handle connection, auth, and startup failures while allowing unexpected Python errors to propagate.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8eeacad and 249b6e9.

📒 Files selected for processing (5)
  • backend/app/core/orchestration/queue_manager.py (1 hunks)
  • backend/app/database/weaviate/client.py (1 hunks)
  • frontend/src/App.tsx (0 hunks)
  • frontend/src/components/integration/BotIntegration.tsx (1 hunks)
  • frontend/src/lib/api.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/App.tsx
🧰 Additional context used
🪛 Ruff (0.14.1)
backend/app/database/weaviate/client.py

26-26: Use explicit conversion flag

Replace with conversion flag

(RUF010)


31-31: Do not catch blind exception: Exception

(BLE001)


32-32: Use explicit conversion flag

Replace with conversion flag

(RUF010)

@smokeyScraper
Copy link
Contributor

smokeyScraper commented Oct 28, 2025

could you please review the coderabit suggested changes @Aditya30ag?

@Aditya30ag
Copy link
Contributor Author

Yaa sure

@Aditya30ag
Copy link
Contributor Author

@smokeyScraper Done

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
frontend/src/lib/api.ts (1)

174-177: Consider adding production error monitoring.

While silencing health check errors in production reduces console noise (aligned with the PR objectives), completely muting them may hide connectivity issues. Consider sending these errors to a monitoring service (e.g., Sentry, DataDog) for production observability.

Example implementation:

         } catch (error) {
             // Log in development for debugging
             if (import.meta.env.DEV) {
                 console.error('Health check failed:', error);
+            } else {
+                // Send to monitoring service in production
+                // Example: Sentry.captureException(error);
             }
             return false;
         }
backend/app/core/orchestration/queue_manager.py (2)

82-84: Good defensive check. Consider custom exception class.

The connectivity guard prevents publishing to a closed/missing channel, which is excellent defensive coding. The static analysis hint about the long error message is valid but minor—consider extracting this to a custom exception class if you prefer stricter adherence to style guidelines.

Optional refactor to address the static analysis hint:

Create a custom exception at the top of the file:

class QueueNotConnectedError(RuntimeError):
    """Raised when attempting to enqueue without an active channel connection."""
    def __init__(self):
        super().__init__("Queue channel is not connected. Call start() before enqueue().")

Then use it:

-        if not self.channel or self.channel.is_closed:
-            raise RuntimeError("Queue channel is not connected. Call start() before enqueue().")
+        if not self.channel or self.channel.is_closed:
+            raise QueueNotConnectedError()

151-152: Add exc_info=True for consistency.

For consistency with the error logging improvements at lines 44, 125, and 131, consider adding exc_info=True here as well to capture full stack traces when processing items fails.

Apply this diff:

-            logger.error(f"Error processing item {item.get('id', 'unknown')}: {str(e)}")
+            logger.error(f"Error processing item {item.get('id', 'unknown')}: {str(e)}", exc_info=True)
backend/app/database/weaviate/client.py (1)

26-27: Consider narrowing the exception type in cleanup block.

While catching broad Exception in the finally block ensures cleanup doesn't crash, you could narrow it to weaviate.exceptions.WeaviateBaseError for consistency with the main exception handler. The addition of exc_info=True is good and aligns with the PR's debugging improvements.

Apply this diff for more specific exception handling:

         try:
             await client.close()
-        except Exception as e:
+        except weaviate.exceptions.WeaviateBaseError as e:
             logger.warning("Error closing Weaviate client: %s", e, exc_info=True)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 249b6e9 and 73a7e87.

📒 Files selected for processing (4)
  • backend/app/core/orchestration/queue_manager.py (3 hunks)
  • backend/app/database/weaviate/client.py (2 hunks)
  • frontend/src/components/integration/BotIntegration.tsx (1 hunks)
  • frontend/src/lib/api.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/integration/BotIntegration.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/lib/api.ts (1)
frontend/src/lib/supabaseClient.ts (1)
  • supabase (12-12)
🪛 Ruff (0.14.2)
backend/app/database/weaviate/client.py

26-26: Do not catch blind exception: Exception

(BLE001)

backend/app/core/orchestration/queue_manager.py

84-84: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (7)
frontend/src/lib/api.ts (1)

86-94: Excellent fix for 401 handling!

This implementation properly addresses the previous review comment about incomplete 401 error handling. The solution correctly:

  • Signs out the user via Supabase
  • Prevents infinite redirect loops by checking the current pathname
  • Preserves the return URL for post-login navigation
  • Still rejects the error for caller handling

The async/await pattern for signOut() ensures the session is cleared before redirecting.

backend/app/core/orchestration/queue_manager.py (4)

44-44: LGTM! Improved exception logging.

Adding exc_info=True captures the full stack trace when RabbitMQ connection fails, which significantly aids debugging of connection issues.


88-88: LGTM! Explicit enum serialization.

Using priority.value makes the enum-to-string conversion explicit, ensuring consistent JSON serialization and improving code clarity.


93-97: Excellent improvement to message durability.

Configuring messages with PERSISTENT delivery mode ensures they survive RabbitMQ restarts, and setting the content_type header improves message semantics. This pairs well with the durable queue declaration on line 41.


125-125: LGTM! Consistent exception logging improvements.

Adding exc_info=True to both message processing errors (line 125) and worker errors (line 131) provides full stack traces for debugging, consistent with the logging improvement at line 44.

Also applies to: 131-131

backend/app/database/weaviate/client.py (2)

2-2: LGTM: Improved exception handling.

The addition of weaviate.exceptions import and the switch to catching specific WeaviateBaseError instead of generic Exception improves error handling specificity. The exc_info=True parameter successfully addresses the PR objective of capturing full stack traces for better debugging.

Also applies to: 20-21


9-11: Based on my verification of Weaviate's official documentation, the per-context client pattern in the code is correct and recommended. Weaviate's documentation explicitly shows using the async context manager pattern with async with weaviate.use_async_with_local() where the connection is automatically closed when the context is exited. The context manager is used to ensure that the client is connected to the server during data operations.

The original review comment raised concerns about performance overhead and resource exhaustion from creating new clients per context. However, this pattern aligns with Weaviate's documented best practices for async clients. Weaviate recommends considering the asynchronous client API to improve performance in asynchronous environments.

While connection pooling is mentioned as a best practice to reduce overhead by allowing multiple requests to share a single connection, this is an optional optimization rather than a requirement. The current per-context implementation is a valid, documented pattern that ensures proper resource lifecycle management.

@smokeyScraper
Copy link
Contributor

hey @Aditya30ag,
Thanks for the improvements! The error handling and logging changes are solid. However, the 401 handler implementation has a few issues that need to be addressed before it can be merged.

Issues to Address

  1. returnUrl feature is incomplete
    You're appending returnUrl to the login redirect, but LoginPage.tsx doesn't read or use this parameter. Users won't actually be redirected back to their original page after login.
    Please either:
    Add logic to LoginPage.tsx to read returnUrl from query params and redirect after successful login, OR
    Remove the returnUrl logic from this PR and we can tackle it as a separate feature

  2. Path check will break other public pages
    if (!window.location.pathname.includes('/login'))
    This misses our other public routes. If a 401 happens on /forgot-password, /reset-password, or /signup, users will get incorrectly yanked to the login page mid-flow.

  3. Race condition with multiple 401s
    On page load, multiple components often fire API requests simultaneously. If they all return 401, the current code will call signOut() and trigger redirects multiple times.

Comment on lines 84 to 94
this.client.interceptors.response.use(
(response) => response,
(error) => {
async (error) => {
if (error.response?.status === 401) {
// Handle unauthorized - could redirect to login
console.error('Unauthorized request');
// Clear session and redirect to login
// Avoid infinite redirect loop if already on login page
if (!window.location.pathname.includes('/login')) {
await supabase.auth.signOut();
const returnUrl = encodeURIComponent(window.location.pathname + window.location.search);
window.location.href = `/login?returnUrl=${returnUrl}`;
}
Copy link
Contributor

@smokeyScraper smokeyScraper Dec 27, 2025

Choose a reason for hiding this comment

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

returnUrl is added but never consumed by LoginPage.tsx

Copy link
Contributor Author

Choose a reason for hiding this comment

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

solve this issue also

console.error('Unauthorized request');
// Clear session and redirect to login
// Avoid infinite redirect loop if already on login page
if (!window.location.pathname.includes('/login')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Misses /forgot-password, /reset-password, /signup; users on these pages will be incorrectly redirected.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

const PUBLIC_PATHS = [
'/login',
'/signup',
'/forgot-password',
'/reset-password',
];
added this

@smokeyScraper
Copy link
Contributor

Could you please make these changes @Aditya30ag ? Rest, the PR seems ready to be merged.

@Aditya30ag
Copy link
Contributor Author

@smokeyScraper yeah sure i will do the changes

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
frontend/src/components/pages/LoginPage.tsx (1)

78-81: Unused exception variable.

The caught exception e is not logged or used. Consider removing it or prefixing with underscore to indicate intentional discard.

Proposed fix
-      } catch (e) {
+      } catch {
         // If decoding fails, fallback to home
         navigate('/', { replace: true });
       }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73a7e87 and c044541.

📒 Files selected for processing (2)
  • frontend/src/components/pages/LoginPage.tsx
  • frontend/src/lib/api.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: smokeyScraper
Repo: AOSSIE-Org/Devr.AI PR: 85
File: tests/test_supabase.py:1-3
Timestamp: 2025-06-28T14:45:55.244Z
Learning: In the Devr.AI project, smokeyScraper prefers to defer comprehensive test refactoring to separate PRs/efforts when doing major backend restructuring, rather than expanding the scope of the current refactoring PR to include test updates.
Learnt from: smokeyScraper
Repo: AOSSIE-Org/Devr.AI PR: 87
File: tests/test_supabase.py:1-3
Timestamp: 2025-06-28T23:15:13.374Z
Learning: In the Devr.AI project, smokeyScraper prefers to defer test updates and fixes (like missing imports after module reorganization) to separate PRs rather than expanding the scope of module update/chore PRs to include comprehensive test refactoring.
🧬 Code graph analysis (1)
frontend/src/lib/api.ts (1)
frontend/src/lib/supabaseClient.ts (1)
  • supabase (12-12)
🔇 Additional comments (3)
frontend/src/lib/api.ts (3)

11-17: LGTM!

The PUBLIC_PATHS array correctly includes all auth-related routes referenced in the codebase, and the startsWith check properly handles nested paths. This addresses the previously raised concern about missing public routes.


121-124: Ensure consistent open-redirect protection with LoginPage.

The returnUrl encoding here is correct. However, since pathname comes from window.location.pathname, it's trusted. The concern is on the consumption side (LoginPage.tsx) where the startsWith('/') check should be strengthened to prevent //evil.com style attacks — see the related comment on that file.


200-211: LGTM!

Limiting health check error logging to development mode aligns with the PR objective to reduce console noise in production.

Comment on lines +69 to +77
const rawReturn = searchParams.get('returnUrl');
try {
const decoded = rawReturn ? decodeURIComponent(rawReturn) : null;
// Protect against open redirects by only allowing internal paths
if (decoded && decoded.startsWith('/')) {
navigate(decoded, { replace: true });
} else {
navigate('/', { replace: true });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Open redirect vulnerability: startsWith('/') is insufficient.

The check decoded.startsWith('/') can be bypassed with protocol-relative URLs like //evil.com or backslash variants like /\evil.com, which browsers may interpret as external redirects.

Proposed fix
       const rawReturn = searchParams.get('returnUrl');
       try {
         const decoded = rawReturn ? decodeURIComponent(rawReturn) : null;
         // Protect against open redirects by only allowing internal paths
-        if (decoded && decoded.startsWith('/')) {
+        if (decoded && decoded.startsWith('/') && !decoded.startsWith('//') && !decoded.startsWith('/\\')) {
           navigate(decoded, { replace: true });
         } else {
           navigate('/', { replace: true });
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rawReturn = searchParams.get('returnUrl');
try {
const decoded = rawReturn ? decodeURIComponent(rawReturn) : null;
// Protect against open redirects by only allowing internal paths
if (decoded && decoded.startsWith('/')) {
navigate(decoded, { replace: true });
} else {
navigate('/', { replace: true });
}
const rawReturn = searchParams.get('returnUrl');
try {
const decoded = rawReturn ? decodeURIComponent(rawReturn) : null;
// Protect against open redirects by only allowing internal paths
if (decoded && decoded.startsWith('/') && !decoded.startsWith('//') && !decoded.startsWith('/\\')) {
navigate(decoded, { replace: true });
} else {
navigate('/', { replace: true });
}
🤖 Prompt for AI Agents
In frontend/src/components/pages/LoginPage.tsx around lines 69 to 77, the
current check decoded.startsWith('/') permits protocol-relative URLs (e.g.
//evil.com) and backslash variants; update validation to only allow internal
absolute-paths by ensuring the decoded path starts with a single forward slash
not followed by another slash or backslash (e.g. use a regex like ^\/(?![\/\\])
and also reject any input that contains backslashes or a colon), or
alternatively construct new URL(decoded, window.location.origin) and only accept
it if its origin equals window.location.origin and its pathname begins with '/';
then navigate to the validated pathname (or fall back to '/').

Comment on lines +7 to +9
// Prevents multiple simultaneous 401 handlers from racing and triggering
// sign-out/redirect more than once.
let isHandlingUnauthorized = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Race condition guard never resets — breaks subsequent 401 handling.

isHandlingUnauthorized is set to true on Line 110 but never reset to false. After the first 401 triggers a redirect, this flag stays true for the lifetime of the page. If the user logs back in (or if the page is cached/restored), any future 401 responses will be silently ignored, leaving users stuck with broken API calls.

Proposed fix

Since window.location.href navigates away and reloads the page (resetting module state), this works for the happy path. However, if navigation fails or is blocked, the flag stays set. Consider resetting in a finally block or after a timeout as a safety net:

                     if (!isHandlingUnauthorized) {
                         isHandlingUnauthorized = true;
                         try {
                             await supabase.auth.signOut();
                         } catch (e) {
                             // Log but continue to redirect; signing out isn't critical here
                             console.error(
                                 'Error signing out on 401 handler',
                                 e
                             );
                         }
 
                         const returnUrl = encodeURIComponent(
                             pathname + window.location.search
                         );
                         window.location.href = `/login?returnUrl=${returnUrl}`;
+                        // Reset after a delay in case navigation is blocked (e.g., beforeunload handler)
+                        setTimeout(() => {
+                            isHandlingUnauthorized = false;
+                        }, 2000);
                     }

Also applies to: 109-125

🤖 Prompt for AI Agents
In frontend/src/lib/api.ts around lines 7-9 (and related handling at 109-125),
the module-level guard isHandlingUnauthorized is set true when a 401 is handled
but never reset, causing all subsequent 401s to be ignored; change the handler
so isHandlingUnauthorized is always reset (e.g., clear it in a finally block
after the redirect attempt and/or set a short fallback timeout to reset the flag
if navigation fails) so that the guard prevents concurrent handling but does not
permanently block future 401 handling.

@Aditya30ag
Copy link
Contributor Author

Please have a look done the requested changes

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.

2 participants