Skip to content

🛡️ Sentinel: Secure bootstrapping and input validation hardening#249

Merged
abhimehro merged 3 commits intomainfrom
sentinel-bootstrapping-validation-10893054420746374177
Feb 16, 2026
Merged

🛡️ Sentinel: Secure bootstrapping and input validation hardening#249
abhimehro merged 3 commits intomainfrom
sentinel-bootstrapping-validation-10893054420746374177

Conversation

@abhimehro
Copy link
Owner

This PR addresses a TOCTOU vulnerability in .env loading by moving load_dotenv() to execute after permission checks. It also hardens folder name validation to prevent path traversal and command option injection attacks.

Vulnerability:

  1. Secrets were loaded from .env at module import time, before permissions were fixed to 0600.
  2. Folder names allowed path traversal (..) and option injection (-flag).

Fix:

  1. Deferred load_dotenv until after check_env_permissions.
  2. Updated is_valid_folder_name to reject dangerous patterns.
  3. Added comprehensive tests.

PR created automatically by Jules for task 10893054420746374177 started by @abhimehro

- Move `load_dotenv` to `main()` to ensure `.env` permissions are checked before loading secrets.
- Re-initialize `TOKEN` inside `main()` after loading env.
- Harden `is_valid_folder_name` to block path traversal (`.` and `..`) and option injection (`-`).
- Added tests for bootstrapping logic and folder validation hardening.

Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
@google-labs-jules
Copy link

👋 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 February 15, 2026 10:57
@trunk-io
Copy link

trunk-io bot commented Feb 15, 2026

😎 Merged manually by @abhimehro - details.

@github-actions
Copy link

👋 Development Partner is reviewing this PR. Will provide feedback shortly.

@gemini-code-assist
Copy link

Summary of Changes

Hello @abhimehro, 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 significantly enhances the application's security posture by addressing two critical vulnerabilities. It rectifies an insecure bootstrapping order that could expose sensitive environment variables and strengthens input validation to prevent malicious folder names from being used for path traversal or command injection. These changes ensure a more robust and secure handling of configuration and user-provided data.

Highlights

  • Secure .env Loading: The load_dotenv() function is now executed after critical permission checks, mitigating a Time-of-Check-to-Time-of-Use (TOCTOU) vulnerability where secrets could be exposed from world-readable .env files.
  • Hardened Folder Name Validation: Enhanced is_valid_folder_name to prevent path traversal (. and ..) and command option injection (names starting with -) attacks.
  • Comprehensive Testing: New and updated tests were added to cover the secure bootstrapping order and the hardened folder name validation logic.
Changelog
  • .jules/sentinel.md
    • Documented the "Insecure Bootstrapping Order (.env Loading)" vulnerability, learning, and prevention strategies.
  • main.py
    • Removed the module-level call to load_dotenv().
    • Modified is_valid_folder_name to explicitly block folder names that are . or .., and those starting with -.
    • Moved load_dotenv() into the main() function, ensuring it runs after check_env_permissions().
    • Added re-initialization of the global TOKEN variable within main() to reflect values loaded from .env.
  • tests/test_bootstrapping.py
    • Added a new test file to verify that main() correctly reloads the TOKEN from environment variables after load_dotenv() is called.
  • tests/test_folder_validation.py
    • Expanded test_folder_name_security with new test cases to validate the blocking of path traversal (. and ..) and command option injection (names starting with -).
  • tests/test_push_rules_perf.py
    • Ensured the main module instance is correctly referenced in setUp for consistency during testing.
  • uv.lock
    • Updated dependencies to include pytest-xdist and execnet.
Activity
  • No human activity (comments, reviews, etc.) has been recorded on this pull request yet.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

# Force reload main to pick up initial token
import importlib
importlib.reload(main)
assert main.TOKEN == "initial_token"

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
# Force reload main to pick up initial token
import importlib
importlib.reload(main)
assert main.TOKEN == "initial_token"

Check notice

Code scanning / Bandit

Possible hardcoded password: 'initial_token' Note test

Possible hardcoded password: 'initial_token'
# 2. Mock dependencies
# Mock load_dotenv to simulate loading a .env file that changes TOKEN
def mock_load_dotenv():
os.environ["TOKEN"] = "loaded_from_env_file"

Check notice

Code scanning / Bandit

Possible hardcoded password: 'loaded_from_env_file' Note test

Possible hardcoded password: 'loaded_from_env_file'

# 4. Verify
# load_dotenv must have been called
assert mock_load_dotenv_call.called

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
# But verifying exact order is hard with separate mocks unless we use a manager
# However, if main.TOKEN is updated, we know the logic ran.

assert main.TOKEN == "loaded_from_env_file"

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
# But verifying exact order is hard with separate mocks unless we use a manager
# However, if main.TOKEN is updated, we know the logic ran.

assert main.TOKEN == "loaded_from_env_file"

Check notice

Code scanning / Bandit

Possible hardcoded password: 'loaded_from_env_file' Note test

Possible hardcoded password: 'loaded_from_env_file'
Comment on lines +80 to +81
assert (
main.validate_folder_data(pt_data, f"http://pt-{pt_name}.com") is False

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines +89 to +90
assert (
main.validate_folder_data(opt_data, f"http://opt-{opt_name}.com") is False

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Copy link

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

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that effectively addresses two security vulnerabilities: a Time-of-Check, Time-of-Use (TOCTOU) race condition during .env file loading, and insufficient input validation for folder names. The changes are well-implemented and follow security best practices. Moving load_dotenv() to after the permission checks and re-initializing the TOKEN global variable correctly mitigates the bootstrapping vulnerability. The hardening of is_valid_folder_name to prevent path traversal and command option injection is a solid defensive measure. The accompanying tests are thorough and validate the fixes effectively, including a good test for the module-level bootstrapping logic. The code quality is high, and I have no recommendations for changes.

Copy link

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 addresses critical security vulnerabilities in the application's bootstrapping process and input validation. The primary fix prevents a Time-of-Check-Time-of-Use (TOCTOU) vulnerability where .env secrets could be loaded before permission checks, potentially exposing them if the file was world-readable. Additionally, it hardens folder name validation to block path traversal (..) and command option injection (-flag) attacks.

Changes:

  • Moved load_dotenv() from module-level to main() after check_env_permissions() to prevent TOCTOU vulnerability
  • Enhanced is_valid_folder_name() to reject path traversal patterns (., ..) and command option injection (names starting with -)
  • Added comprehensive test coverage for bootstrapping security and input validation hardening
  • Added pytest-xdist dependency for parallel test execution

Reviewed changes

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

Show a summary per file
File Description
main.py Deferred load_dotenv() to main() after permission checks; re-initialize TOKEN after loading; added path traversal and command injection blocks to folder validation
tests/test_bootstrapping.py New test verifying TOKEN re-initialization after load_dotenv() in main()
tests/test_folder_validation.py Added tests for path traversal and command option injection blocking
tests/test_push_rules_perf.py Added module reload handling to ensure tests use current main instance
.jules/sentinel.md Documented the bootstrapping TOCTOU vulnerability and mitigation
uv.lock Added pytest-xdist 3.8.0 and execnet 2.1.2 dependencies

Comment on lines +64 to +67
# check_env_permissions must have been called BEFORE load_dotenv
# We can check order by checking if check_perms was called
# But verifying exact order is hard with separate mocks unless we use a manager
# However, if main.TOKEN is updated, we know the logic ran.
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The test verifies that load_dotenv was called and that TOKEN was updated, but doesn't explicitly verify that check_env_permissions was called BEFORE load_dotenv, which is the critical security requirement. Consider using a mock manager to verify call ordering:

from unittest.mock import call

mock_manager = MagicMock()
mock_manager.attach_mock(mock_check_perms, 'check_env_permissions')
mock_manager.attach_mock(mock_load_dotenv_call, 'load_dotenv')

# After main.main() call:
expected_calls = [
    call.check_env_permissions(),
    call.load_dotenv()
]
actual_calls = mock_manager.mock_calls[:2]  # First two calls
assert actual_calls == expected_calls, f"Expected {expected_calls}, got {actual_calls}"

This ensures the security fix (permissions checked before loading secrets) is actually enforced.

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +92
# Case 8: Path Traversal (Security Hardening)
# Block '.' and '..' which could be used for path traversal
path_traversal_cases = [".", ".."]
for pt_name in path_traversal_cases:
pt_data = {"group": {"group": pt_name}}
assert (
main.validate_folder_data(pt_data, f"http://pt-{pt_name}.com") is False
), f"Path traversal name '{pt_name}' should be blocked"

# Case 9: Command Option Injection (Security Hardening)
# Block names starting with '-' which could be interpreted as flags
option_injection_cases = ["-flag", "--flag", "-v", "--verbose"]
for opt_name in option_injection_cases:
opt_data = {"group": {"group": opt_name}}
assert (
main.validate_folder_data(opt_data, f"http://opt-{opt_name}.com") is False
), f"Option injection name '{opt_name}' should be blocked"

Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

Consider adding edge case tests for whitespace-padded malicious patterns to verify the defensive stripping logic works correctly. For example:

  • " . " (space-padded dot)
  • " .. " (space-padded double dot)
  • " -flag" (space-padded option injection)
  • "- flag" (hyphen with space after)

While the current code should handle these correctly (line 712 rejects pure whitespace, and lines 721-727 strip before checking), explicit tests would document this behavior and prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +719 to +728
# Security: Block path traversal attempts
# Check stripped name to prevent whitespace bypass (e.g. " . ")
clean_name = name.strip()
if clean_name in (".", ".."):
return False

# Security: Block command option injection (if name is passed to shell)
if clean_name.startswith("-"):
return False

Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

Consider updating the function's docstring (lines 702-711) to document the newly added security protections. The docstring mentions "path traversal" but doesn't specify that it blocks "." and ".." patterns. It also doesn't mention the new command option injection protection (names starting with "-"). Adding these details would improve code maintainability and help future developers understand the complete security model.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

👋 Development Partner is reviewing this PR. Will provide feedback shortly.

@github-actions
Copy link

👋 Development Partner is reviewing this PR. Will provide feedback shortly.

@abhimehro abhimehro merged commit 8b037a9 into main Feb 16, 2026
9 of 11 checks passed
@abhimehro abhimehro deleted the sentinel-bootstrapping-validation-10893054420746374177 branch February 16, 2026 02:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants