-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Security Improvements (CodeQL, DAST, Log Redaction) #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
4422f0c
feat: implement log scrubbing and security workflows
Miyamura80 bb1a1b4
Update src/utils/logging_config.py
Miyamura80 a27af99
chore: remove DAST workflow (project is not a webapp)
Miyamura80 c530988
Update src/utils/logging_config.py
Miyamura80 8738ece
🚀 optimize log scrubbing by pre-compiling PII regex patterns
Miyamura80 7470e97
🐛 extend log scrubbing to exceptions and tracebacks
Miyamura80 451e5b0
✅ add unit tests for PII redaction in logs and exceptions
Miyamura80 60adac1
Update src/utils/logging_config.py
Miyamura80 438fcfa
✨ fix import sorting in tests to pass CI
Miyamura80 6114987
🐛 fix syntax error in log scrubbing logic
Miyamura80 1a03811
🐛 preserve exception instance in log scrubbing to avoid breaking logu…
Miyamura80 dfe6d02
✅ add setup_shared_variables fixture to TestLoggingSecurity
Miyamura80 5aaa891
⚙️ explicitly exclude test files from vulture to fix CI failure
Miyamura80 cd71987
🔨 expand PII redaction patterns for comprehensive coverage
Miyamura80 7a55336
🐛 fix email regex bug and reorder patterns for specificity
Miyamura80 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| name: "CodeQL" | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ "main" ] | ||
| pull_request: | ||
| branches: [ "main" ] | ||
| schedule: | ||
| - cron: '34 20 * * 1' | ||
|
|
||
| jobs: | ||
| analyze: | ||
| name: Analyze | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| security-events: write | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| language: [ 'javascript-typescript', 'python' ] | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Initialize CodeQL | ||
| uses: github/codeql-action/init@v3 | ||
| with: | ||
| languages: ${{ matrix.language }} | ||
|
|
||
| - name: Autobuild | ||
| uses: github/codeql-action/autobuild@v3 | ||
|
|
||
| - name: Perform CodeQL Analysis | ||
| uses: github/codeql-action/analyze@v3 | ||
| with: | ||
| category: "/language:${{matrix.language}}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| import pytest | ||
|
|
||
| from src.utils.logging_config import scrub_sensitive_data | ||
| from tests.test_template import TestTemplate | ||
|
|
||
|
|
||
| class TestLoggingSecurity(TestTemplate): | ||
|
Miyamura80 marked this conversation as resolved.
|
||
| @pytest.fixture(autouse=True) | ||
| def setup_shared_variables(self, setup): | ||
| # Initialize shared attributes here | ||
| pass | ||
|
|
||
| def test_email_redaction(self): | ||
| """Test that email addresses are redacted from log messages.""" | ||
| record = {"message": "User email is test@example.com", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert "test@example.com" not in record["message"] | ||
| assert "[REDACTED_EMAIL]" in record["message"] | ||
|
|
||
| def test_api_key_redaction(self): | ||
| """Test that OpenAI API keys are redacted from log messages.""" | ||
| api_key = "sk-abc123def456ghi789jkl012mno345pqr678stu901" | ||
| record = {"message": f"Using key: {api_key}", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert api_key not in record["message"] | ||
| assert "[REDACTED_API_KEY]" in record["message"] | ||
|
|
||
| def test_multiple_redactions(self): | ||
| """Test redacting multiple sensitive items in a single message.""" | ||
| record = { | ||
| "message": "Email test@example.com and key sk-123456789012345678901234", | ||
| "exception": None, | ||
| } | ||
| scrub_sensitive_data(record) | ||
| assert "[REDACTED_EMAIL]" in record["message"] | ||
| assert "[REDACTED_API_KEY]" in record["message"] | ||
| assert "test@example.com" not in record["message"] | ||
| assert "sk-123456789012345678901234" not in record["message"] | ||
|
|
||
| def test_exception_message_redaction(self): | ||
| """Test that PII is redacted from exception messages.""" | ||
| # Mocking the exception tuple structure used by loguru: (type, value, traceback) | ||
| exception_value = ValueError("Failed for user test@example.com") | ||
| record = { | ||
| "message": "An error occurred", | ||
| "exception": (ValueError, exception_value, None), | ||
| } | ||
|
|
||
| scrub_sensitive_data(record) | ||
|
|
||
| # Verify message (even if it didn't have PII) | ||
| assert record["message"] == "An error occurred" | ||
|
|
||
| # Verify exception redaction | ||
| _, value, _ = record["exception"] | ||
| assert "test@example.com" not in str(value) | ||
| assert "[REDACTED_EMAIL]" in str(value) | ||
|
|
||
| def test_exception_api_key_redaction(self): | ||
| """Test redacting API keys from exception values.""" | ||
| api_key = "sk-123456789012345678901234" | ||
| exception_value = Exception(f"Auth failed with {api_key}") | ||
| record = {"message": "Error", "exception": (Exception, exception_value, None)} | ||
|
|
||
| scrub_sensitive_data(record) | ||
|
|
||
| _, value, _ = record["exception"] | ||
| assert api_key not in str(value) | ||
| assert "[REDACTED_API_KEY]" in str(value) | ||
|
|
||
| def test_no_sensitive_data_unchanged(self): | ||
| """Test that normal messages are left untouched.""" | ||
| original_message = "Normal system message" | ||
| record = {"message": original_message, "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert record["message"] == original_message | ||
|
|
||
| def test_anthropic_api_key_redaction(self): | ||
| """Test that Anthropic API keys are redacted.""" | ||
| api_key = "sk-ant-api03-abc123def456ghi789jkl012mno345pqr678" | ||
| record = {"message": f"Using Anthropic key: {api_key}", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert api_key not in record["message"] | ||
| assert "[REDACTED_API_KEY]" in record["message"] | ||
|
|
||
| def test_stripe_api_key_redaction(self): | ||
| """Test that Stripe API keys are redacted.""" | ||
| # Construct keys dynamically to avoid GitHub secret scanning | ||
| suffix = "0" * 24 | ||
| prefixes = ["sk" + "_live_", "sk" + "_test_", "pk" + "_live_", "rk" + "_live_"] | ||
|
|
||
| for prefix in prefixes: | ||
| key = prefix + suffix | ||
| record = {"message": f"Stripe key: {key}", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert key not in record["message"] | ||
| assert "[REDACTED_API_KEY]" in record["message"] | ||
|
|
||
| def test_bearer_token_redaction(self): | ||
| """Test that Authorization Bearer tokens are redacted.""" | ||
| token = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkw" | ||
| record = {"message": f"Authorization: {token}", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in record["message"] | ||
| assert "[REDACTED_BEARER_TOKEN]" in record["message"] | ||
|
|
||
| def test_generic_api_key_redaction(self): | ||
| """Test that generic api_key patterns are redacted.""" | ||
| patterns = [ | ||
| "api_key=abc123def456ghi789jkl012", | ||
| "API-KEY: abc123def456ghi789jkl012", | ||
| "apikey='abc123def456ghi789jkl012'", | ||
| "project_key=abc123def456ghi789jkl012", | ||
| "secret-key: abc123def456ghi789jkl012", | ||
| ] | ||
| for pattern in patterns: | ||
| record = {"message": f"Config: {pattern}", "exception": None} | ||
| scrub_sensitive_data(record) | ||
| assert "abc123def456ghi789jkl012" not in record["message"] | ||
| assert "[REDACTED_KEY]" in record["message"] | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.