diff --git a/mock_ssh_test.go b/mock_ssh_test.go index 4f29323..863489a 100644 --- a/mock_ssh_test.go +++ b/mock_ssh_test.go @@ -526,6 +526,7 @@ type sshRunner interface { Run(ctx context.Context, cmd string) (string, string, int, error) RunScript(ctx context.Context, script string) (string, string, int, error) Upload(ctx context.Context, local io.Reader, remote string, mode fs.FileMode) error + Download(ctx context.Context, remote string) ([]byte, error) } type sshFileTransferRunner interface { @@ -2011,6 +2012,7 @@ func moduleURIWithClient(_ *Executor, client sshRunner, args map[string]any) (*T forceBasicAuth := getBoolArg(args, "force_basic_auth", false) unixSocket := getStringArg(args, "unix_socket", "") followRedirects := lower(getStringArg(args, "follow_redirects", "safe")) + src := getStringArg(args, "src", "") if url == "" { return nil, mockError("moduleURIWithClient", "uri: url required") @@ -2047,7 +2049,13 @@ func moduleURIWithClient(_ *Executor, client sshRunner, args map[string]any) (*T curlOpts = appendURIFollowRedirects(curlOpts, method, followRedirects) // Body - if body := args["body"]; body != nil { + if src != "" { + bodyBytes, err := client.Download(context.Background(), src) + if err != nil { + return nil, mockWrap("moduleURIWithClient", "download src", err) + } + curlOpts = append(curlOpts, "-d", sprintf("%q", string(bodyBytes))) + } else if body := args["body"]; body != nil { bodyText, err := renderURIBody(body, bodyFormat) if err != nil { return nil, mockWrap("moduleURIWithClient", "render body", err) diff --git a/modules.go b/modules.go index d13edf3..13604ff 100644 --- a/modules.go +++ b/modules.go @@ -2022,6 +2022,7 @@ func (e *Executor) moduleURI(ctx context.Context, client sshExecutorClient, args forceBasicAuth := getBoolArg(args, "force_basic_auth", false) unixSocket := getStringArg(args, "unix_socket", "") followRedirects := lower(getStringArg(args, "follow_redirects", "safe")) + src := getStringArg(args, "src", "") if url == "" { return nil, coreerr.E("Executor.moduleURI", "url required", nil) @@ -2060,7 +2061,13 @@ func (e *Executor) moduleURI(ctx context.Context, client sshExecutorClient, args curlOpts = appendURIFollowRedirects(curlOpts, method, followRedirects) // Body - if body := args["body"]; body != nil { + if src != "" { + bodyBytes, err := client.Download(ctx, src) + if err != nil { + return nil, coreerr.E("Executor.moduleURI", "download src", err) + } + curlOpts = append(curlOpts, "-d", sprintf("%q", string(bodyBytes))) + } else if body := args["body"]; body != nil { bodyText, err := renderURIBody(body, bodyFormat) if err != nil { return nil, coreerr.E("Executor.moduleURI", "render body", err) diff --git a/modules_adv_test.go b/modules_adv_test.go index b776a88..820edfe 100644 --- a/modules_adv_test.go +++ b/modules_adv_test.go @@ -1689,6 +1689,26 @@ func TestModulesAdv_ModuleURI_Good_UsesUnixSocket(t *testing.T) { assert.True(t, mock.hasExecuted(`--unix-socket '/var/run/docker.sock'`)) } +func TestModulesAdv_ModuleURI_Good_UsesSourceFileBody(t *testing.T) { + e, mock := newTestExecutorWithMock("host1") + require.NoError(t, mock.Upload(context.Background(), newReader("alpha=1&beta=2"), "/tmp/request-body.txt", 0644)) + mock.expectCommand(`curl.*form\.example\.com`, "created\n201", "", 0) + + result, err := moduleURIWithClient(e, mock, map[string]any{ + "url": "https://form.example.com/submit", + "method": "POST", + "src": "/tmp/request-body.txt", + "body_format": "json", + "status_code": 201, + }) + + require.NoError(t, err) + assert.False(t, result.Failed) + assert.Equal(t, 201, result.RC) + assert.True(t, mock.containsSubstring(`-d "alpha=1&beta=2"`)) + assert.False(t, mock.containsSubstring("Content-Type: application/json")) +} + func TestModulesAdv_ModuleURI_Good_FormURLEncodedBody(t *testing.T) { e, mock := newTestExecutorWithMock("host1") mock.expectCommand(`curl.*form\.example\.com`, "created\n201", "", 0)