Skip to content

🛡️ Sentinel: [HIGH] Fix TOCTOU and device file size limit bypass - #65

Open
ivangegovdve-sudo wants to merge 1 commit into
mainfrom
sentinel/fix-toctou-device-file-bypass-5267218339915732856
Open

🛡️ Sentinel: [HIGH] Fix TOCTOU and device file size limit bypass#65
ivangegovdve-sudo wants to merge 1 commit into
mainfrom
sentinel/fix-toctou-device-file-bypass-5267218339915732856

Conversation

@ivangegovdve-sudo

Copy link
Copy Markdown
Owner

🚨 Severity: HIGH

💡 Vulnerability: File size checks using path.stat().st_size could be bypassed if the path pointed to a device file (like /dev/zero, which reports a size of 0). Additionally, there was a Time-of-Check to Time-of-Use (TOCTOU) vulnerability where a file could be modified and grow between the stat() size check and the read_text() call. Both vectors could lead to unbounded memory consumption DoS or application hangs.

🎯 Impact: An attacker or a severe system misconfiguration could provide an excessively large file or a device file, causing the application to consume all available memory and crash when attempting to load JSON data.

🔧 Fix:

  1. Added is_file() checks to ensure the target is a regular file, rejecting device files.
  2. Switched from read_text() to opening the file and using a bounded read (f.read(limit + 1)) to load the data safely and without TOCTOU risk.
  3. Updated JsonFilePracticeRepository, JsonFileProgressRepository, JsonFileProgressSnapshotStore, and CheckpointStore.
  4. Recorded the critical security learning in .jules/sentinel.md.

✅ Verification: Run uv run pytest to ensure all functionality remains correct and exceptions are correctly thrown when expected. Checked that uv run ruff check . passes without errors.


PR created automatically by Jules for task 5267218339915732856 started by @ivangegovdve-sudo

Added `is_file()` checks to prevent size limit bypass using device files
like `/dev/zero`. Also switched from `read_text()` to bounded `f.read()`
to prevent TOCTOU vulnerabilities where a file grows after its size was
checked. Updates all JSON file-backed adapters and records the critical
learning in Sentinel's journal.

Co-authored-by: ivangegovdve-sudo <225339531+ivangegovdve-sudo@users.noreply.github.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a high-severity security vulnerability related to unbounded memory consumption. It prevents potential denial-of-service (DoS) attacks by ensuring that only regular files are processed and by implementing bounded file reading to avoid TOCTOU issues. The changes enhance the application's resilience against malicious or misconfigured file inputs.

Highlights

  • Security Fix: Addresses a TOCTOU vulnerability and prevents bypassing file size limits by using device files, mitigating potential DoS attacks.
  • Regular File Validation: Introduces is_file() checks to ensure that only regular files are processed, rejecting device files.
  • Bounded File Reading: Replaces read_text() with a bounded f.read(limit + 1) to safely load file data and prevent TOCTOU issues.
  • Repository Updates: Updates JsonFilePracticeRepository, JsonFileProgressRepository, JsonFileProgressSnapshotStore, and CheckpointStore to incorporate the security fixes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses a critical TOCTOU vulnerability and a device file bypass issue by implementing bounded file reads and checking for regular files. This is a significant security enhancement. My review includes a high-severity suggestion to handle potential crashes from empty JSON files in the checkpoint_store. Additionally, I've made several medium-severity recommendations to extract the hardcoded file size limit into a shared constant, which will improve code maintainability and consistency across the different file-based adapters.

Comment on lines +244 to 245
parsed = json.loads(content)
return parsed if isinstance(parsed, dict) else {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

An empty checkpoint file will cause json.loads(content) to raise a json.JSONDecodeError because content will be an empty string. This exception is not handled, which could crash the application. It's best to handle this case gracefully by returning an empty dictionary, which is consistent with how a non-existent file is handled.

Suggested change
parsed = json.loads(content)
return parsed if isinstance(parsed, dict) else {}
try:
parsed = json.loads(content)
except json.JSONDecodeError:
return {}
return parsed if isinstance(parsed, dict) else {}

Comment on lines +240 to +241
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The file size limit 10 * 1024 * 1024 is hardcoded. This value is duplicated across multiple files (json_file_practice_repository.py, json_file_progress_repository.py, json_file_progress_snapshot_store.py), making it difficult to maintain. Consider defining it as a constant in a shared module (e.g., a new src/python_learning_orchestrated/adapters/constants.py) and importing it where needed.

For example, in a new constants.py file:

MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB

Then you can import and use this constant.

Suggested change
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:
content = f.read(MAX_FILE_SIZE + 1)
if len(content) > MAX_FILE_SIZE:

Comment on lines +115 to +116
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The file size limit 10 * 1024 * 1024 is hardcoded here. To improve maintainability, this should be replaced with a shared constant. Please see my other comment about creating a MAX_FILE_SIZE constant.

Suggested change
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:
content = f.read(MAX_FILE_SIZE + 1)
if len(content) > MAX_FILE_SIZE:

Comment on lines +52 to +53
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The file size limit 10 * 1024 * 1024 is hardcoded here. To improve maintainability, this should be replaced with a shared constant. Please see my other comment about creating a MAX_FILE_SIZE constant.

Suggested change
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:
content = f.read(MAX_FILE_SIZE + 1)
if len(content) > MAX_FILE_SIZE:

Comment on lines +46 to +47
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The file size limit 10 * 1024 * 1024 is hardcoded here. To improve maintainability, this should be replaced with a shared constant. Please see my other comment about creating a MAX_FILE_SIZE constant.

Suggested change
content = f.read(10 * 1024 * 1024 + 1)
if len(content) > 10 * 1024 * 1024:
content = f.read(MAX_FILE_SIZE + 1)
if len(content) > MAX_FILE_SIZE:

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.

1 participant