Skip to content

Commit 154de31

Browse files
authored
fix(tasks): reject client-supplied signals_scout task origin
origin_product was API-settable to signals_scout (only image_builder was rejected), so any task:write caller could forge a scout task whose appended run logs would be mirrored into PostHog's internal Logs project once the mirror ships - flagged by the security review bots on the PR. Scout tasks are created exclusively server-side by the signals scout harness via the tasks facade, so treat signals_scout like image_builder: an internal-only origin the public serializer rejects. With creation server-attested, mirroring_enabled() can keep gating on origin_product. The mirror is new in this PR, so no forged tasks can predate the check where it matters. Test: parameterized serializer-level SimpleTestCase over the internal-only origins (verified to fail without the fix); the existing image_builder endpoint test remains the wiring guard. Generated-By: PostHog Code Task-Id: 0c511836-2180-455a-9b58-45df2a0661ec
1 parent 63d88b5 commit 154de31

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

products/tasks/backend/presentation/serializers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,11 @@ def validate_origin_product(self, value):
595595
"""Reject internal-only origins that are set by server-side flows, never by API callers."""
596596
if value == tasks_facade.TaskOriginProduct.IMAGE_BUILDER:
597597
raise serializers.ValidationError("origin_product 'image_builder' is reserved for image-builder sessions")
598+
if value == tasks_facade.TaskOriginProduct.SIGNALS_SCOUT:
599+
# Scout tasks are created only by the signals scout harness. A forged scout origin
600+
# would route the task's run logs into PostHog's internal Logs project
601+
# (run_log_mirror) and inherit scout visibility semantics.
602+
raise serializers.ValidationError("origin_product 'signals_scout' is reserved for signals scout runs")
598603
return value
599604

600605
def validate_repository(self, value):

products/tasks/backend/tests/test_presentation_serializers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,24 @@
99
from products.tasks.backend.presentation.serializers import (
1010
TaskRunCreateRequestSerializer,
1111
TaskRunLivingArtifactCreateRequestSerializer,
12+
TaskWriteSerializer,
1213
)
1314

1415

16+
class TestTaskWriteSerializerOriginProduct(SimpleTestCase):
17+
@parameterized.expand(
18+
[
19+
("image_builder", True),
20+
("signals_scout", True),
21+
("user_created", False),
22+
]
23+
)
24+
def test_internal_only_origins_are_rejected(self, origin_product: str, expected_rejected: bool) -> None:
25+
serializer = TaskWriteSerializer(data={"origin_product": origin_product})
26+
serializer.is_valid()
27+
assert ("origin_product" in serializer.errors) is expected_rejected
28+
29+
1530
class TestTaskRunLivingArtifactCreateRequestSerializer(SimpleTestCase):
1631
@parameterized.expand(
1732
[

0 commit comments

Comments
 (0)