diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..3e31d884 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,11 @@ +## Sentinel's Journal + +This journal documents CRITICAL security learnings found during codebase audits. +Only add entries for: +- Vulnerability patterns specific to this codebase +- Security fixes with unexpected side effects +- Rejected security changes with important constraints +- Surprising security gaps in architecture +- Reusable security patterns for this project + +--- diff --git a/main.py b/main.py index 86792da4..01a9b2a1 100644 --- a/main.py +++ b/main.py @@ -151,6 +151,19 @@ def sanitize_for_log(text: Any) -> str: s = str(text) if TOKEN and TOKEN in s: s = s.replace(TOKEN, "[REDACTED]") + + # Redact Basic Auth in URLs (e.g. https://user:pass@host) + s = re.sub(r"://[^/@]+@", "://[REDACTED]@", s) + + # Redact sensitive query parameters + sensitive_keys = r"token|key|secret|password|auth|access_token|api_key" + s = re.sub( + r"([?&])(" + sensitive_keys + r")=[^&\s]+", + r"\1\2=[REDACTED]", + s, + flags=re.IGNORECASE, + ) + # repr() safely escapes control characters (e.g., \n -> \\n, \x1b -> \\x1b) # This prevents log injection and terminal hijacking. safe = repr(s) diff --git a/tests/test_log_sanitization.py b/tests/test_log_sanitization.py index d8cdcb09..56d8bdfc 100644 --- a/tests/test_log_sanitization.py +++ b/tests/test_log_sanitization.py @@ -67,5 +67,25 @@ def test_create_folder_logs_unsafe_name(self, mock_get, mock_post, mock_sleep, m self.assertTrue(found_sanitized, "Should find sanitized name in logs") self.assertFalse(found_raw, "Should not find raw name in logs") + def test_sanitize_for_log_redacts_credentials(self): + """Test that sanitize_for_log redacts Basic Auth and sensitive query params.""" + # Test Basic Auth + url_with_auth = "https://user:password123@example.com/folder.json" + sanitized = main.sanitize_for_log(url_with_auth) + self.assertNotIn("password123", sanitized) + self.assertIn("[REDACTED]", sanitized) + + # Test Query Params + url_with_param = "https://example.com/folder.json?secret=mysecretkey" + sanitized_param = main.sanitize_for_log(url_with_param) + self.assertNotIn("mysecretkey", sanitized_param) + self.assertIn("[REDACTED]", sanitized_param) + + # Test Case Insensitivity + url_with_token = "https://example.com/folder.json?TOKEN=mytoken" + sanitized_token = main.sanitize_for_log(url_with_token) + self.assertNotIn("mytoken", sanitized_token) + self.assertIn("[REDACTED]", sanitized_token) + if __name__ == '__main__': unittest.main()