-
Notifications
You must be signed in to change notification settings - Fork 61
CM-57660-Remove PAT token from repository URL #375
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
+172
−4
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
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
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,64 @@ | ||
| from typing import Optional | ||
| from urllib.parse import urlparse, urlunparse | ||
|
|
||
| from cycode.logger import get_logger | ||
|
|
||
| logger = get_logger('URL Utils') | ||
|
|
||
|
|
||
| def sanitize_repository_url(url: Optional[str]) -> Optional[str]: | ||
| """Remove credentials (username, password, tokens) from repository URL. | ||
|
|
||
| This function sanitizes repository URLs to prevent sending PAT tokens or other | ||
| credentials to the API. It handles both HTTP/HTTPS URLs with embedded credentials | ||
| and SSH URLs (which are returned as-is since they don't contain credentials in the URL). | ||
|
|
||
| Args: | ||
| url: Repository URL that may contain credentials (e.g., https://token@github.com/user/repo.git) | ||
|
|
||
| Returns: | ||
| Sanitized URL without credentials (e.g., https://github.com/user/repo.git), or None if input is None | ||
|
|
||
| Examples: | ||
| >>> sanitize_repository_url('https://token@github.com/user/repo.git') | ||
| 'https://github.com/user/repo.git' | ||
| >>> sanitize_repository_url('https://user:token@github.com/user/repo.git') | ||
| 'https://github.com/user/repo.git' | ||
| >>> sanitize_repository_url('git@github.com:user/repo.git') | ||
| 'git@github.com:user/repo.git' | ||
| >>> sanitize_repository_url(None) | ||
| None | ||
| """ | ||
| if not url: | ||
| return url | ||
|
|
||
| # Handle SSH URLs - no credentials to remove | ||
| # ssh:// URLs have the format ssh://git@host/path | ||
| if url.startswith('ssh://'): | ||
| return url | ||
| # git@host:path format (scp-style) | ||
| if '@' in url and '://' not in url and url.startswith('git@'): | ||
| return url | ||
|
|
||
| try: | ||
| parsed = urlparse(url) | ||
| # Remove username and password from netloc | ||
| # Reconstruct URL without credentials | ||
| sanitized_netloc = parsed.hostname | ||
| if parsed.port: | ||
| sanitized_netloc = f'{sanitized_netloc}:{parsed.port}' | ||
|
|
||
| return urlunparse( | ||
| ( | ||
| parsed.scheme, | ||
| sanitized_netloc, | ||
| parsed.path, | ||
| parsed.params, | ||
| parsed.query, | ||
| parsed.fragment, | ||
| ) | ||
| ) | ||
| except Exception as e: | ||
| logger.debug('Failed to sanitize repository URL, returning original, %s', {'url': url, 'error': str(e)}) | ||
| # If parsing fails, return original URL to avoid breaking functionality | ||
| return url | ||
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,80 @@ | ||
| from cycode.cli.utils.url_utils import sanitize_repository_url | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_with_token() -> None: | ||
| """Test that PAT tokens are removed from HTTPS URLs.""" | ||
| url = 'https://token@github.com/user/repo.git' | ||
| expected = 'https://github.com/user/repo.git' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_with_username_and_token() -> None: | ||
| """Test that username and token are removed from HTTPS URLs.""" | ||
| url = 'https://user:token@github.com/user/repo.git' | ||
cycode-security[bot] marked this conversation as resolved.
Show resolved
Hide resolved
cycode-security[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| expected = 'https://github.com/user/repo.git' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_with_port() -> None: | ||
| """Test that URLs with ports are handled correctly.""" | ||
| url = 'https://token@github.com:443/user/repo.git' | ||
| expected = 'https://github.com:443/user/repo.git' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_ssh_format() -> None: | ||
| """Test that SSH URLs are returned as-is (no credentials in URL format).""" | ||
| url = 'git@github.com:user/repo.git' | ||
| assert sanitize_repository_url(url) == url | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_ssh_protocol() -> None: | ||
| """Test that ssh:// URLs are returned as-is.""" | ||
| url = 'ssh://git@github.com/user/repo.git' | ||
| assert sanitize_repository_url(url) == url | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_no_credentials() -> None: | ||
| """Test that URLs without credentials are returned unchanged.""" | ||
| url = 'https://github.com/user/repo.git' | ||
| assert sanitize_repository_url(url) == url | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_none() -> None: | ||
| """Test that None input returns None.""" | ||
| assert sanitize_repository_url(None) is None | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_empty_string() -> None: | ||
| """Test that empty string is returned as-is.""" | ||
| assert sanitize_repository_url('') == '' | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_gitlab() -> None: | ||
| """Test that GitLab URLs are sanitized correctly.""" | ||
| url = 'https://oauth2:token@gitlab.com/user/repo.git' | ||
mateusz-sterczewski marked this conversation as resolved.
Show resolved
Hide resolved
cycode-security[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| expected = 'https://gitlab.com/user/repo.git' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_bitbucket() -> None: | ||
| """Test that Bitbucket URLs are sanitized correctly.""" | ||
| url = 'https://x-token-auth:token@bitbucket.org/user/repo.git' | ||
cycode-security[bot] marked this conversation as resolved.
Show resolved
Hide resolved
cycode-security[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| expected = 'https://bitbucket.org/user/repo.git' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_with_path_and_query() -> None: | ||
| """Test that URLs with paths, query params, and fragments are preserved.""" | ||
| url = 'https://token@github.com/user/repo.git?ref=main#section' | ||
| expected = 'https://github.com/user/repo.git?ref=main#section' | ||
| assert sanitize_repository_url(url) == expected | ||
|
|
||
|
|
||
| def test_sanitize_repository_url_invalid_url() -> None: | ||
| """Test that invalid URLs are returned as-is (graceful degradation).""" | ||
| # This should not raise an exception, but return the original | ||
| url = 'not-a-valid-url' | ||
| result = sanitize_repository_url(url) | ||
| # Should return original since parsing fails | ||
| assert result == url | ||
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.