feat: auto-remove uploaded file after execution (upload+execute mode)

When --upload is combined with --exec/--command, the uploaded file is
automatically deleted from the remote filesystem after execution completes.
This supports the upload-execute-cleanup workflow (e.g., upload a.bat,
execute cmd.exe /c a.bat, then delete a.bat — safe because cmd.exe
already loaded the script).

Upload-only mode (--upload without --exec) does NOT delete the file.

- Add UploadRemover optional interface in io.go
- Add RemoveUploadedFile method on FileStager (deletes via SMB)
- In ExecuteCleanMethod, call RemoveUploadedFile after execution if both
  upload and execution were performed

Co-authored-by: Carter <carter-falconops@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-03-04 17:02:33 +00:00
parent a3e1159527
commit 28e24e3cbe
3 changed files with 34 additions and 0 deletions
+6
View File
@@ -47,6 +47,12 @@ type UploadConfirmer interface {
ConfirmUpload(ctx context.Context) error
}
// UploadRemover is an optional interface that InputProvider implementations
// can satisfy to remove a previously uploaded file from the remote filesystem.
type UploadRemover interface {
RemoveUploadedFile(ctx context.Context) error
}
type ExecutionInput struct {
StageFile io.ReadCloser
Executable string
+11
View File
@@ -138,11 +138,22 @@ func ExecuteCleanMethod(ctx context.Context, module CleanExecutionMethod, execIO
}
// Execute (only if a command/executable was provided)
executed := false
if execIO.Input != nil && (execIO.Input.Executable != "" || execIO.Input.Command != "" || execIO.Input.ExecutablePath != "") {
if err = module.Execute(ctx, execIO); err != nil {
log.Error().Err(err).Msg("Execution failed")
return fmt.Errorf("execute: %w", err)
}
executed = true
}
// Remove uploaded file after execution (upload+execute mode only)
if executed && execIO.Upload != nil && execIO.Upload.Provider != nil {
if remover, ok := execIO.Upload.Provider.(UploadRemover); ok {
if removeErr := remover.RemoveUploadedFile(ctx); removeErr != nil {
log.Warn().Err(removeErr).Msg("Failed to remove uploaded file")
}
}
}
// Module cleanup
+17
View File
@@ -63,6 +63,23 @@ func (o *FileStager) Upload(ctx context.Context, reader io.Reader) (err error) {
return
}
// RemoveUploadedFile deletes the uploaded file from the remote filesystem.
// The share must already be mounted from a prior Upload call.
func (o *FileStager) RemoveUploadedFile(ctx context.Context) error {
log := zerolog.Ctx(ctx)
if o.Client.mount == nil {
return fmt.Errorf("share not mounted")
}
if err := o.Client.mount.Remove(o.relativePath); err != nil {
return fmt.Errorf("remove remote file: %w", err)
}
log.Info().Str("path", o.File).Msg("Removed uploaded file")
return nil
}
// ConfirmUpload checks that the uploaded file exists on the remote filesystem
// and logs the file path and size. The share must already be mounted from a
// prior Upload call.