From 0081bd84303c93d727c3f5cb880ebd17fc15488e Mon Sep 17 00:00:00 2001 From: Shivansh Garg Date: Sat, 17 Oct 2026 16:10:04 +0530 Subject: [PATCH] fix(actions): bound request body size in artifacts v4 twirp handlers (#39818) parseProtobufBody read the full Twirp request body via io.ReadAll with no size limit, allowing an authenticated runner token holder to force unbounded memory allocation. Cap it at 1 MiB via io.LimitReader, matching the existing pattern in job_summary.go. Fixes https://github.com/go-gitea/gitea/issues/39814 --- routers/api/actions/artifactsv4.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index a7befae91d8..93c892573d7 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -290,13 +290,22 @@ func (r *artifactV4Routes) findArtifactByName(ctx *ArtifactContext, runID int64, return &art, nil } +// maxProtobufBodySize caps the Twirp request body size accepted by parseProtobufBody. +// These requests only ever carry a handful of small scalar fields (run IDs, artifact +// names, etc.), so this is far above any legitimate payload while still bounding memory use. +const maxProtobufBodySize = 1024 * 1024 // 1 MiB + func (r *artifactV4Routes) parseProtobufBody(ctx *ArtifactContext, req protoreflect.ProtoMessage) bool { - body, err := io.ReadAll(ctx.Req.Body) + body, err := io.ReadAll(io.LimitReader(ctx.Req.Body, maxProtobufBodySize+1)) if err != nil { log.Error("Error decode request body: %v", err) ctx.HTTPError(http.StatusInternalServerError, "Error decode request body") return false } + if len(body) > maxProtobufBodySize { + ctx.HTTPError(http.StatusRequestEntityTooLarge, "request body too large") + return false + } err = protojson.Unmarshal(body, req) if err != nil { log.Error("Error decode request body: %v", err)