Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions dev_tools/prepared_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,12 @@ def report_status_to_github(
if target_url is not None:
payload['target_url'] = target_url

url = "https://api.github.com/repos/{}/{}/statuses/{}?access_token={}".format(
self.repository.organization,
self.repository.name,
self.actual_commit_id,
self.repository.access_token,
url = "https://api.github.com/repos/{}/{}/statuses/{}".format(
self.repository.organization, self.repository.name, self.actual_commit_id
)
Comment on lines +100 to 102
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The actual_commit_id can be None when working with local uncommitted changes, as noted in the class docstring. If it is None, the resulting URL will be malformed (ending in /None), which will cause the GitHub API request to fail and raise an IOError. An early return should be added to handle this case gracefully.

Suggested change
url = "https://api.github.com/repos/{}/{}/statuses/{}".format(
self.repository.organization, self.repository.name, self.actual_commit_id
)
if self.actual_commit_id is None:
return
url = "https://api.github.com/repos/{}/{}/statuses/{}".format(
self.repository.organization, self.repository.name, self.actual_commit_id
)

headers = {'Authorization': 'token {}'.format(self.repository.access_token)}

response = requests.post(url, json=payload)
response = requests.post(url, json=payload, headers=headers)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The requests.post call is missing a timeout. Without a timeout, the request can hang indefinitely if the network is unstable or the server is unresponsive, which can block CI/CD pipelines. It is recommended to specify a timeout value.

Suggested change
response = requests.post(url, json=payload, headers=headers)
response = requests.post(url, json=payload, headers=headers, timeout=30)


if response.status_code != 201:
raise IOError(
Expand Down
38 changes: 38 additions & 0 deletions dev_tools/prepared_env_security_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import unittest
from unittest.mock import patch, MagicMock
from dev_tools.prepared_env import PreparedEnv
from dev_tools.github_repository import GithubRepository


class TestPreparedEnvSecurity(unittest.TestCase):
@patch('requests.post')
def test_report_status_to_github_token_in_header(self, mock_post):
# Setup
mock_response = MagicMock()
mock_response.status_code = 201
mock_post.return_value = mock_response

repo = GithubRepository('my-org', 'my-repo', 'my-token')
env = PreparedEnv(repo, 'my-commit', 'compare-commit', None, None)

# Execute
env.report_status_to_github('success', 'desc', 'ctx')

# Verify
args, kwargs = mock_post.call_args
url = args[0]
headers = kwargs.get('headers', {})

# Security check: Token should NOT be in the URL
self.assertNotIn('access_token=my-token', url, "Token should not be passed in the URL")

# Security check: Token should be in the Authorization header
self.assertEqual(
headers.get('Authorization'),
'token my-token',
"Token should be passed in the Authorization header",
)


if __name__ == '__main__':
unittest.main()
Loading