Пример #1
0
func TestMaxDepth(t *testing.T) {
	var (
		r run
		s search
	)
	var expectedfiles = []string{
		basedir + "/" + TESTDATA[0].name,
	}
	r.Parameters = *newParameters()
	s.Paths = append(s.Paths, basedir)
	s.Names = append(s.Names, "^"+TESTDATA[0].name+"$")
	s.Contents = append(s.Contents, TESTDATA[0].content)
	s.Options.MatchAll = true
	s.Options.MaxDepth = 5
	r.Parameters.Searches["s1"] = s
	msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("%s\n", msg)
	out := r.Run(bytes.NewBuffer(msg))
	if len(out) == 0 {
		t.Fatal("run failed")
	}
	t.Log(out)
	if evalResults([]byte(out), expectedfiles) != nil {
		t.Fatal(err)
	}
}
Пример #2
0
func TestContentSearch(t *testing.T) {
	for _, tp := range TESTDATA {
		var (
			r run
			s search
		)
		var expectedfiles = []string{
			basedir + "/" + tp.name,
			basedir + subdirs + tp.name,
		}
		r.Parameters = *newParameters()
		s.Paths = append(s.Paths, basedir)
		s.Contents = append(s.Contents, tp.content)
		s.Contents = append(s.Contents, "!^FOOBAR$")
		s.Options.MatchAll = true
		r.Parameters.Searches["s1"] = s
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		t.Logf("%s\n", msg)
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = evalResults([]byte(out), expectedfiles)
		if err != nil {
			t.Fatal(err)
		}
	}
}
Пример #3
0
func TestAllHashes(t *testing.T) {
	for _, tp := range TESTDATA {
		var (
			r run
			s search
		)
		var expectedfiles = []string{
			basedir + "/" + tp.name,
			basedir + subdirs + tp.name,
		}
		r.Parameters = *newParameters()
		s.Paths = append(s.Paths, basedir)
		s.MD5 = append(s.MD5, tp.md5)
		s.SHA1 = append(s.SHA1, tp.sha1)
		s.SHA2 = append(s.SHA2, tp.sha2)
		s.SHA3 = append(s.SHA3, tp.sha3)
		s.Options.MatchAll = true
		r.Parameters.Searches["s1"] = s
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters, false)
		if err != nil {
			t.Fatal(err)
		}
		t.Logf("%s\n", msg)
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = evalResults([]byte(out), expectedfiles)
		if err != nil {
			t.Fatal(err)
		}
	}
}
Пример #4
0
func TestMacroal(t *testing.T) {
	var MacroalTestCases = []macroaltest{
		macroaltest{
			desc:    "want testfile0 with all lines matching '^(.+)?$', should find 2 files",
			name:    "^" + TESTDATA[0].name + "$",
			content: "^(.+)?$",
			expectedfiles: []string{
				basedir + "/" + TESTDATA[0].name,
				basedir + subdirs + TESTDATA[0].name},
		},
		macroaltest{
			desc:          "want testfile0 with no line matching '^(.+)?$', should find 0 file",
			name:          "^" + TESTDATA[0].name + "$",
			content:       "!^(.+)?$",
			expectedfiles: []string{""},
		},
		macroaltest{
			desc:    "want testfile0 with no line matching '!FOOBAR', should find 2 files",
			name:    "^" + TESTDATA[0].name + "$",
			content: "!FOOBAR",
			expectedfiles: []string{
				basedir + "/" + TESTDATA[0].name,
				basedir + subdirs + TESTDATA[0].name},
		},
		macroaltest{
			desc:          "want testfile0 with all lines matching 'FOOBAR', should find 0 file",
			name:          "^" + TESTDATA[0].name + "$",
			content:       "FOOBAR",
			expectedfiles: []string{""},
		},
	}
	for _, mt := range MacroalTestCases {
		t.Log(mt.desc)
		var r run
		var s search
		r.Parameters = *newParameters()
		s.Paths = append(s.Paths, basedir)
		s.Names = append(s.Names, mt.name)
		s.Contents = append(s.Contents, mt.content)
		s.Options.MatchAll = true
		s.Options.Macroal = true
		r.Parameters.Searches["s1"] = s
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		t.Logf("%s\n", msg)
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = evalResults([]byte(out), mt.expectedfiles)
		if err != nil {
			t.Fatal(err)
		}
	}
}
Пример #5
0
func TestHashes(t *testing.T) {
	for _, hashtype := range []string{`md5`, `sha1`, `sha256`, `sha384`, `sha512`,
		`sha3_224`, `sha3_256`, `sha3_384`, `sha3_512`} {
		for _, tp := range TESTDATA {
			var (
				r run
				s search
			)
			var expectedfiles = []string{
				basedir + "/" + tp.name,
				basedir + subdirs + tp.name,
			}
			r.Parameters = *newParameters()
			s.Paths = append(s.Paths, basedir)
			switch hashtype {
			case `md5`:
				s.MD5 = append(s.MD5, tp.md5)
			case `sha1`:
				s.SHA1 = append(s.SHA1, tp.sha1)
			case `sha256`:
				s.SHA256 = append(s.SHA256, tp.sha256)
			case `sha384`:
				s.SHA384 = append(s.SHA384, tp.sha384)
			case `sha512`:
				s.SHA512 = append(s.SHA512, tp.sha512)
			case `sha3_224`:
				s.SHA3_224 = append(s.SHA3_224, tp.sha3_224)
			case `sha3_256`:
				s.SHA3_256 = append(s.SHA3_256, tp.sha3_256)
			case `sha3_384`:
				s.SHA3_384 = append(s.SHA3_384, tp.sha3_384)
			case `sha3_512`:
				s.SHA3_512 = append(s.SHA3_512, tp.sha3_512)
			}
			r.Parameters.Searches["s1"] = s
			msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
			if err != nil {
				t.Fatal(err)
			}
			t.Logf("%s\n", msg)
			out := r.Run(bytes.NewBuffer(msg))
			if len(out) == 0 {
				t.Fatal("run failed")
			}
			t.Log(out)
			err = evalResults([]byte(out), expectedfiles)
			if err != nil {
				t.Fatal(err)
			}
		}
	}
}
Пример #6
0
func TestFindGoTestProcess(t *testing.T) {
	var (
		r run
		s search
	)
	r.Parameters = *newParameters()
	s.Names = append(s.Names, "go-build")
	s.Bytes = append(s.Bytes, "7465737420736561726368206c6f6f6b696e6720666f722073656c66")
	s.Contents = append(s.Contents, "test search looking for self")
	s.Options.MatchAll = true
	s.Options.Offset = 0.0
	s.Options.MaxLength = 10000000
	s.Options.LogFailures = true
	r.Parameters.Searches["testsearch"] = s
	msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
	if err != nil {
		t.Fatal(err)
	}
	out := r.Run(bytes.NewBuffer(msg))
	if len(out) == 0 {
		t.Fatal("run failed")
	}
	t.Log(out)
	err = json.Unmarshal([]byte(out), &r.Results)
	if err != nil {
		t.Fatal(err)
	}
	if !r.Results.Success {
		t.Fatal("failed to run memory search")
	}
	if !r.Results.FoundAnything {
		t.Fatal("should have found own go test process but didn't")
	}
	prints, err := r.PrintResults(r.Results, false)
	if err != nil {
		t.Fatal(err)
	}
	if len(prints) < 2 {
		t.Fatal("not enough results printed")
	}
	prints, err = r.PrintResults(r.Results, true)
	if err != nil {
		t.Fatal(err)
	}
	if len(prints) != 1 {
		t.Fatal("wrong number of results, should be one")
	}
}
Пример #7
0
func TestDecompressedContentSearch(t *testing.T) {
	for _, tp := range TESTDATA {
		var (
			r run
			s search
		)
		var expectedfiles = []string{
			basedir + "/" + tp.name,
			basedir + subdirs + tp.name,
		}
		// testfile8 is a corrupt gzip and should fail to decompress
		if tp.name == "testfile8" {
			expectedfiles = []string{""}
		}
		r.Parameters = *newParameters()
		s.Paths = append(s.Paths, basedir)
		if tp.decompressedcontent != "" {
			s.Contents = append(s.Contents, tp.decompressedcontent)
		} else {
			s.Contents = append(s.Contents, tp.content)
		}
		s.Contents = append(s.Contents, "!^FOOBAR$")
		s.Options.MatchAll = true
		s.Options.Decompress = true
		r.Parameters.Searches["s1"] = s
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		t.Logf("%s\n", msg)
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = evalResults([]byte(out), expectedfiles)
		if err != nil {
			t.Fatal(err)
		}
	}
}
Пример #8
0
// This function is used to call the file module from this module. In order to
// avoid exporting types from the file module, we construct parameters for the
// file module using the parameter creation functions (passing command line
// arguments).
//
// We use the file modules file system location functions here to avoid
// duplicating functionality in this module.
func fileModuleLocator(pattern string, regex bool, root string, depth int) ([]string, error) {
	ret := make([]string, 0)

	// Build a pseudo-run struct to let us call the file module.
	run := modules.Available["file"].NewRun()
	args := make([]string, 0)
	args = append(args, "-path", root)
	args = append(args, "-name", pattern)
	args = append(args, "-maxdepth", strconv.Itoa(depth))
	param, err := run.(modules.HasParamsParser).ParamsParser(args)

	buf, err := modules.MakeMessage(modules.MsgClassParameters, param, false)
	if err != nil {
		return ret, nil
	}
	rdr := bytes.NewReader(buf)

	res := run.Run(rdr)
	var modresult modules.Result
	var sr file.SearchResults
	err = json.Unmarshal([]byte(res), &modresult)
	if err != nil {
		return ret, err
	}
	err = modresult.GetElements(&sr)
	if err != nil {
		return ret, err
	}

	p0, ok := sr["s1"]
	if !ok {
		return ret, fmt.Errorf("result in file module call was missing")
	}
	for _, x := range p0 {
		ret = append(ret, x.File)
	}

	return ret, nil
}
Пример #9
0
func TestSearches(t *testing.T) {
	var parameters = []testParams{
		{true, `{"searches":{"s1":{"names":["go"]}}}`},
		{false, `{"searches":{"s1":{"libraries":["^caribou.so$"]}}}`},
		{true, `{"searches":{"s1":{"contents":["memory_test"], "names": ["go"]}}}`},
		{false, `{"searches":{"s1":{"names":["1983yrotewdshhhoiufhes7fd29"],"bytes":["ffffffffaaaabbbbcccceeee"],"options":{"matchall": true}}}}`},
	}
	for _, tp := range parameters {
		var r run
		r.Parameters = *newParameters()
		err := json.Unmarshal([]byte(tp.params), &r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = json.Unmarshal([]byte(out), &r.Results)
		if err != nil {
			t.Fatal(err)
		}
		if !r.Results.Success {
			t.Fatal("failed to run memory search")
		}
		if r.Results.FoundAnything && !tp.expect {
			t.Fatalf("found something for search '%s' and shouldn't have", tp.params)
		} else if !r.Results.FoundAnything && tp.expect {
			t.Fatalf("found nothing for search '%s' and should have", tp.params)
		}
	}
}
Пример #10
0
func main() {
	var (
		conf                                           client.Configuration
		cli                                            client.Client
		err                                            error
		op                                             mig.Operation
		a                                              mig.Action
		migrc, show, render, target, expiration, afile string
		verbose                                        bool
		modargs                                        []string
		run                                            interface{}
	)
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "%v\n", e)
		}
	}()
	homedir := client.FindHomedir()
	fs := flag.NewFlagSet("mig flag", flag.ContinueOnError)
	fs.Usage = continueOnFlagError
	fs.StringVar(&migrc, "c", homedir+"/.migrc", "alternative configuration file")
	fs.StringVar(&show, "show", "found", "type of results to show")
	fs.StringVar(&render, "render", "text", "results rendering mode")
	fs.StringVar(&target, "t", fmt.Sprintf("status='%s' AND mode='daemon'", mig.AgtStatusOnline), "action target")
	fs.StringVar(&expiration, "e", "300s", "expiration")
	fs.StringVar(&afile, "i", "/path/to/file", "Load action from file")
	fs.BoolVar(&verbose, "v", false, "Enable verbose output")

	// if first argument is missing, or is help, print help
	// otherwise, pass the remainder of the arguments to the module for parsing
	// this client is agnostic to module parameters
	if len(os.Args) < 2 || os.Args[1] == "help" || os.Args[1] == "-h" || os.Args[1] == "--help" {
		usage()
	}

	if len(os.Args) < 2 || os.Args[1] == "-V" {
		fmt.Println(version)
		os.Exit(0)
	}

	// when reading the action from a file, go directly to launch
	if os.Args[1] == "-i" {
		err = fs.Parse(os.Args[1:])
		if err != nil {
			panic(err)
		}
		if afile == "/path/to/file" {
			panic("-i flag must take an action file path as argument")
		}
		a, err = mig.ActionFromFile(afile)
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stderr, "[info] launching action from file, all flags are ignored\n")
		goto readytolaunch
	}

	// arguments parsing works as follow:
	// * os.Args[1] must contain the name of the module to launch. we first verify
	//   that a module exist for this name and then continue parsing
	// * os.Args[2:] contains both global options and module parameters. We parse the
	//   whole []string to extract global options, and module parameters will be left
	//   unparsed in fs.Args()
	// * fs.Args() with the module parameters is passed as a string to the module parser
	//   which will return a module operation to store in the action
	op.Module = os.Args[1]
	if _, ok := modules.Available[op.Module]; !ok {
		panic("Unknown module " + op.Module)
	}

	// -- Ugly hack Warning --
	// Parse() will fail on the first flag that is not defined, but in our case module flags
	// are defined in the module packages and not in this program. Therefore, the flag parse error
	// is expected. Unfortunately, Parse() writes directly to stderr and displays the error to
	// the user, which confuses them. The right fix would be to prevent Parse() from writing to
	// stderr, since that's really the job of the calling program, but in the meantime we work around
	// it by redirecting stderr to null before calling Parse(), and put it back to normal afterward.
	// for ref, issue is at https://github.com/golang/go/blob/master/src/flag/flag.go#L793
	fs.SetOutput(os.NewFile(uintptr(87592), os.DevNull))
	err = fs.Parse(os.Args[2:])
	fs.SetOutput(nil)
	if err != nil {
		// ignore the flag not defined error, which is expected because
		// module parameters are defined in modules and not in main
		if len(err.Error()) > 30 && err.Error()[0:29] == "flag provided but not defined" {
			// requeue the parameter that failed
			modargs = append(modargs, err.Error()[31:])
		} else {
			// if it's another error, panic
			panic(err)
		}
	}
	for _, arg := range fs.Args() {
		modargs = append(modargs, arg)
	}
	run = modules.Available[op.Module].NewRun()
	if _, ok := run.(modules.HasParamsParser); !ok {
		fmt.Fprintf(os.Stderr, "[error] module '%s' does not support command line invocation\n", op.Module)
		os.Exit(2)
	}
	op.Parameters, err = run.(modules.HasParamsParser).ParamsParser(modargs)
	if err != nil || op.Parameters == nil {
		panic(err)
	}
	// If running against the local target, don't post the action to the MIG API
	// but run it locally instead.
	if target == "local" {
		msg, err := modules.MakeMessage(modules.MsgClassParameters, op.Parameters)
		if err != nil {
			panic(err)
		}
		out := run.(modules.Runner).Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			panic("got empty results, run failed")
		}
		if _, ok := run.(modules.HasResultsPrinter); ok {
			var modres modules.Result
			err := json.Unmarshal([]byte(out), &modres)
			if err != nil {
				panic(err)
			}
			outRes, err := run.(modules.HasResultsPrinter).PrintResults(modres, true)
			if err != nil {
				panic(err)
			}
			for _, resLine := range outRes {
				fmt.Println(resLine)
			}
		} else {
			out = fmt.Sprintf("%s\n", out)
		}
		os.Exit(0)
	}

	a.Operations = append(a.Operations, op)

	for _, arg := range os.Args[1:] {
		a.Name += arg + " "
	}
	a.Target = target

readytolaunch:
	// instanciate an API client
	conf, err = client.ReadConfiguration(migrc)
	if err != nil {
		panic(err)
	}
	cli, err = client.NewClient(conf, "cmd-"+version)
	if err != nil {
		panic(err)
	}

	if verbose {
		cli.EnableDebug()
	}

	// set the validity 60 second in the past to deal with clock skew
	a.ValidFrom = time.Now().Add(-60 * time.Second).UTC()
	period, err := time.ParseDuration(expiration)
	if err != nil {
		panic(err)
	}
	a.ExpireAfter = a.ValidFrom.Add(period)
	// add extra 60 seconds taken for clock skew
	a.ExpireAfter = a.ExpireAfter.Add(60 * time.Second).UTC()

	asig, err := cli.SignAction(a)
	if err != nil {
		panic(err)
	}
	a = asig

	// evaluate target before launch, give a change to cancel before going out to agents
	agents, err := cli.EvaluateAgentTarget(a.Target)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33m%d agents will be targeted. ctrl+c to cancel. launching in \x1b[0m", len(agents))
	for i := 5; i > 0; i-- {
		time.Sleep(1 * time.Second)
		fmt.Fprintf(os.Stderr, "\x1b[33m%d\x1b[0m ", i)
	}
	fmt.Fprintf(os.Stderr, "\x1b[33mGO\n\x1b[0m")

	// launch and follow
	a, err = cli.PostAction(a)
	if err != nil {
		panic(err)
	}
	c := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		err = cli.FollowAction(a)
		if err != nil {
			panic(err)
		}
		done <- true
	}()
	select {
	case <-c:
		fmt.Fprintf(os.Stderr, "stop following action. agents may still be running. printing available results:\n")
		goto printresults
	case <-done:
		goto printresults
	}
printresults:
	err = cli.PrintActionResults(a, show, render)
	if err != nil {
		panic(err)
	}
}
Пример #11
0
func TestMismatch(t *testing.T) {
	var MismatchTestCases = []mismatchtest{
		mismatchtest{
			desc: "want files that don't match name '^testfile0' with maxdept=1, should find testfile1 and testfile2",
			search: search{
				Paths: []string{basedir},
				Names: []string{"^" + TESTDATA[0].name + "$"},
				Options: options{
					MaxDepth: 1,
					Mismatch: []string{"name"},
				},
			},
			expectedfiles: []string{
				basedir + "/" + TESTDATA[1].name,
				basedir + "/" + TESTDATA[2].name},
		},
		mismatchtest{
			desc: "want files that don't have a size of 190 bytes or larger than 10{k,m,g,t} or smaller than 10 bytes, should find testfile1 and testfile2",
			search: search{
				Paths: []string{basedir},
				Sizes: []string{"190", ">10k", ">10m", ">10g", ">10t", "<10"},
				Options: options{
					MaxDepth: 1,
					MatchAll: true,
					Mismatch: []string{"size"},
				},
			},
			expectedfiles: []string{
				basedir + "/" + TESTDATA[1].name,
				basedir + "/" + TESTDATA[2].name},
		},
		mismatchtest{
			desc: "want files that have not been modified in the last hour ago, should find nothing",
			search: search{
				Paths:  []string{basedir + subdirs, basedir},
				Mtimes: []string{"<1h"},
				Options: options{
					Mismatch: []string{"mtime"},
				},
			},
			expectedfiles: []string{""},
		},
		mismatchtest{
			desc: "want files that don't have 644 permissions, should find nothing",
			search: search{
				Paths: []string{basedir},
				Modes: []string{"-rw-r--r--"},
				Options: options{
					Mismatch: []string{"mode"},
				},
			},
			expectedfiles: []string{""},
		},
		mismatchtest{
			desc: "want files that don't a name different than testfile0, should find testfile0",
			search: search{
				Paths: []string{basedir},
				Names: []string{"!^testfile0$"},
				Options: options{
					Mismatch: []string{"name"},
				},
			},
			expectedfiles: []string{
				basedir + "/" + TESTDATA[0].name,
				basedir + subdirs + TESTDATA[0].name},
		},
		mismatchtest{
			desc: "test matchall+macroal+mismatch: want file where at least one line fails to match the regex on testfile0, should find testfile1 that has the extra line 'some other other text'",
			search: search{
				Paths:    []string{basedir},
				Names:    []string{"^testfile(0|1)$"},
				Contents: []string{`^((---.+)|(#.+)|(\s+)|(some (other )?text))?$`},
				Options: options{
					MatchAll: true,
					Macroal:  true,
					Mismatch: []string{"content"},
				},
			},
			expectedfiles: []string{
				basedir + "/" + TESTDATA[1].name,
				basedir + subdirs + TESTDATA[1].name},
		},
		mismatchtest{
			desc: "want files that don't match the hashes of testfile2, should find testfile0 & 1",
			search: search{
				Paths:    []string{basedir},
				MD5:      []string{TESTDATA[2].md5},
				SHA1:     []string{TESTDATA[2].sha1},
				SHA256:   []string{TESTDATA[2].sha256},
				SHA384:   []string{TESTDATA[2].sha384},
				SHA512:   []string{TESTDATA[2].sha512},
				SHA3_224: []string{TESTDATA[2].sha3_224},
				SHA3_256: []string{TESTDATA[2].sha3_256},
				SHA3_384: []string{TESTDATA[2].sha3_384},
				SHA3_512: []string{TESTDATA[2].sha3_512},
				Options: options{
					MaxDepth: 1,
					MatchAll: true,
					Mismatch: []string{`md5`, `sha1`, `sha256`, `sha384`, `sha512`,
						`sha3_224`, `sha3_256`, `sha3_384`, `sha3_512`},
				},
			},
			expectedfiles: []string{
				basedir + "/" + TESTDATA[0].name,
				basedir + "/" + TESTDATA[1].name},
		},
	}

	for _, mt := range MismatchTestCases {
		t.Log(mt.desc)
		var r run
		r.Parameters = *newParameters()
		r.Parameters.Searches["s1"] = mt.search
		msg, err := modules.MakeMessage(modules.MsgClassParameters, r.Parameters)
		if err != nil {
			t.Fatal(err)
		}
		t.Logf("%s\n", msg)
		out := r.Run(bytes.NewBuffer(msg))
		if len(out) == 0 {
			t.Fatal("run failed")
		}
		t.Log(out)
		err = evalResults([]byte(out), mt.expectedfiles)
		if err != nil {
			t.Fatal(err)
		}
	}

}