Skip to content

🛡️ Sentinel: [MEDIUM] Fix Insufficient Logging of Database Exceptions#102

Merged
daggerstuff merged 1 commit intostagingfrom
security/missing-db-logging-api-6310620232349936904
Mar 30, 2026
Merged

🛡️ Sentinel: [MEDIUM] Fix Insufficient Logging of Database Exceptions#102
daggerstuff merged 1 commit intostagingfrom
security/missing-db-logging-api-6310620232349936904

Conversation

@daggerstuff
Copy link
Copy Markdown
Owner

@daggerstuff daggerstuff commented Mar 29, 2026

🚨 Severity: MEDIUM
💡 Vulnerability: Swallowed database exceptions hid potential underlying malicious activity (e.g., SQL injection probing) behind generic 500 responses without creating any auditable server logs.
🔧 Fix: Imported the logging module and initialized a standard logger. Caught sqlite3.Error as e and logged logger.error(f"Database error: {e}") before returning the sanitized HTTPException in api/dataset_api.py.
✅ Verification: Review api/dataset_api.py and invoke an invalid database call to verify logger.error appears in stdout while the 500 response remains clean.


PR created automatically by Jules for task 6310620232349936904 started by @daggerstuff

Summary by Sourcery

Add server-side logging for database exceptions in dataset APIs to improve security monitoring and auditability.

Bug Fixes:

  • Log sqlite3 database exceptions in dataset API endpoints before returning generic 500 errors to avoid silently swallowing critical error details.

Documentation:

  • Extend Sentinel security log with a new entry describing the database error logging mitigation for insufficient logging and monitoring.

Summary by cubic

Add server-side logging for database exceptions in the dataset API to improve monitoring without leaking details to clients. Errors are logged and clients still receive a generic 500.

  • Bug Fixes
    • Initialize logging and set logger = logging.getLogger(__name__) in api/dataset_api.py.
    • Catch sqlite3.Error as e and log logger.error(f"Database error: {e}") before raising HTTPException(500).
    • Documented the practice in .Jules/sentinel.md.

Written for commit 827b285. Summary will update on new commits.

Summary by CodeRabbit

  • Chores
    • Implemented comprehensive logging for database errors across core API endpoints to enhance system error tracking and monitoring capabilities.

Co-authored-by: daggerstuff <261005129+daggerstuff@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings March 29, 2026 13:18
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai Error Error Mar 29, 2026 1:18pm

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 29, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds structured server-side logging for sqlite3 database exceptions in dataset APIs while preserving existing sanitized HTTP 500 responses, and documents the security learning in the Sentinel log.

Sequence diagram for dataset API database error handling with logging

sequenceDiagram
    actor Client
    participant FastAPI as FastAPIApp
    participant DatasetAPI as dataset_api_endpoint
    participant SQLite as SQLiteDatabase
    participant Logger as Logger

    Client->>FastAPI: HTTP request (e.g. GET /datasets)
    FastAPI->>DatasetAPI: Call list_datasets / get_dataset_metadata / query_dataset
    DatasetAPI->>SQLite: Execute SQL query
    SQLite-->>DatasetAPI: sqlite3.Error e

    DatasetAPI->>Logger: error(Database error: e)
    DatasetAPI->>FastAPI: Raise HTTPException 500 Database error occurred
    FastAPI-->>Client: HTTP 500 response with generic error message
Loading

Flow diagram for logger usage and exception handling in dataset_api

flowchart TD
    A[Start dataset API handler] --> B[Open SQLite connection]
    B --> C[Execute SQL query]
    C --> D{sqlite3.Error raised?}
    D -- No --> E[Build normal response payload]
    E --> F[Return successful HTTP response]
    D -- Yes --> G["logger.error(Database error: e)"]
    G --> H[Raise HTTPException 500 Database error occurred]
    H --> I[Return generic 500 to client]
    F --> J[Close connection in finally]
    I --> J[Close connection in finally]
    J --> K[End]
Loading

File-Level Changes

Change Details Files
Introduce a module-level logger and log database exceptions before returning generic HTTP 500 errors.
  • Initialize a module-level logger using logging.getLogger(name).
  • Update all sqlite3.Error handlers in dataset endpoints to capture the exception as a variable.
  • Log the concrete database error message with logger.error prior to raising HTTPException with a generic 500 response.
  • Ensure existing connection cleanup in finally blocks remains unchanged.
api/dataset_api.py
Document the new security pattern in the Sentinel security log.
  • Append a new Sentinel entry describing the risk of swallowing database errors without logging.
  • Record the recommended practice of logging database exceptions server-side while returning sanitized messages to clients.
.Jules/sentinel.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Added server-side logging of SQLite database exception details in three API handlers (list_datasets, get_dataset_metadata, query_dataset) before returning generic 500 errors to clients. Updated sentinel documentation to track this logging enhancement for improved backend observability.

Changes

Cohort / File(s) Summary
Documentation & Tracking
.Jules/sentinel.md
Added sentinel entry documenting the implementation of server-side exception logging for database errors.
API Exception Logging
api/dataset_api.py
Added logging module import and module-level logger; implemented sqlite3.Error exception logging via logger.error() in three API handlers before raising generic 500 HTTPException.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A logger's delight, we hop with glee,
Errors now logged where none could see,
Database troubles caught and tucked away,
While clients sleep soundly, safe and okay! 🌙✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references the main change (logging database exceptions) with appropriate severity indicator, but includes an emoji which adds noise and the bracketed severity level makes it less concise.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/missing-db-logging-api-6310620232349936904

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
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Consider logging the full traceback for these database errors (e.g., using logger.exception(...) or logger.error(..., exc_info=True)) so investigations have stack context rather than just the error message string.
  • You now have three nearly identical sqlite3.Error handlers; consider extracting a small helper (e.g., handle_db_error(e)) or context manager to centralize the logging and HTTPException raising logic.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider logging the full traceback for these database errors (e.g., using `logger.exception(...)` or `logger.error(..., exc_info=True)`) so investigations have stack context rather than just the error message string.
- You now have three nearly identical `sqlite3.Error` handlers; consider extracting a small helper (e.g., `handle_db_error(e)`) or context manager to centralize the logging and HTTPException raising logic.

Fix all in Cursor


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves backend observability for SQLite failures in the Dataset Access API by logging sqlite3.Error exceptions server-side while preserving sanitized 500 responses to clients.

Changes:

  • Added module-level logger initialization in api/dataset_api.py.
  • Updated three API endpoints to catch sqlite3.Error as e and log the error before raising a generic HTTPException(500).
  • Documented the new logging requirement in .Jules/sentinel.md.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
api/dataset_api.py Adds logger + logs sqlite exceptions in dataset endpoints before returning sanitized 500s.
.Jules/sentinel.md Adds a new Sentinel entry describing server-side logging for DB errors.
Comments suppressed due to low confidence (1)

api/dataset_api.py:189

  • In list_datasets, conn is first assigned inside the try, but the finally block unconditionally references it. If get_db_connection() raises before conn is set, this will raise an UnboundLocalError and mask the original DB exception. Initialize conn = None before the try (as done in the other endpoints) so the finally check is safe.
    finally:
        if conn:
            conn.close()

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

)
except sqlite3.Error:
except sqlite3.Error as e:
logger.error(f"Database error: {e}")
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

Logging with logger.error(f"Database error: {e}") drops the stack trace, which makes incident investigation harder. Prefer logger.exception(...) or logger.error(..., exc_info=True) in the except sqlite3.Error block so the traceback is captured alongside the message.

Suggested change
logger.error(f"Database error: {e}")
logger.exception("Database error occurred while listing datasets")

Copilot uses AI. Check for mistakes.
)
except sqlite3.Error:
except sqlite3.Error as e:
logger.error(f"Database error: {e}")
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

This except sqlite3.Error block logs only the exception string; without exc_info/traceback it can be difficult to diagnose which query/line failed. Use logger.exception(...) (or logger.error(..., exc_info=True)) so the traceback is emitted in logs.

Suggested change
logger.error(f"Database error: {e}")
logger.exception("Database error while fetching dataset metadata")

Copilot uses AI. Check for mistakes.

except sqlite3.Error:
except sqlite3.Error as e:
logger.error(f"Database error: {e}")
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

Same as above: this logs only the exception message without the traceback. Switch to logger.exception(...) (or logger.error(..., exc_info=True)) to capture stack traces for auditing/debugging DB failures.

Suggested change
logger.error(f"Database error: {e}")
logger.exception("Database error")

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@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.

Caution

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

⚠️ Outside diff range comments (1)
api/dataset_api.py (1)

139-190: ⚠️ Potential issue | 🟡 Minor

conn may be unbound in finally block if exception occurs early.

Unlike get_dataset_metadata (line 199) and query_dataset (line 261), this function doesn't initialize conn = None before the try block. If get_db_connection() raises a non-sqlite3.Error exception, the finally block will raise UnboundLocalError when evaluating if conn:.

🔧 Suggested fix
 async def list_datasets(
     current_auth_entity: Any = Depends(get_current_active_user_or_api_key),
 ):
     """List all available datasets (tables in the database)."""
     datasets = []
+    conn = None
     try:
         conn = get_db_connection()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/dataset_api.py` around lines 139 - 190, The variable conn can be
uninitialized if get_db_connection() raises before conn is assigned, causing an
UnboundLocalError in the finally block; fix by initializing conn = None before
the try in the function that builds datasets (where conn, get_db_connection,
cursor, and finally are used), ensure the finally checks if conn is not None (or
uses a safer context) before calling conn.close(), and keep existing
sqlite3.Error handling intact so cleanup never references an unbound variable.
🧹 Nitpick comments (1)
api/dataset_api.py (1)

19-20: Consider including exc_info=True for full stack traces.

The PR goal is to audit potential SQL injection probing, but logging only the exception message (f"Database error: {e}") omits the stack trace. Per the pattern in api/embedding_agent/app.py (line 120), the project uses exc_info=True to capture full tracebacks for debugging.

🔧 Suggested improvement across all three logging sites
-        logger.error(f"Database error: {e}")
+        logger.error(f"Database error: {e}", exc_info=True)

Apply to lines 185, 243, and 324.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/dataset_api.py` around lines 19 - 20, Search dataset_api.py for the
logger.error calls that log exceptions using the message "Database error: {e}"
(three occurrences) and change each call to include exc_info=True so the full
stack trace is captured (e.g., logger.error(f"Database error: {e}",
exc_info=True)); preserve the existing message and behavior otherwise and follow
the pattern used in api/embedding_agent/app.py where exc_info=True is passed to
logger.error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@api/dataset_api.py`:
- Around line 139-190: The variable conn can be uninitialized if
get_db_connection() raises before conn is assigned, causing an UnboundLocalError
in the finally block; fix by initializing conn = None before the try in the
function that builds datasets (where conn, get_db_connection, cursor, and
finally are used), ensure the finally checks if conn is not None (or uses a
safer context) before calling conn.close(), and keep existing sqlite3.Error
handling intact so cleanup never references an unbound variable.

---

Nitpick comments:
In `@api/dataset_api.py`:
- Around line 19-20: Search dataset_api.py for the logger.error calls that log
exceptions using the message "Database error: {e}" (three occurrences) and
change each call to include exc_info=True so the full stack trace is captured
(e.g., logger.error(f"Database error: {e}", exc_info=True)); preserve the
existing message and behavior otherwise and follow the pattern used in
api/embedding_agent/app.py where exc_info=True is passed to logger.error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43cec153-7832-4009-87fc-dad313481485

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad89b7 and 827b285.

📒 Files selected for processing (2)
  • .Jules/sentinel.md
  • api/dataset_api.py

@daggerstuff daggerstuff merged commit 2373fd6 into staging Mar 30, 2026
9 of 10 checks passed
@daggerstuff daggerstuff deleted the security/missing-db-logging-api-6310620232349936904 branch March 30, 2026 13:33
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