Address PR #119 review feedback: NO_COLOR compliance, naming, and test coverage#123
Conversation
|
🚫 This pull request was closed by Abhi Mehrotra (@abhimehro), so it was removed from the merge queue. See more details here. |
- Moved security check after Colors class definition to respect NO_COLOR - Fixed naming conventions: st -> file_stat, e -> error, exc_type -> exception_type - Fixed line-too-long issues by breaking into multiple lines - Use robust octal formatting: format(stat.S_IMODE(), '03o') - Added comprehensive tests for security features Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
- Extract common permission check logic into helper function - Reduces duplication across test methods Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request addresses code review feedback from PR #119 by improving NO_COLOR compliance, enhancing code quality through better naming conventions, and adding security-focused test coverage for .env file permission checks.
Changes:
- Moved .env permission check after Colors class initialization to properly respect the NO_COLOR environment variable
- Improved variable naming and code formatting (st → file_stat, e → error, exc_type → exception_type, multi-line strings)
- Added comprehensive security tests in a new test file covering permission checks, error handling, and platform-specific behavior
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| main.py | Relocated security warning code to respect NO_COLOR; improved naming and octal permission formatting |
| tests/test_security.py | New test file with 6 tests for security features in main.py and fix_env.py |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/test_security.py
Outdated
| def test_fix_env_sets_secure_permissions(tmp_path, monkeypatch): | ||
| """Test that fix_env.py sets secure permissions on .env file.""" | ||
| # Change to temp directory | ||
| monkeypatch.chdir(tmp_path) | ||
|
|
||
| # Create a .env file with insecure content | ||
| env_file = tmp_path / ".env" | ||
| env_file.write_text("TOKEN='test_token'\nPROFILE='test_profile'\n") | ||
|
|
||
| if os.name != 'nt': # Skip on Windows | ||
| # Set insecure permissions initially | ||
| os.chmod(env_file, 0o644) | ||
|
|
||
| # Run fix_env | ||
| fix_env.fix_env() | ||
|
|
||
| # Check that permissions are now secure | ||
| file_stat = os.stat(env_file) | ||
| mode = stat.S_IMODE(file_stat.st_mode) | ||
|
|
||
| # Verify permissions are 600 (read/write for owner only) | ||
| assert mode == 0o600, f"Expected 600, got {oct(mode)}" | ||
|
|
||
| # Verify no group or other permissions | ||
| assert not (file_stat.st_mode & stat.S_IRWXG), "Group has permissions" | ||
| assert not (file_stat.st_mode & stat.S_IRWXO), "Others have permissions" |
There was a problem hiding this comment.
On Windows (when os.name == 'nt'), this test does nothing and silently passes. The entire test logic is inside the if os.name != 'nt': block, so on Windows the test function runs but performs no assertions. Consider using pytest.mark.skipif decorator to explicitly skip Windows instead of having conditional test logic that makes the test silently pass without testing anything.
tests/test_security.py
Outdated
| def _check_env_permissions_and_warn(env_file_path, mock_stderr): | ||
| """ | ||
| Helper function that replicates the permission check logic from main.py. | ||
| This is used in tests to verify the behavior without importing main.py. | ||
| """ | ||
| file_stat = os.stat(env_file_path) | ||
| if file_stat.st_mode & (stat.S_IRWXG | stat.S_IRWXO): | ||
| platform_hint = ( | ||
| "Please secure your .env file so it is only readable by " | ||
| "the owner." | ||
| ) | ||
| if os.name != "nt": | ||
| platform_hint += " For example: 'chmod 600 .env'." | ||
| perms = format(stat.S_IMODE(file_stat.st_mode), '03o') | ||
| mock_stderr.write( | ||
| f"⚠️ Security Warning: .env file is " | ||
| f"readable by others ({perms})! {platform_hint}\n" | ||
| ) |
There was a problem hiding this comment.
The helper function _check_env_permissions_and_warn doesn't match the actual implementation in main.py. The main.py code uses Colors.WARNING and Colors.ENDC for formatting (lines 111-113 in main.py), but this helper function omits those color codes. This means the tests aren't accurately verifying the actual behavior of the security warnings in main.py.
| def test_env_permission_check_handles_stat_error(monkeypatch): | ||
| """Test that permission check handles stat() errors gracefully.""" | ||
| # Mock sys.stderr to capture warnings | ||
| mock_stderr = MagicMock() | ||
| monkeypatch.setattr(sys, "stderr", mock_stderr) | ||
|
|
||
| # Simulate a permission error | ||
| try: | ||
| raise PermissionError("Cannot access file") | ||
| except Exception as error: | ||
| exception_type = type(error).__name__ | ||
| sys.stderr.write( | ||
| f"⚠️ Security Warning: Could not check .env " | ||
| f"permissions ({exception_type}: {error})\n" | ||
| ) | ||
|
|
||
| # Verify error warning was written | ||
| mock_stderr.write.assert_called() | ||
| call_args = mock_stderr.write.call_args[0][0] | ||
| assert "Security Warning" in call_args | ||
| assert "Could not check .env permissions" in call_args | ||
| assert "PermissionError" in call_args |
There was a problem hiding this comment.
This test doesn't actually test the error handling in main.py's permission check code. It simulates an error by raising and catching an exception directly, then writing to stderr. To properly test the error handling, the test should mock os.stat to raise an exception and then verify that the actual permission check code in main.py handles it correctly. Consider importing the relevant code from main.py or restructuring main.py to make the permission check logic testable.
| def test_env_permission_check_warns_on_insecure_permissions(monkeypatch, tmp_path): | ||
| """Test that insecure .env permissions trigger a warning.""" | ||
| # Create a temporary .env file | ||
| env_file = tmp_path / ".env" | ||
| env_file.write_text("TOKEN=test") | ||
|
|
||
| # Set insecure permissions (readable by others) | ||
| if os.name != 'nt': # Skip on Windows | ||
| os.chmod(env_file, 0o644) | ||
|
|
||
| # Mock sys.stderr to capture warnings | ||
| mock_stderr = MagicMock() | ||
| monkeypatch.setattr(sys, "stderr", mock_stderr) | ||
|
|
||
| # Run the permission check logic | ||
| _check_env_permissions_and_warn(env_file, mock_stderr) | ||
|
|
||
| # Verify warning was written | ||
| mock_stderr.write.assert_called() | ||
| call_args = mock_stderr.write.call_args[0][0] | ||
| assert "Security Warning" in call_args | ||
| assert "readable by others" in call_args | ||
| assert "644" in call_args | ||
|
|
There was a problem hiding this comment.
On Windows (when os.name == 'nt'), this test does nothing and silently passes. The entire test logic is inside the if os.name != 'nt': block, so on Windows the test function runs but performs no assertions. Consider using pytest.mark.skipif decorator to explicitly skip Windows instead of having conditional test logic that makes the test silently pass without testing anything.
tests/test_security.py
Outdated
| def test_env_permission_check_no_warn_on_secure_permissions(monkeypatch, tmp_path): | ||
| """Test that secure .env permissions do not trigger a warning.""" | ||
| # Create a temporary .env file | ||
| env_file = tmp_path / ".env" | ||
| env_file.write_text("TOKEN=test") | ||
|
|
||
| # Set secure permissions (readable only by owner) | ||
| if os.name != 'nt': # Skip on Windows | ||
| os.chmod(env_file, 0o600) | ||
|
|
||
| # Mock sys.stderr to capture warnings | ||
| mock_stderr = MagicMock() | ||
| monkeypatch.setattr(sys, "stderr", mock_stderr) | ||
|
|
||
| # Run the permission check logic | ||
| _check_env_permissions_and_warn(env_file, mock_stderr) | ||
|
|
||
| # Verify no warning was written | ||
| mock_stderr.write.assert_not_called() |
There was a problem hiding this comment.
On Windows (when os.name == 'nt'), this test does nothing and silently passes. The entire test logic is inside the if os.name != 'nt': block, so on Windows the test function runs but performs no assertions. Consider using pytest.mark.skipif decorator to explicitly skip Windows instead of having conditional test logic that makes the test silently pass without testing anything.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
abhimehro
left a comment
There was a problem hiding this comment.
Hi @copilot, please apply any changes based on the following suggestions:
-
Lines +77 to +98 of tests/test_security.py: “This test doesn't actually test the error handling in main.py's permission check code. It simulates an error by raising and catching an exception directly, then writing to stderr. To properly test the error handling, the test should mock os.stat to raise an exception and then verify that the actual permission check code in main.py handles it correctly. Consider importing the relevant code from main.py or restructuring main.py to make the permission check logic testable.”
-
Lines +11 to +28 of tests/test_security.py: “The helper function _check_env_permissions_and_warn doesn't match the actual implementation in main.py. The main.py code uses Colors.WARNING and Colors.ENDC for formatting (lines 111-113 in main.py), but this helper function omits those color codes. This means the tests aren't accurately verifying the actual behavior of the security warnings in main.py.”
-
Lines +31 to +54 of tests/test_security.py: “On Windows (when os.name == 'nt'), this test does nothing and silently passes. The entire test logic is inside the if os.name != 'nt': block, so on Windows the test function runs but performs no assertions. Consider using pytest.mark.skipif decorator to explicitly skip Windows instead of having conditional test logic that makes the test silently pass without testing anything.”
-
Lines +77 to +98 of tests/test_security.py: “This test doesn't actually test the error handling in main.py's permission check code. It simulates an error by raising and catching an exception directly, then writing to stderr. To properly test the error handling, the test should mock os.stat to raise an exception and then verify that the actual permission check code in main.py handles it correctly. Consider importing the relevant code from main.py or restructuring main.py to make the permission check logic testable.”
-
Lines +56 to +74 of tests/test_security.py: “On Windows (when os.name == 'nt'), this test does nothing and silently passes. The entire test logic is inside the if os.name != 'nt': block, so on Windows the test function runs but performs no assertions. Consider using pytest.mark.skipif decorator to explicitly skip Windows instead of having conditional test logic that makes the test silently pass without testing anything.”
Thank you!
- Extract check_env_permissions() function in main.py for testability - Use pytest.mark.skipif for Windows-specific tests instead of conditional logic - Test actual implementation from main.py instead of duplicating logic - Mock os.stat to properly test error handling - Remove unused imports (tempfile, patch, mock_open) - Tests now accurately verify behavior with Colors.WARNING/ENDC Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
Changes applied in commit e8f3062:
All 13 tests pass, CodeQL scan clean with 0 alerts. |
29408b7
into
sentinel/secure-env-permissions-638384298251323176
Summary: Address PR #119 Review Comments
All review comments from PR #119 have been successfully addressed:
Code Quality & Linting Issues ✅
NO_COLOR Compliance ✅
Test Coverage ✅
Validation ✅
Latest Changes
main.py:
check_env_permissions()function for testabilitytests/test_security.py:
pytest.mark.skipiffor Windows-incompatible testscheck_env_permissions()from main.pyos.statto properly test error handlingOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.