Skip to content

Commit 48cb071

Browse files
runningcodeclaude
andcommitted
fix(preprod): Validate error_code range and handle non-dict JSON
Use parse_request_with_pydantic (which uses parse_obj_as) instead of manual unpacking to properly handle non-dict JSON bodies. Add Field constraint to error_code (0-3) matching the InstallableAppErrorCode enum range. Also make the shared helper's error message generic. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f225296 commit 48cb071

3 files changed

Lines changed: 17 additions & 16 deletions

File tree

src/sentry/preprod/api/endpoints/project_preprod_distribution.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from __future__ import annotations
22

33
import logging
4+
from typing import Any, cast
45

5-
import orjson
6-
import pydantic
7-
from pydantic import BaseModel
8-
from rest_framework import serializers
6+
from pydantic import BaseModel, Field
97
from rest_framework.request import Request
108
from rest_framework.response import Response
119

@@ -14,6 +12,7 @@
1412
from sentry.api.base import internal_region_silo_endpoint
1513
from sentry.models.project import Project
1614
from sentry.preprod.api.bases.preprod_artifact_endpoint import PreprodArtifactEndpoint
15+
from sentry.preprod.api.endpoints.project_preprod_size import parse_request_with_pydantic
1716
from sentry.preprod.authentication import (
1817
LaunchpadRpcPermission,
1918
LaunchpadRpcSignatureAuthentication,
@@ -24,7 +23,7 @@
2423

2524

2625
class PutDistribution(BaseModel):
27-
error_code: int
26+
error_code: int = Field(ge=0, le=3)
2827
error_message: str
2928

3029

@@ -44,15 +43,7 @@ def put(
4443
head_artifact_id: int,
4544
head_artifact: PreprodArtifact,
4645
) -> Response:
47-
try:
48-
j = orjson.loads(request.body)
49-
except orjson.JSONDecodeError:
50-
raise serializers.ValidationError("Invalid json")
51-
try:
52-
put = PutDistribution(**j)
53-
except pydantic.ValidationError:
54-
logger.exception("Could not parse PutDistribution")
55-
raise serializers.ValidationError("Could not parse PutDistribution")
46+
put: PutDistribution = parse_request_with_pydantic(request, cast(Any, PutDistribution))
5647

5748
head_artifact.installable_app_error_code = put.error_code
5849
head_artifact.installable_app_error_message = put.error_message

src/sentry/preprod/api/endpoints/project_preprod_size.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def parse_request_with_pydantic(request: Request, model: type[T]) -> T:
3939
# can be used instead of parse_obj_as
4040
return parse_obj_as(model, j)
4141
except pydantic.ValidationError:
42-
logger.exception("Could not parse PutSize")
43-
raise serializers.ValidationError("Could not parse PutSize")
42+
logger.exception("Could not parse %s", model.__name__)
43+
raise serializers.ValidationError(f"Could not parse {model.__name__}")
4444

4545

4646
@internal_region_silo_endpoint

tests/sentry/preprod/api/endpoints/test_project_preprod_distribution.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ def test_set_error(self) -> None:
5757
)
5858
assert self.artifact.installable_app_error_message == "Unsupported artifact type"
5959

60+
@override_settings(LAUNCHPAD_RPC_SHARED_SECRET=[SHARED_SECRET_FOR_TESTS])
61+
def test_invalid_error_code(self) -> None:
62+
response = self._put(orjson.dumps({"error_code": 99, "error_message": "bad"}))
63+
assert response.status_code == 400
64+
65+
@override_settings(LAUNCHPAD_RPC_SHARED_SECRET=[SHARED_SECRET_FOR_TESTS])
66+
def test_non_dict_json_body(self) -> None:
67+
response = self._put(orjson.dumps([1, 2, 3]))
68+
assert response.status_code == 400
69+
6070
@override_settings(LAUNCHPAD_RPC_SHARED_SECRET=[SHARED_SECRET_FOR_TESTS])
6171
def test_requires_launchpad_rpc_authentication(self) -> None:
6272
self.login_as(self.user)

0 commit comments

Comments
 (0)