Files
osmedeus/cmd/cloud.go
T

185 lines
7.0 KiB
Go

package cmd
import (
"fmt"
"io"
"os"
"path"
"github.com/fatih/color"
"github.com/j3ssie/osmedeus/core"
"github.com/j3ssie/osmedeus/distribute"
"github.com/j3ssie/osmedeus/libs"
"github.com/j3ssie/osmedeus/utils"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func init() {
var cloudCmd = &cobra.Command{
Use: "cloud",
Short: "Perform a scan using the distributed cloud mode",
Long: core.Banner(),
RunE: runCloud,
}
// core options
cloudCmd.Flags().StringVarP(&options.Cloud.Module, "module", "m", "", "module name for running")
cloudCmd.Flags().StringVarP(&options.Cloud.Flow, "flow", "f", "general", "Flow name for running (default: general)")
cloudCmd.Flags().StringVarP(&options.Cloud.Workspace, "workspace", "w", "", "Name of workspace (default is same as target)")
cloudCmd.Flags().StringSliceVarP(&options.Cloud.Params, "params", "p", []string{}, "Custom params -p='foo=bar' (Multiple -p flags are accepted)")
// chunk inputs
cloudCmd.Flags().BoolVar(&options.Cloud.EnablePrivateIP, "privateIP", false, "Enable Private IP")
cloudCmd.Flags().BoolVar(&options.Cloud.TargetAsFile, "as-file", false, "Run target as file (use -T targets.txt file instead of -t targets.txt at cloud instance)")
cloudCmd.Flags().StringVar(&options.Cloud.LocalSyncFolder, "rfolder", "", "Remote Folder to sync back to local")
// commands on cloud
cloudCmd.Flags().IntVar(&options.Cloud.Threads, "cloud-threads", 1, "Concurrency level on remote cloud")
cloudCmd.Flags().StringVar(&options.Cloud.Extra, "extra", "", "append raw command after the command builder")
cloudCmd.Flags().StringVar(&options.Cloud.RawCommand, "cmd", "", "specific raw command and override everything (eg: --cmd 'curl {{Target}}')")
cloudCmd.Flags().StringVar(&options.Cloud.ClearTime, "clear", "10m", "time to wait before next clear check")
cloudCmd.Flags().StringVar(&options.Cloud.TempTarget, "tempTargets", "/tmp/osm-tmp-inputs/", "Temp Folder to store targets file")
// mics option
cloudCmd.Flags().BoolVar(&options.Cloud.EnableSyncWorkflow, "sync-workflow", false, "Enable Sync Workflow folder to remote machine first before starting the scan")
cloudCmd.Flags().BoolVarP(&options.Cloud.CopyWorkspaceToGit, "gws", "G", false, "Enable Copy Workspace to Git (run -f sync after done)")
cloudCmd.Flags().BoolVarP(&options.Cloud.DisableLocalSync, "no-lsync", "z", false, "Disable sync back data to local machine")
cloudCmd.Flags().BoolVar(&options.Cloud.BackgroundRun, "bg", false, "Send command to instance without checking if process is done or not")
cloudCmd.Flags().BoolVar(&options.Cloud.EnableTerraform, "tf", false, "Use terraform to create cloud instance")
cloudCmd.Flags().BoolVar(&options.Cloud.NoDelete, "no-del", false, "Don't delete instance after done (you can run 'osmedeus provider health' to clean up later)")
cloudCmd.Flags().BoolVar(&options.Cloud.IgnoreProcess, "no-ps", false, "Disable checking process on remote machine")
cloudCmd.Flags().IntVar(&options.Cloud.Retry, "retry", 10, "Number of retry when command is error")
cloudCmd.SetHelpFunc(CloudHelp)
RootCmd.AddCommand(cloudCmd)
cloudCmd.PreRun = func(cmd *cobra.Command, args []string) {
if options.FullHelp {
cmd.Help()
os.Exit(0)
}
}
}
func runCloud(cmd *cobra.Command, _ []string) error {
utils.GoodF("Launching %v Engine %v developed by %v", cases.Title(language.Und, cases.NoLower).String(libs.BINARY), color.HiCyanString(libs.VERSION), color.HiMagentaString(libs.AUTHOR))
utils.InforF("Storing the log file to: %v", color.CyanString(options.LogFile))
// parse some arguments
threads, _ := cmd.Flags().GetInt("thread")
if threads > 1 || options.Cloud.Threads <= 1 {
options.Cloud.Threads = threads
}
// get pre run commands
getPreRun(&options)
// change targets list if chunk mode enable
if options.Cloud.EnableChunk {
utils.InforF("Running cloud scan in chunk mode")
for _, target := range options.Scan.Inputs {
chunkTargets := HandleChunksInputs(target)
if len(chunkTargets) == 0 {
continue
}
distribute.InitCloud(options, chunkTargets)
// remove chunk inputs
utils.DebugF("Remove chunk inputs file")
for _, ctarget := range chunkTargets {
os.RemoveAll(ctarget)
}
}
return nil
}
// @NOTE: pro-tips
if options.Concurrency > 1 && len(options.Scan.Inputs) == 1 {
if !utils.FileExists(options.Scan.Inputs[0]) {
utils.WarnF("You're using %v in cloud scan but your input %v is just a single domain", color.HiMagentaString(`'-c %v'`, options.Concurrency), color.HiMagentaString(`'-t %v'`, options.Scan.Inputs[0]))
utils.WarnF("Consider running: osmedeus cloud -c 5 -T list-of-targets.txt")
}
}
distribute.InitCloud(options, options.Scan.Inputs)
return nil
}
// HandleChunksInputs split the inputs to multiple file first
func HandleChunksInputs(target string) []string {
var chunkTargets []string
utils.MakeDir(options.Cloud.ChunkInputs)
if !utils.FileExists(target) {
utils.ErrorF("error to split input file: %v", target)
return chunkTargets
}
if options.Cloud.NumberOfParts == 0 {
options.Cloud.NumberOfParts = options.Concurrency
}
utils.DebugF("Splitting %v to %v part", target, options.Cloud.NumberOfParts)
rawChunks, err := utils.SplitLineChunks(target, options.Cloud.NumberOfParts)
if err != nil || len(rawChunks) == 0 {
utils.ErrorF("error to split input file: %v", target)
return chunkTargets
}
fp, err := os.Open(target)
if err != nil {
utils.ErrorF("error to open input file: %v", target)
return chunkTargets
}
for index, offset := range rawChunks {
targetName := fmt.Sprintf("%s-chunk-%v", utils.CleanPath(target), index)
targetName = path.Join(options.Cloud.ChunkInputs, targetName)
sectionReader := io.NewSectionReader(fp, offset.Start, offset.Stop-offset.Start)
targetFile, err := os.OpenFile(targetName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
utils.ErrorF("error when create chunk file: %v", target)
continue
}
_, err = io.Copy(targetFile, sectionReader)
if err != nil {
utils.ErrorF("error to read chunk file: %s", err)
continue
}
targetFile.Close()
chunkTargets = append(chunkTargets, targetName)
}
return chunkTargets
}
func getPreRun(options *libs.Options) {
if options.Cloud.Module != "" {
module := core.DirectSelectModule(*options, options.Cloud.Module)
if module == "" {
utils.ErrorF("Error to select module: %s", options.Cloud.Module)
return
}
parsedModule, err := core.ParseModules(module)
if err == nil {
options.Cloud.RemotePreRun = parsedModule.RemotePreRun
options.Cloud.LocalPostRun = parsedModule.LocalPostRun
options.Cloud.LocalPreRun = parsedModule.LocalPreRun
options.Cloud.LocalSteps = parsedModule.LocalSteps
}
return
}
if options.Cloud.Flow != "" {
flows := core.SelectFlow(options.Cloud.Flow, *options)
for _, flow := range flows {
parseFlow, err := core.ParseFlow(flow)
if err == nil {
options.Cloud.RemotePreRun = parseFlow.RemotePreRun
options.Cloud.LocalPostRun = parseFlow.LocalPostRun
options.Cloud.LocalPreRun = parseFlow.LocalPreRun
}
}
}
}