mirror of
https://github.com/vxcontrol/pentagi.git
synced 2026-09-25 19:55:44 +00:00
test(docker,flows): cover the listing cap paths, bound the container-list path count
The truncation slice and the demux stdout byte-cap had no docker-layer tests — only the handler's Truncated wiring was exercised through the fake, so a mis-slice or a dropped cap would have gone unnoticed. Extract find-output parsing into a pure parseFindEntries and take the byte cap as a demuxExecStdout parameter, then unit-test both boundaries (at cap / cap+1 / over-limit stream). Also bound how many paths one container-files request may list (maxContainerListPaths), so the per-path entry cap can't be multiplied by an attacker-chosen path count into a large fan-out or response body. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
18234f05a3
commit
7cd22ccbc9
@@ -38,6 +38,11 @@ import (
|
||||
// resources/ ← user resources copied from user_resources table; also pushed to container /work/resources/
|
||||
// container/ ← files synced from the container via pull; never sent back to container
|
||||
|
||||
// maxContainerListPaths bounds how many directory paths one container-files
|
||||
// request may list, so the per-path entry cap can't be multiplied into an
|
||||
// unbounded fan-out or response.
|
||||
const maxContainerListPaths = 128
|
||||
|
||||
type pendingUpload struct {
|
||||
fileName string
|
||||
dstPath string
|
||||
@@ -940,6 +945,14 @@ func (s *FlowFileService) GetFlowContainerFiles(c *gin.Context) {
|
||||
response.Error(c, response.ErrFlowFilesInvalidRequest, err)
|
||||
return
|
||||
}
|
||||
// Bound the number of paths per request so the per-path entry cap can't be
|
||||
// multiplied by an attacker-chosen path count into a huge fan-out / response.
|
||||
if len(containerPaths) > maxContainerListPaths {
|
||||
err = fmt.Errorf("too many paths requested (%d, limit %d)", len(containerPaths), maxContainerListPaths)
|
||||
logger.FromContext(c).WithError(err).WithField("flow_id", flowID).Error("too many container paths")
|
||||
response.Error(c, response.ErrFlowFilesInvalidRequest, err)
|
||||
return
|
||||
}
|
||||
if len(containerPaths) == 0 {
|
||||
containerPaths = []string{docker.WorkFolderPathInContainer}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"pentagi/pkg/docker"
|
||||
@@ -132,3 +134,29 @@ func TestGetFlowContainerFiles_TruncatedFlagSurfaced(t *testing.T) {
|
||||
require.Equal(t, http.StatusOK, *code)
|
||||
assert.True(t, resp.Truncated)
|
||||
}
|
||||
|
||||
// The per-request path count is bounded so the per-path entry cap can't be
|
||||
// multiplied by an attacker-chosen number of paths.
|
||||
func TestGetFlowContainerFiles_TooManyPathsRejected(t *testing.T) {
|
||||
buildQuery := func(n int) string {
|
||||
parts := make([]string, n)
|
||||
for i := range parts {
|
||||
parts[i] = "paths[]=/p" + strconv.Itoa(i)
|
||||
}
|
||||
return strings.Join(parts, "&")
|
||||
}
|
||||
|
||||
// One over the cap → 400 before any docker call.
|
||||
db := setupFlowFileServiceTestDB(t)
|
||||
seedFlow(t, db, 1, 1)
|
||||
svc := NewFlowFileService(db, t.TempDir(), &fakeDockerClient{running: true}, nil)
|
||||
c, w := newFlowFileTestContext(http.MethodGet,
|
||||
"/flows/1/files/container?"+buildQuery(maxContainerListPaths+1), nil,
|
||||
[]string{"flow_files.view", "containers.view"}, 1, 1)
|
||||
svc.GetFlowContainerFiles(c)
|
||||
require.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
// Exactly at the cap is allowed.
|
||||
code, _ := listContainerFiles(t, &fakeDockerClient{running: true}, buildQuery(maxContainerListPaths))
|
||||
require.Equal(t, http.StatusOK, *code)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user