Skip to content

Commit 0c43195

Browse files
feat: Validate service part of api request url
1 parent b759c96 commit 0c43195

10 files changed

Lines changed: 107 additions & 41 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import click
2+
3+
from codecov_cli.helpers.git import GitService
4+
5+
# The set of acceptable upload services from Shelter
6+
_CODECOV_UPLOAD_SERVICES = frozenset(s.value for s in GitService) | {"github-actions"}
7+
8+
9+
def validate_upload_service(
10+
service: str,
11+
) -> None:
12+
allowed = ", ".join(sorted(_CODECOV_UPLOAD_SERVICES))
13+
if not service:
14+
raise click.ClickException(
15+
"Upload service is missing (Codecov requires it for upload URLs). "
16+
f"Pass --git-service with one of: {allowed}"
17+
)
18+
if service not in _CODECOV_UPLOAD_SERVICES:
19+
raise click.ClickException(
20+
f"Invalid upload service {service!r}. Use one of: {allowed}"
21+
)

codecov-cli/codecov_cli/services/commit/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
log_warnings_and_errors_if_any,
1010
send_post_request,
1111
)
12+
from codecov_cli.helpers.upload_url_validation import validate_upload_service
1213

1314
logger = logging.getLogger("codecovcli")
1415

15-
1616
def create_commit_logic(
1717
commit_sha: str,
1818
parent_sha: typing.Optional[str],
@@ -78,7 +78,9 @@ def send_commit_data(
7878
}
7979

8080
upload_url = enterprise_url or CODECOV_INGEST_URL
81-
url = f"{upload_url}/upload/{service}/{slug}/commits"
81+
service_part = (service or "").strip()
82+
validate_upload_service(service_part)
83+
url = f"{upload_url}/upload/{service_part}/{slug}/commits"
8284
return send_post_request(
8385
url=url,
8486
data=data,

codecov-cli/codecov_cli/services/empty_upload/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
from codecov_cli.helpers.config import CODECOV_API_URL
55
from codecov_cli.helpers.encoder import encode_slug
6+
from codecov_cli.helpers.upload_url_validation import validate_upload_service
7+
68
from codecov_cli.helpers.request import (
79
get_token_header,
810
log_warnings_and_errors_if_any,
@@ -25,7 +27,9 @@ def empty_upload_logic(
2527
encoded_slug = encode_slug(slug)
2628
headers = get_token_header(token)
2729
upload_url = enterprise_url or CODECOV_API_URL
28-
url = f"{upload_url}/upload/{git_service}/{encoded_slug}/commits/{commit_sha}/empty-upload"
30+
service_part = (git_service or "").strip()
31+
validate_upload_service(service_part)
32+
url = f"{upload_url}/upload/{service_part}/{encoded_slug}/commits/{commit_sha}/empty-upload"
2933
sending_result = send_post_request(
3034
url=url,
3135
headers=headers,

codecov-cli/codecov_cli/services/report/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
request_result,
1515
send_post_request,
1616
)
17+
from codecov_cli.helpers.upload_url_validation import validate_upload_service
1718

1819
logger = logging.getLogger("codecovcli")
1920
MAX_NUMBER_TRIES = 3
@@ -61,7 +62,9 @@ def send_create_report_request(
6162
}
6263
headers = get_token_header(token)
6364
upload_url = enterprise_url or CODECOV_INGEST_URL
64-
url = f"{upload_url}/upload/{service}/{encoded_slug}/commits/{commit_sha}/reports"
65+
service_part = (service or "").strip()
66+
validate_upload_service(service_part)
67+
url = f"{upload_url}/upload/{service_part}/{encoded_slug}/commits/{commit_sha}/reports"
6568
return send_post_request(url=url, headers=headers, data=data)
6669

6770

@@ -106,7 +109,9 @@ def send_reports_result_request(
106109
}
107110
headers = get_token_header(token)
108111
upload_url = enterprise_url or CODECOV_API_URL
109-
url = f"{upload_url}/upload/{service}/{encoded_slug}/commits/{commit_sha}/reports/{report_code}/results"
112+
service_part = (service or "").strip()
113+
validate_upload_service(service_part)
114+
url = f"{upload_url}/upload/{service_part}/{encoded_slug}/commits/{commit_sha}/reports/{report_code}/results"
110115
return send_post_request(url=url, data=data, headers=headers)
111116

112117

@@ -121,7 +126,9 @@ def send_reports_result_get_request(
121126
):
122127
headers = get_token_header(token)
123128
upload_url = enterprise_url or CODECOV_API_URL
124-
url = f"{upload_url}/upload/{service}/{encoded_slug}/commits/{commit_sha}/reports/{report_code}/results"
129+
service_part = (service or "").strip()
130+
validate_upload_service(service_part)
131+
url = f"{upload_url}/upload/{service_part}/{encoded_slug}/commits/{commit_sha}/reports/{report_code}/results"
125132
number_tries = 0
126133
while number_tries < MAX_NUMBER_TRIES:
127134
resp = request.get(url=url, headers=headers)

codecov-cli/codecov_cli/services/upload_completion/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from codecov_cli.helpers.config import CODECOV_API_URL
55
from codecov_cli.helpers.encoder import encode_slug
6+
from codecov_cli.helpers.upload_url_validation import validate_upload_service
67
from codecov_cli.helpers.request import (
78
get_token_header,
89
log_warnings_and_errors_if_any,
@@ -24,7 +25,9 @@ def upload_completion_logic(
2425
encoded_slug = encode_slug(slug)
2526
headers = get_token_header(token)
2627
upload_url = enterprise_url or CODECOV_API_URL
27-
url = f"{upload_url}/upload/{git_service}/{encoded_slug}/commits/{commit_sha}/upload-complete"
28+
service_part = (git_service or "").strip()
29+
validate_upload_service(service_part)
30+
url = f"{upload_url}/upload/{service_part}/{encoded_slug}/commits/{commit_sha}/upload-complete"
2831
data = {
2932
"cli_args": args,
3033
}

codecov-cli/tests/services/commit/test_commit_service.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import uuid
22

3+
import click
4+
import pytest
35
from click.testing import CliRunner
46

57
from codecov_cli.services.commit import create_commit_logic, send_commit_data
@@ -26,7 +28,7 @@ def test_commit_command_with_warnings(mocker):
2628
branch="branch",
2729
slug="owner/repo",
2830
token="token",
29-
service="service",
31+
service="github",
3032
)
3133

3234
out_bytes = parse_outstreams_into_log_lines(outstreams[0].getvalue())
@@ -43,7 +45,7 @@ def test_commit_command_with_warnings(mocker):
4345
branch="branch",
4446
slug="owner::::repo",
4547
token="token",
46-
service="service",
48+
service="github",
4749
enterprise_url=None,
4850
args=None,
4951
)
@@ -72,7 +74,7 @@ def test_commit_command_with_error(mocker):
7274
branch="branch",
7375
slug="owner/repo",
7476
token="token",
75-
service="service",
77+
service="github",
7678
enterprise_url=None,
7779
args={},
7880
)
@@ -93,7 +95,7 @@ def test_commit_command_with_error(mocker):
9395
branch="branch",
9496
slug="owner::::repo",
9597
token="token",
96-
service="service",
98+
service="github",
9799
enterprise_url=None,
98100
args={},
99101
)
@@ -112,7 +114,7 @@ def test_commit_sender_200(mocker):
112114
"branch",
113115
"owner::::repo",
114116
token,
115-
"service",
117+
"github",
116118
None,
117119
None,
118120
)
@@ -134,7 +136,7 @@ def test_commit_sender_403(mocker):
134136
"branch",
135137
"owner::::repo",
136138
token,
137-
"service",
139+
"github",
138140
None,
139141
None,
140142
)
@@ -176,6 +178,33 @@ def test_commit_sender_with_forked_repo(mocker):
176178
)
177179

178180

181+
@pytest.mark.parametrize(
182+
"service,slug,enterprise_url,fragment",
183+
[
184+
(None, "o::::r", None, "Upload service is missing"),
185+
("", "o::::r", None, "Upload service is missing"),
186+
("circleci", "o::::r", None, "Invalid upload service"),
187+
],
188+
)
189+
def test_commit_sender_rejects_invalid_url_parts(
190+
mocker, service, slug, enterprise_url, fragment
191+
):
192+
mocker.patch("codecov_cli.helpers.request.requests.post")
193+
with pytest.raises(click.ClickException) as excinfo:
194+
send_commit_data(
195+
"commit_sha",
196+
"parent_sha",
197+
"pr",
198+
"branch",
199+
slug,
200+
uuid.uuid4(),
201+
service,
202+
enterprise_url,
203+
None,
204+
)
205+
assert fragment in str(excinfo.value)
206+
207+
179208
def test_commit_without_token(mocker):
180209
mocked_response = mocker.patch(
181210
"codecov_cli.services.commit.send_post_request",

codecov-cli/tests/services/empty_upload/test_empty_upload.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_empty_upload_with_warnings(mocker):
2626
"commit_sha",
2727
"owner/repo",
2828
uuid.uuid4(),
29-
"service",
29+
"github",
3030
None,
3131
False,
3232
False,
@@ -62,7 +62,7 @@ def test_empty_upload_with_error(mocker):
6262
"commit_sha",
6363
"owner/repo",
6464
uuid.uuid4(),
65-
"service",
65+
"github",
6666
None,
6767
False,
6868
False,
@@ -93,7 +93,7 @@ def test_empty_upload_200(mocker):
9393
runner = CliRunner()
9494
with runner.isolation() as outstreams:
9595
res = empty_upload_logic(
96-
"commit_sha", "owner/repo", token, "service", None, False, False, None
96+
"commit_sha", "owner/repo", token, "github", None, False, False, None
9797
)
9898
out_bytes = parse_outstreams_into_log_lines(outstreams[0].getvalue())
9999
assert out_bytes == [
@@ -113,7 +113,7 @@ def test_empty_upload_403(mocker):
113113
)
114114
token = uuid.uuid4()
115115
res = empty_upload_logic(
116-
"commit_sha", "owner/repo", token, "service", None, False, False, None
116+
"commit_sha", "owner/repo", token, "github", None, False, False, None
117117
)
118118
assert res.error == RequestError(
119119
code="HTTP Error 403",
@@ -138,7 +138,7 @@ def test_empty_upload_force(mocker):
138138
runner = CliRunner()
139139
with runner.isolation() as outstreams:
140140
res = empty_upload_logic(
141-
"commit_sha", "owner/repo", token, "service", None, False, True, None
141+
"commit_sha", "owner/repo", token, "github", None, False, True, None
142142
)
143143
out_bytes = parse_outstreams_into_log_lines(outstreams[0].getvalue())
144144
assert out_bytes == [
@@ -165,7 +165,7 @@ def test_empty_upload_no_token(mocker):
165165
runner = CliRunner()
166166
with runner.isolation() as outstreams:
167167
res = empty_upload_logic(
168-
"commit_sha", "owner/repo", None, "service", None, False, False, None
168+
"commit_sha", "owner/repo", None, "github", None, False, False, None
169169
)
170170

171171
out_bytes = parse_outstreams_into_log_lines(outstreams[0].getvalue())

codecov-cli/tests/services/report/test_report_results.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_report_results_command_with_warnings(mocker):
2727
res = create_report_results_logic(
2828
commit_sha="commit_sha",
2929
code="code",
30-
service="service",
30+
service="github",
3131
slug="owner/repo",
3232
token="token",
3333
enterprise_url=None,
@@ -46,7 +46,7 @@ def test_report_results_command_with_warnings(mocker):
4646
args=None,
4747
commit_sha="commit_sha",
4848
report_code="code",
49-
service="service",
49+
service="github",
5050
encoded_slug="owner::::repo",
5151
token="token",
5252
enterprise_url=None,
@@ -72,7 +72,7 @@ def test_report_results_command_with_error(mocker):
7272
res = create_report_results_logic(
7373
commit_sha="commit_sha",
7474
code="code",
75-
service="service",
75+
service="github",
7676
slug="owner/repo",
7777
token="token",
7878
enterprise_url=None,
@@ -89,7 +89,7 @@ def test_report_results_command_with_error(mocker):
8989
args=None,
9090
commit_sha="commit_sha",
9191
report_code="code",
92-
service="service",
92+
service="github",
9393
encoded_slug="owner::::repo",
9494
token="token",
9595
enterprise_url=None,
@@ -103,7 +103,7 @@ def test_report_results_request_200(mocker):
103103
)
104104
token = uuid.uuid4()
105105
res = send_reports_result_request(
106-
"commit_sha", "report_code", "encoded_slug", "service", token, None, None
106+
"commit_sha", "report_code", "encoded_slug", "github", token, None, None
107107
)
108108
assert res.error is None
109109
assert res.warnings == []
@@ -116,7 +116,7 @@ def test_report_results_request_no_token(mocker):
116116
return_value=mocker.MagicMock(status_code=200),
117117
)
118118
res = send_reports_result_request(
119-
"commit_sha", "report_code", "encoded_slug", "service", None, None, None
119+
"commit_sha", "report_code", "encoded_slug", "github", None, None, None
120120
)
121121
assert res.error is None
122122
assert res.warnings == []
@@ -130,7 +130,7 @@ def test_report_results_403(mocker):
130130
)
131131
token = uuid.uuid4()
132132
res = send_reports_result_request(
133-
"commit_sha", "report_code", "encoded_slug", "service", token, None, None
133+
"commit_sha", "report_code", "encoded_slug", "github", token, None, None
134134
)
135135
assert res.error == RequestError(
136136
code="HTTP Error 403",
@@ -150,7 +150,7 @@ def test_get_report_results_200_completed(mocker, capsys):
150150
)
151151
token = uuid.uuid4()
152152
res = send_reports_result_get_request(
153-
"commit_sha", "report_code", "encoded_slug", "service", token, None
153+
"commit_sha", "report_code", "encoded_slug", "github", token, None
154154
)
155155
output = parse_outstreams_into_log_lines(capsys.readouterr().err)
156156
assert res.error is None
@@ -171,7 +171,7 @@ def test_get_report_results_no_token(mocker, capsys):
171171
),
172172
)
173173
res = send_reports_result_get_request(
174-
"commit_sha", "report_code", "encoded_slug", "service", None, None
174+
"commit_sha", "report_code", "encoded_slug", "github", None, None
175175
)
176176
assert res.error is None
177177
assert res.warnings == []
@@ -189,7 +189,7 @@ def test_get_report_results_200_pending(mocker, capsys):
189189
)
190190
token = uuid.uuid4()
191191
res = send_reports_result_get_request(
192-
"commit_sha", "report_code", "encoded_slug", "service", token, None
192+
"commit_sha", "report_code", "encoded_slug", "github", token, None
193193
)
194194
output = parse_outstreams_into_log_lines(capsys.readouterr().err)
195195
assert res.error is None
@@ -207,7 +207,7 @@ def test_get_report_results_200_error(mocker, capsys):
207207
)
208208
token = uuid.uuid4()
209209
res = send_reports_result_get_request(
210-
"commit_sha", "report_code", "encoded_slug", "service", token, None
210+
"commit_sha", "report_code", "encoded_slug", "github", token, None
211211
)
212212
output = parse_outstreams_into_log_lines(capsys.readouterr().err)
213213
assert res.error is None
@@ -228,7 +228,7 @@ def test_get_report_results_200_undefined_state(mocker, capsys):
228228
)
229229
token = uuid.uuid4()
230230
res = send_reports_result_get_request(
231-
"commit_sha", "report_code", "encoded_slug", "service", token, None
231+
"commit_sha", "report_code", "encoded_slug", "github", token, None
232232
)
233233
output = parse_outstreams_into_log_lines(capsys.readouterr().err)
234234
assert res.error is None
@@ -246,7 +246,7 @@ def test_get_report_results_401(mocker, capsys):
246246
)
247247
token = uuid.uuid4()
248248
res = send_reports_result_get_request(
249-
"commit_sha", "report_code", "encoded_slug", "service", token, None
249+
"commit_sha", "report_code", "encoded_slug", "github", token, None
250250
)
251251
output = parse_outstreams_into_log_lines(capsys.readouterr().err)
252252
assert res.error == RequestError(

0 commit comments

Comments
 (0)