Skip to content

🛡️ Sentinel: [MEDIUM] Auto-fix insecure .env permissions#166

Closed
abhimehro wants to merge 1 commit intomainfrom
sentinel/auto-fix-env-perms-6052902321226855987
Closed

🛡️ Sentinel: [MEDIUM] Auto-fix insecure .env permissions#166
abhimehro wants to merge 1 commit intomainfrom
sentinel/auto-fix-env-perms-6052902321226855987

Conversation

@abhimehro
Copy link
Owner

Automatically remediate insecure .env file permissions (world-readable) by setting them to 600 (owner read/write only). Falls back to warning if fix fails.

Why:

  • Secrets (tokens) are stored in .env.
  • Loose permissions allow other users on the system to steal secrets.
  • Auto-fixing is safer and friendlier than just warning.

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

Automatically remediate insecure .env file permissions (world-readable) by setting them to 600 (owner read/write only). Falls back to warning if fix fails.

Why:
- Secrets (tokens) are stored in .env.
- Loose permissions allow other users on the system to steal secrets.
- Auto-fixing is safer and friendlier than just warning.

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 6, 2026 11:03
@trunk-io
Copy link

trunk-io bot commented Feb 6, 2026

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

@github-actions github-actions bot added the python label Feb 6, 2026
@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 security posture of the application by introducing an automatic remediation mechanism for .env file permissions. Instead of merely warning users about world-readable .env files, the system will now attempt to automatically restrict access to owner-only (permissions 600) on Unix-based platforms. This proactive approach minimizes the risk of sensitive data exposure, falling back to a clear warning if the automatic fix cannot be applied.

Highlights

  • Automatic Permission Remediation: Implemented an automatic fix for insecure .env file permissions on Unix-like systems, setting them to 600 (owner read/write).
  • Robust Error Handling: Added robust error handling for the auto-fix, ensuring a warning is issued if the permission change fails.
  • Comprehensive Testing: Introduced comprehensive unit tests to validate the auto-fix functionality, covering success, failure, and platform-specific behaviors.
  • Updated Security Test: Updated an existing security test to reflect the new auto-remediation logic for .env file permissions.
Changelog
  • main.py
    • Added logic within check_env_permissions to automatically change .env file permissions to 600 on Unix systems if they are insecure.
    • Incorporated a try-except block to catch potential chmod failures, appending a failure message to the warning.
    • Modified the output message to indicate a successful fix when applicable.
  • tests/test_permissions.py
    • New file containing dedicated tests for the .env permission auto-fix.
    • Tests cover successful permission fixing, graceful handling of chmod failures, scenarios where permissions are already secure, and correct warning behavior on Windows.
  • tests/test_security.py
    • Renamed test_env_permission_check_warns_on_insecure_permissions to test_env_permission_check_fixes_insecure_permissions.
    • Updated the test's assertions to verify that the .env file's permissions are indeed changed to 600 and a success message is logged, rather than just a warning.
Activity
  • The pull request was automatically generated by Jules based on a task initiated by @abhimehro.
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.

if "Fixed .env permissions" in call_args[0][0] and "set to 600" in call_args[0][0]:
found = True
break
assert found, "Success message not found in stderr writes"

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.
if "Security Warning" in msg and "Auto-fix failed" in msg:
found = True
break
assert found, "Failure warning not found in stderr writes"

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.
if "Security Warning" in msg and "chmod 600 .env" in msg:
found = True
break
assert found, "Windows warning not found"

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.

# Verify warning was written
# Verify it fixed the file
assert stat.S_IMODE(os.stat(env_file).st_mode) == 0o600

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.
assert "Security Warning" in call_args
assert "readable by others" in call_args
assert "644" in call_args
assert "Fixed .env permissions" in call_args

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.
assert "readable by others" in call_args
assert "644" in call_args
assert "Fixed .env permissions" in call_args
assert "set to 600" in call_args

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.
# Auto-fix permissions on Unix-like systems
os.chmod(env_path, stat.S_IRUSR | stat.S_IWUSR)
sys.stderr.write(
f"{Colors.GREEN}🔒 Security: Fixed .env permissions (set to 600).{Colors.ENDC}\n"

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (104/100) Warning

Line too long (104/100)
@@ -0,0 +1,128 @@
import os

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Missing module docstring Warning test

Missing module docstring
monkeypatch.setattr(os.path, "exists", lambda x: True)

# 0o600 is S_IRUSR | S_IWUSR
# os.stat returns st_mode which includes file type bits, but check_env_permissions masks with S_IRWXG | S_IRWXO

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (115/100) Warning test

Line too long (115/100)
# Auto-fix permissions on Unix-like systems
os.chmod(env_path, stat.S_IRUSR | stat.S_IWUSR)
sys.stderr.write(
f"{Colors.GREEN}🔒 Security: Fixed .env permissions (set to 600).{Colors.ENDC}\n"

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (104/100) Warning

Line too long (104/100)
@@ -0,0 +1,128 @@
import os

Check warning

Code scanning / Pylint (reported by Codacy)

Missing module docstring Warning test

Missing module docstring
monkeypatch.setattr(os, "stat", lambda x: mock_stat)

# Mock chmod to raise exception
def raise_error(*args):

Check warning

Code scanning / Pylint (reported by Codacy)

Missing function docstring Warning test

Missing function docstring
monkeypatch.setattr(os.path, "exists", lambda x: True)

# 0o600 is S_IRUSR | S_IWUSR
# os.stat returns st_mode which includes file type bits, but check_env_permissions masks with S_IRWXG | S_IRWXO

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (115/100) Warning test

Line too long (115/100)
monkeypatch.setattr(os, "stat", lambda x: mock_stat)

# Mock chmod to raise exception
def raise_error(*args):

Check notice

Code scanning / Pylint (reported by Codacy)

Unused argument 'args' Note test

Unused argument 'args'
if "Fixed .env permissions" in call_args[0][0] and "set to 600" in call_args[0][0]:
found = True
break
assert found, "Success message not found in stderr writes"

Check notice

Code scanning / Bandit (reported by Codacy)

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.
if "Security Warning" in msg and "Auto-fix failed" in msg:
found = True
break
assert found, "Failure warning not found in stderr writes"

Check notice

Code scanning / Bandit (reported by Codacy)

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.
if "Security Warning" in msg and "chmod 600 .env" in msg:
found = True
break
assert found, "Windows warning not found"

Check notice

Code scanning / Bandit (reported by Codacy)

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.

# Verify warning was written
# Verify it fixed the file
assert stat.S_IMODE(os.stat(env_file).st_mode) == 0o600

Check notice

Code scanning / Bandit (reported by Codacy)

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.
assert "Security Warning" in call_args
assert "readable by others" in call_args
assert "644" in call_args
assert "Fixed .env permissions" in call_args

Check notice

Code scanning / Bandit (reported by Codacy)

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.
assert "readable by others" in call_args
assert "644" in call_args
assert "Fixed .env permissions" in call_args
assert "set to 600" in call_args

Check notice

Code scanning / Bandit (reported by Codacy)

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 pull request introduces a helpful security enhancement by automatically fixing insecure permissions on .env files, falling back to a warning if the fix fails. The change is well-implemented and accompanied by a comprehensive new test suite in tests/test_permissions.py that covers various scenarios, including successful fixes, failures, and platform-specific behavior. The existing integration test in tests/test_security.py has also been updated to reflect the new auto-fix logic.

My review includes a few suggestions to improve the code's robustness and maintainability:

  • In main.py, I've recommended catching a more specific exception (OSError) to make error handling more precise.
  • In the new test file tests/test_permissions.py, I've suggested ways to make the test assertions cleaner and more direct, and proposed using a pytest fixture to reduce boilerplate code across the tests.

Overall, this is a solid contribution that improves the security posture of the application for users.

f"{Colors.GREEN}🔒 Security: Fixed .env permissions (set to 600).{Colors.ENDC}\n"
)
return
except Exception as e:

Choose a reason for hiding this comment

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

medium

It's better to catch a more specific exception than the generic Exception. os.chmod raises OSError on errors (including PermissionError, which is a subclass). Catching OSError will prevent masking other unrelated errors (like KeyboardInterrupt or other programming errors) and make the error handling more robust.

Suggested change
except Exception as e:
except OSError as e:

import sys
from unittest.mock import MagicMock
import main

Choose a reason for hiding this comment

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

medium

To reduce code duplication across your test functions, consider using a pytest fixture to handle the common mocking setup (os.name, os.path.exists, os.stat, os.chmod, sys.stderr). This will make your tests cleaner and easier to maintain.

Here's an example of how you could define such a fixture:

import pytest

@pytest.fixture
def mock_env_check(monkeypatch):
    """Fixture for mocking environment for permission checks."""
    monkeypatch.setattr(os, "name", "posix")
    monkeypatch.setattr(os.path, "exists", lambda _: True)

    mock_stat_obj = MagicMock()
    monkeypatch.setattr(os, "stat", lambda _: mock_stat_obj)

    mock_chmod = MagicMock()
    monkeypatch.setattr(os, "chmod", mock_chmod)

    mock_stderr = MagicMock()
    monkeypatch.setattr(sys, "stderr", mock_stderr)

    class Mocks:
        stat = mock_stat_obj
        chmod = mock_chmod
        stderr = mock_stderr

    return Mocks()

You could then refactor your tests to use it, which would simplify the setup in each test.

Comment on lines +37 to +42
found = False
for call_args in mock_stderr.write.call_args_list:
if "Fixed .env permissions" in call_args[0][0] and "set to 600" in call_args[0][0]:
found = True
break
assert found, "Success message not found in stderr writes"

Choose a reason for hiding this comment

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

medium

This loop to find the message in stderr can be simplified. Since a successful fix should only write to stderr once before returning, you can assert that write was called once and then check the content of that single call. This makes the assertion more direct and readable.

    mock_stderr.write.assert_called_once()
    output = mock_stderr.write.call_args[0][0]
    assert "Fixed .env permissions" in output and "set to 600" in output

Comment on lines +65 to +71
found = False
for call_args in mock_stderr.write.call_args_list:
msg = call_args[0][0]
if "Security Warning" in msg and "Auto-fix failed" in msg:
found = True
break
assert found, "Failure warning not found in stderr writes"

Choose a reason for hiding this comment

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

medium

Similar to the previous test, this loop can be simplified. When the auto-fix fails, the function writes a single warning message to stderr. You can assert that write was called once and then inspect its content directly.

    mock_stderr.write.assert_called_once()
    output = mock_stderr.write.call_args[0][0]
    assert "Security Warning" in output and "Auto-fix failed" in output

Comment on lines +122 to +128
found = False
for call_args in mock_stderr.write.call_args_list:
msg = call_args[0][0]
if "Security Warning" in msg and "chmod 600 .env" in msg:
found = True
break
assert found, "Windows warning not found"

Choose a reason for hiding this comment

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

medium

This loop can also be simplified. On Windows, the function should write a single warning message. A direct assertion on a single call is cleaner and more precise.

    mock_stderr.write.assert_called_once()
    output = mock_stderr.write.call_args[0][0]
    assert "Security Warning" in output and "chmod 600 .env" in output

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 implements automatic remediation of insecure .env file permissions by setting them to 600 (owner read/write only) on Unix-like systems, with graceful fallback to warning messages on Windows or when the fix fails. This enhances security by proactively fixing a common misconfiguration that could expose secrets to other users on the system.

Changes:

  • Modified check_env_permissions() in main.py to automatically fix insecure permissions on Unix-like systems using os.chmod()
  • Updated test in test_security.py to verify the auto-fix behavior with real file operations
  • Added comprehensive unit tests in new test_permissions.py file covering all scenarios (successful fix, fix failure, already secure, and Windows behavior)

Reviewed changes

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

File Description
main.py Added try-except block to auto-fix .env permissions to 600 on Unix, with success message and fallback to warning on failure
tests/test_security.py Updated test name and assertions to verify automatic permission fixing instead of just warning
tests/test_permissions.py New comprehensive test suite with unit tests covering all permission fix scenarios using mocked dependencies

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

Copilot AI added a commit that referenced this pull request Feb 8, 2026
…itization, add dry-run plan details

Incorporates the best changes from 36 Jules PRs, addressing review feedback:

Bolt (Performance) - from PR #173:
- Pre-compile PROFILE_ID_PATTERN and RULE_PATTERN at module level
- Use compiled patterns in is_valid_profile_id_format, validate_profile_id, and is_valid_rule
- Supersedes PRs: #140, #143, #152, #155, #158, #161, #167, #170, #173

Sentinel (Security) - from PR #172 with review feedback:
- Enhance sanitize_for_log to redact Basic Auth credentials in URLs
- Redact sensitive query parameters (token, key, secret, password, etc.)
- Handle fragment separators (#) per Gemini Code Assist review
- Use [^&#\s]* pattern per Copilot reviewer suggestion
- Update docstring per reviewer suggestion
- Supersedes PRs: #142, #145, #148, #151, #154, #157, #160, #169, #172

Palette (UX) - from PR #174 with lint fixes:
- Add print_plan_details function for dry-run visibility
- Fix duplicate render_progress_bar definition bug
- Supersedes PRs: #139, #141, #144, #147, #150, #153, #156, #159, #162, #165, #168, #171, #174

Also: #146, #149, #164 (parallel folder deletion) and #166 (auto-fix .env perms) are independent features not consolidated here.

Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
abhimehro added a commit that referenced this pull request Feb 9, 2026
…itization, add dry-run plan details

Incorporates the best changes from 36 Jules PRs, addressing review feedback:

Bolt (Performance) - from PR #173:
- Pre-compile PROFILE_ID_PATTERN and RULE_PATTERN at module level
- Use compiled patterns in is_valid_profile_id_format, validate_profile_id, and is_valid_rule
- Supersedes PRs: #140, #143, #152, #155, #158, #161, #167, #170, #173

Sentinel (Security) - from PR #172 with review feedback:
- Enhance sanitize_for_log to redact Basic Auth credentials in URLs
- Redact sensitive query parameters (token, key, secret, password, etc.)
- Handle fragment separators (#) per Gemini Code Assist review
- Use [^&#\s]* pattern per Copilot reviewer suggestion
- Update docstring per reviewer suggestion
- Supersedes PRs: #142, #145, #148, #151, #154, #157, #160, #169, #172

Palette (UX) - from PR #174 with lint fixes:
- Add print_plan_details function for dry-run visibility
- Fix duplicate render_progress_bar definition bug
- Supersedes PRs: #139, #141, #144, #147, #150, #153, #156, #159, #162, #165, #168, #171, #174

Also: #146, #149, #164 (parallel folder deletion) and #166 (auto-fix .env perms) are independent features not consolidated here.

Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
Copilot AI added a commit that referenced this pull request Feb 9, 2026
Security: Automatically fix insecure .env permissions to 600
- Auto-fix permissions when .env is world-readable (prevents secret theft)
- Use OSError instead of generic Exception (more specific)
- Comprehensive error handling with fallback warnings
- Windows-specific handling (warns but doesn't try to chmod)
- Add comprehensive test coverage for all scenarios
- Test auto-fix success, failure, Windows, secure files, missing files, errors

Addresses feedback from PR #166

Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
abhimehro added a commit that referenced this pull request Feb 9, 2026
Security: Automatically fix insecure .env permissions to 600
- Auto-fix permissions when .env is world-readable (prevents secret theft)
- Use OSError instead of generic Exception (more specific)
- Comprehensive error handling with fallback warnings
- Windows-specific handling (warns but doesn't try to chmod)
- Add comprehensive test coverage for all scenarios
- Test auto-fix success, failure, Windows, secure files, missing files, errors

Addresses feedback from PR #166

Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
@abhimehro abhimehro closed this Feb 9, 2026
@abhimehro abhimehro deleted the sentinel/auto-fix-env-perms-6052902321226855987 branch February 9, 2026 02:18
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