Skip to content

Commit 8708171

Browse files
feat: auto-generate upload dest path, add upload confirmation check
- --upload-dest is now optional; defaults to C:\Windows\Temp\<UUID> (same pattern as --out remote path generation) - After upload, confirm file exists via SMB stat and log path + size - Add --no-upload-confirm flag to skip the confirmation check - Add UploadConfirmer optional interface in io.go - Add ConfirmUpload method on FileStager (stats remote file, logs details) Co-authored-by: Carter <carter-falconops@users.noreply.github.com>
1 parent e16a230 commit 8708171

5 files changed

Lines changed: 43 additions & 5 deletions

File tree

cmd/args.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ func registerExecutionFlags(fs *pflag.FlagSet) {
5151

5252
func registerExecutionUploadFlags(fs *pflag.FlagSet) {
5353
fs.StringVar(&uploadSource, "upload", "", "Upload local `file` to remote filesystem")
54-
fs.StringVar(&uploadDest, "upload-dest", "", "Remote destination `path` for uploaded file")
54+
fs.StringVar(&uploadDest, "upload-dest", "", "Remote destination `path` for uploaded file (default: random temp path)")
55+
fs.BoolVar(&exec.Upload.NoConfirm, "no-upload-confirm", false, "Skip upload confirmation check")
5556
}
5657

5758
func registerExecutionOutputFlags(fs *pflag.FlagSet) {
@@ -185,9 +186,6 @@ func argsUpload(methods ...string) func(cmd *cobra.Command, args []string) error
185186
return args(append(as, func(*cobra.Command, []string) (err error) {
186187

187188
if uploadSource != "" {
188-
if uploadDest == "" {
189-
return fmt.Errorf("--upload-dest is required when --upload is set")
190-
}
191189
exec.Upload.Reader, err = os.Open(uploadSource)
192190
if err != nil {
193191
return fmt.Errorf("open upload file: %w", err)

cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ Authors: FalconOps LLC (@FalconOpsLLC),
185185
}
186186

187187
if uploadSource != "" {
188+
if uploadDest == "" {
189+
uploadDest = `C:\Windows\Temp\` + uuid.NewString()
190+
}
188191
exec.Upload.RemotePath = uploadDest
189192
exec.Upload.Provider = &smb.FileStager{
190193
Client: &smbClient,

pkg/goexec/io.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,18 @@ type ExecutionOutput struct {
3535
}
3636

3737
type ExecutionUpload struct {
38+
NoConfirm bool
3839
RemotePath string
3940
Provider InputProvider
4041
Reader io.ReadCloser
4142
}
4243

44+
// UploadConfirmer is an optional interface that InputProvider implementations
45+
// can satisfy to confirm a file was successfully uploaded.
46+
type UploadConfirmer interface {
47+
ConfirmUpload(ctx context.Context) error
48+
}
49+
4350
type ExecutionInput struct {
4451
StageFile io.ReadCloser
4552
Executable string

pkg/goexec/method.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,13 @@ func ExecuteCleanMethod(ctx context.Context, module CleanExecutionMethod, execIO
124124
log.Error().Err(err).Msg("Upload failed")
125125
return fmt.Errorf("upload: %w", err)
126126
}
127-
log.Info().Msg("Upload succeeded")
127+
if !execIO.Upload.NoConfirm {
128+
if confirmer, ok := execIO.Upload.Provider.(UploadConfirmer); ok {
129+
if err = confirmer.ConfirmUpload(ctx); err != nil {
130+
log.Warn().Err(err).Msg("Upload confirmation failed")
131+
}
132+
}
133+
}
128134
}
129135

130136
// Execute (only if a command/executable was provided)

pkg/goexec/smb/input.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/FalconOpsLLC/goexec/pkg/goexec"
12+
"github.com/rs/zerolog"
1213
)
1314

1415
type FileStager struct {
@@ -61,3 +62,26 @@ func (o *FileStager) Upload(ctx context.Context, reader io.Reader) (err error) {
6162

6263
return
6364
}
65+
66+
// ConfirmUpload checks that the uploaded file exists on the remote filesystem
67+
// and logs the file path and size. The share must already be mounted from a
68+
// prior Upload call.
69+
func (o *FileStager) ConfirmUpload(ctx context.Context) error {
70+
log := zerolog.Ctx(ctx)
71+
72+
if o.Client.mount == nil {
73+
return fmt.Errorf("share not mounted")
74+
}
75+
76+
info, err := o.Client.mount.Stat(o.relativePath)
77+
if err != nil {
78+
return fmt.Errorf("stat remote file: %w", err)
79+
}
80+
81+
log.Info().
82+
Str("path", o.File).
83+
Int64("size", info.Size()).
84+
Msg("Upload confirmed")
85+
86+
return nil
87+
}

0 commit comments

Comments
 (0)