示例#1
0
文件: example.go 项目: jvehent/mig
// PrintResults() is an *optional* method that returns results in a human-readable format.
// if matchOnly is set, only results that have at least one match are returned.
// If matchOnly is not set, all results are returned, along with errors and statistics.
func (r *run) PrintResults(result modules.Result, matchOnly bool) (prints []string, err error) {
	var (
		el    elements
		stats statistics
	)
	err = result.GetElements(&el)
	if err != nil {
		panic(err)
	}
	if el.Hostname != "" {
		prints = append(prints, fmt.Sprintf("hostname is %s", el.Hostname))
	}
	for _, addr := range el.Addresses {
		prints = append(prints, fmt.Sprintf("address is %s", addr))
	}
	for host, addrs := range el.LookedUpHost {
		for _, addr := range addrs {
			prints = append(prints, fmt.Sprintf("lookedup host %s has IP %s", host, addr))
		}
	}
	if matchOnly {
		return
	}
	for _, e := range result.Errors {
		prints = append(prints, fmt.Sprintf("error: %v", e))
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}
	prints = append(prints, fmt.Sprintf("stat: %d stuff found", stats.StuffFound))
	return
}
示例#2
0
文件: memory.go 项目: jvehent/mig
// PrintResults() returns results in a human-readable format. if foundOnly is set,
// only results that have at least one match are returned.
// If foundOnly is not set, all results are returned, along with errors and
// statistics.
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		el    searchResults
		stats statistics
	)
	err = result.GetElements(&el)
	if err != nil {
		panic(err)
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}

	for label, sr := range el {
		for _, mps := range sr {
			var out string
			if mps.Process.Name == "" {
				if foundOnly {
					continue
				}
				out = fmt.Sprintf("0 match found in search '%s'", label)
			} else {
				out = fmt.Sprintf("%s [pid:%.0f] in search '%s'",
					mps.Process.Name, mps.Process.Pid, label)
			}
			if mps.Search.Options.MatchAll {
				prints = append(prints, out)
				continue
			}
			out += " on checks"
			// if matchany, print the detail of the checks that matched with the filename
			for _, v := range mps.Search.Names {
				out += fmt.Sprintf(" name='%s'", v)
			}
			for _, v := range mps.Search.Libraries {
				out += fmt.Sprintf(" library='%s'", v)
			}
			for _, v := range mps.Search.Contents {
				out += fmt.Sprintf(" content='%s'", v)
			}
			for _, v := range mps.Search.Bytes {
				out += fmt.Sprintf(" byte='%s'", v)
			}
			prints = append(prints, out)
		}
	}
	if !foundOnly {
		for _, e := range stats.Failures {
			prints = append(prints, fmt.Sprintf("Failure: %v", e))
		}
		for _, e := range result.Errors {
			prints = append(prints, e)
		}
		stat := fmt.Sprintf("Statistics: %.0f processes checked, %.0f matched, %d failures, ran in %s.",
			stats.ProcessCount, stats.TotalHits, len(stats.Failures), stats.Exectime)
		prints = append(prints, stat)
	}
	return
}
示例#3
0
文件: pkg.go 项目: sneha29shukla/mig
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		elem  elements
		stats Statistics
	)

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}

	for _, x := range elem.Packages {
		resStr := fmt.Sprintf("pkgmatch name=%v version=%v type=%v", x.Name, x.Version, x.Type)
		prints = append(prints, resStr)
	}

	if !foundOnly {
		for _, we := range result.Errors {
			prints = append(prints, we)
		}
		stats := fmt.Sprintf("Statistics: runtime %v", stats.ExecRuntime)
		prints = append(prints, stats)
	}

	return
}
示例#4
0
文件: scribe.go 项目: agnivesh/mig
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		elem  ScribeElements
		stats statistics
	)

	err = result.GetElements(&elem)
	if err != nil {
		panic(err)
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}
	for _, x := range elem.Results {
		if elem.HumanOutput {
			prints = append(prints, x.String())
		} else if elem.JSONOutput {
			prints = append(prints, x.JSON())
		} else {
			for _, y := range x.SingleLineResults() {
				prints = append(prints, y)
			}
		}
	}
	if !foundOnly {
		for _, we := range result.Errors {
			prints = append(prints, we)
		}
		s := fmt.Sprintf("Statistics: runtime %v", stats.ExecRuntime)
		prints = append(prints, s)
	}
	return
}
示例#5
0
func evalResults(jsonresults []byte, expectedfiles []string) error {
	var (
		mr modules.Result
		sr SearchResults
	)
	err := json.Unmarshal(jsonresults, &mr)
	if err != nil {
		return err
	}
	if !mr.Success {
		return fmt.Errorf("failed to run file search")
	}
	if !mr.FoundAnything {
		return fmt.Errorf("should have found %d files in '%s' but didn't",
			len(expectedfiles), basedir)
	}
	if mr.GetElements(&sr) != nil {
		return fmt.Errorf("failed to retrieve search results")
	}
	if len(expectedfiles) == 1 && expectedfiles[0] == "" {
		// should not have found anything to succeed
		if len(sr["s1"]) != 1 {
			return fmt.Errorf("expected to find nothing but found %d files",
				len(sr["s1"]))
		} else if sr["s1"][0].File != "" {
			return fmt.Errorf("expected to find nothing but found file '%s'",
				sr["s1"][0].File)
		}
	}
	if len(sr["s1"]) != len(expectedfiles) {
		if len(sr["s1"]) == 1 && sr["s1"][0].File == "" {
			return fmt.Errorf("expected to find %d files but found nothing",
				len(expectedfiles))
		}
		return fmt.Errorf("expected to find %d files but found %d",
			len(expectedfiles), len(sr["s1"]))
	}
	for _, found := range sr["s1"] {
		for i, expectedfile := range expectedfiles {
			if filepath.Clean(found.File) == filepath.Clean(expectedfile) {
				// good result, remove expected file from list of expected files
				expectedfiles = expectedfiles[:i+copy(expectedfiles[i:], expectedfiles[i+1:])]
			}
		}
	}
	if len(expectedfiles) != 0 {
		return fmt.Errorf("did not find %d files: %s", len(expectedfiles), expectedfiles)
	}
	return nil
}
示例#6
0
文件: ping.go 项目: tudalex/mig
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var el elements
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("Print Error: %v", e)
		}
	}()

	err = result.GetElements(&el)
	if err != nil {
		panic(err)
	}
	if result.FoundAnything {
		prints = append(prints,
			fmt.Sprintf("%s ping of %s succeeded. Target is reachable.",
				el.Protocol,
				el.ResolvedHost,
			),
		)
	}
	// if we don't care about results where the target was not reachable, stop here
	if foundOnly {
		return
	}
	if !result.FoundAnything {
		prints = append(prints,
			fmt.Sprintf("%s ping of %s failed. Target is no reachable.",
				el.Protocol,
				el.ResolvedHost,
			),
		)
	}
	for i, lat := range el.Latencies {
		switch lat {
		case -1:
			prints = append(prints, fmt.Sprintf("ping #%d failed, target was unreachable", i+1))
		case 0:
			prints = append(prints, fmt.Sprintf("ping #%d failed, reason unknown", i+1))
		case 9999999:
			if el.Protocol == "udp" {
				prints = append(prints, fmt.Sprintf("ping #%d may have succeeded (no udp response)", i+1))
			} else {
				prints = append(prints, fmt.Sprintf("ping #%d failed, connection timed out", i+1))
			}
		default:
			prints = append(prints, fmt.Sprintf("ping #%d succeeded in %.0fms", i+1, lat))
		}
	}
	return
}
示例#7
0
文件: timedrift.go 项目: tudalex/mig
func (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var (
		el    elements
		stats statistics
	)
	err = result.GetElements(&el)
	if err != nil {
		return
	}
	prints = append(prints, "local time is "+el.LocalTime)
	if el.HasCheckedDrift {
		if el.IsWithinDrift {
			prints = append(prints, "local time is within acceptable drift from NTP servers")
		} else {
			prints = append(prints, "local time is out of sync from NTP servers")
			for _, drift := range el.Drifts {
				prints = append(prints, drift)
			}
		}
	}
	// stop here if foundOnly is set, we don't want to see errors and stats
	if foundOnly {
		return
	}
	for _, e := range result.Errors {
		prints = append(prints, "error:", e)
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}
	prints = append(prints, "stat: execution time was "+stats.ExecTime)
	for _, ntpstat := range stats.NtpStats {
		if ntpstat.Reachable {
			prints = append(prints, "stat: "+ntpstat.Host+" responded in "+ntpstat.Latency+" with time "+ntpstat.Time.UTC().String()+". local time drifts by "+ntpstat.Drift)
		} else {
			prints = append(prints, "stat: "+ntpstat.Host+" was unreachable")
		}
	}
	if result.Success {
		prints = append(prints, fmt.Sprintf("timedrift module has succeeded"))
	} else {
		prints = append(prints, fmt.Sprintf("timedrift module has failed"))
	}
	return
}
示例#8
0
文件: scribe.go 项目: agnivesh/mig
// 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 (r *run) PrintResults(result modules.Result, foundOnly bool) (prints []string, err error) {
	var statusMsg string
	err = result.GetElements(&statusMsg)
	if err != nil {
		prints = append(prints, "[error] failed to retrieve status message from results")
	}
	if statusMsg != "" {
		prints = append(prints, statusMsg)
	}
	if foundOnly {
		return
	}
	for _, e := range result.Errors {
		prints = append(prints, "[error] "+e)
	}
	if result.Success {
		prints = append(prints, fmt.Sprintf("agentdestroy module has succeeded"))
	} else {
		prints = append(prints, fmt.Sprintf("agentdestroy module has failed"))
	}
	return
}
示例#10
0
文件: main.go 项目: keoni161/mig
func makeComplianceItem(cmd mig.Command, conf Config) (items []gozdef.ComplianceItem, err error) {
	var ci gozdef.ComplianceItem
	ci.Utctimestamp = time.Now().UTC().Format(time.RFC3339Nano)
	ci.Target = cmd.Agent.Name
	ci.Policy.Name = cmd.Action.Threat.Type
	ci.Policy.URL = cmd.Action.Description.URL
	ci.Policy.Level = cmd.Action.Threat.Level
	ci.Check.Ref = cmd.Action.Threat.Ref
	ci.Check.Description = cmd.Action.Name
	ci.Link = fmt.Sprintf("%s/command?commandid=%.0f", conf.API.Host, cmd.ID)
	if cmd.Agent.Tags != nil {
		operator := ""
		if _, ok := cmd.Agent.Tags.(map[string]interface{})["operator"]; ok {
			operator = cmd.Agent.Tags.(map[string]interface{})["operator"].(string)
		}
		team := getTeam(cmd.Agent, conf)
		ci.Tags = struct {
			Operator string `json:"operator"`
			Team     string `json:"team"`
		}{
			Operator: operator,
			Team:     team,
		}
	}
	for i, result := range cmd.Results {
		buf, err := json.Marshal(result)
		if err != nil {
			return items, err
		}
		if i > (len(cmd.Action.Operations) - 1) {
			// skip this entry if the lookup fails
			continue
		}
		switch cmd.Action.Operations[i].Module {
		case "file":
			var r modules.Result
			var el file.SearchResults
			err = json.Unmarshal(buf, &r)
			if err != nil {
				return items, err
			}
			err = r.GetElements(&el)
			if err != nil {
				return items, err
			}
			for label, sr := range el {
				for _, mf := range sr {
					ci.Check.Location = mf.File
					ci.Check.Name = label
					ci.Check.Test.Type = "file"
					ci.Check.Test.Value = ""
					for _, v := range mf.Search.Names {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("name='%s'", v)
					}
					for _, v := range mf.Search.Sizes {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("size='%s'", v)
					}
					for _, v := range mf.Search.Modes {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("mode='%s'", v)
					}
					for _, v := range mf.Search.Mtimes {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("mtime='%s'", v)
					}
					for _, v := range mf.Search.Contents {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("content='%s'", v)
					}
					for _, v := range mf.Search.MD5 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("md5='%s'", v)
					}
					for _, v := range mf.Search.SHA1 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha1='%s'", v)
					}
					for _, v := range mf.Search.SHA256 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha256='%s'", v)
					}
					for _, v := range mf.Search.SHA384 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha384='%s'", v)
					}
					for _, v := range mf.Search.SHA512 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha512='%s'", v)
					}
					for _, v := range mf.Search.SHA3_224 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha3_224='%s'", v)
					}
					for _, v := range mf.Search.SHA3_256 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha3_256='%s'", v)
					}
					for _, v := range mf.Search.SHA3_384 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha3_384='%s'", v)
					}
					for _, v := range mf.Search.SHA3_512 {
						if len(ci.Check.Test.Value) > 0 {
							ci.Check.Test.Value += " and "
						}
						ci.Check.Test.Value += fmt.Sprintf("sha3_512='%s'", v)
					}
					if mf.File == "" {
						for i, p := range mf.Search.Paths {
							if i > 0 {
								ci.Check.Location += ", "
							}
							ci.Check.Location += p
						}
						ci.Compliance = false
					} else {
						ci.Compliance = true
					}
					items = append(items, ci)
				}
			}
		}
	}
	return
}
示例#11
0
func (r *run) PrintResults(result modules.Result, matchOnly bool) (prints []string, err error) {
	var (
		el    elements
		stats statistics
	)
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("PrintResults() -> %v", e)
		}
	}()
	el = *newElements()
	err = result.GetElements(&el)
	if err != nil {
		panic(err)
	}
	for val, res := range el.LocalMAC {
		if matchOnly && len(res) < 1 {
			continue
		}
		for _, el := range res {
			resStr := fmt.Sprintf("found local mac %s for netstat localmac:'%s'", el.LocalMACAddr, val)
			resStr += printNamespaceId(el.Namespace)
			prints = append(prints, resStr)
		}
	}
	for val, res := range el.NeighborMAC {
		if matchOnly && len(res) < 1 {
			continue
		}
		for _, el := range res {
			resStr := fmt.Sprintf("found neighbor mac %s %s for netstat neighbormac:'%s'",
				el.RemoteMACAddr, el.RemoteAddr, val)
			resStr += printNamespaceId(el.Namespace)
			prints = append(prints, resStr)
		}
		if len(res) == 0 {
			resStr := fmt.Sprintf("did not find anything for netstat neighbormac:'%s'", val)
			prints = append(prints, resStr)
		}
	}
	for val, res := range el.LocalIP {
		if matchOnly && len(res) < 1 {
			continue
		}
		for _, el := range res {
			resStr := fmt.Sprintf("found local ip %s for netstat localip:'%s'", el.LocalAddr, val)
			resStr += printNamespaceId(el.Namespace)
			prints = append(prints, resStr)
		}
		if len(res) == 0 {
			resStr := fmt.Sprintf("did not find anything for netstat localip:'%s'", val)
			prints = append(prints, resStr)
		}
	}
	for val, res := range el.ConnectedIP {
		if matchOnly && len(res) < 1 {
			continue
		}
		for _, el := range res {
			resStr := fmt.Sprintf("found connected tuple %s:%.0f with local tuple %s:%.0f for netstat connectedip:'%s'",
				el.RemoteAddr, el.RemotePort, el.LocalAddr, el.LocalPort, val)
			resStr += printNamespaceId(el.Namespace)
			prints = append(prints, resStr)
		}
		if len(res) == 0 {
			resStr := fmt.Sprintf("did not find anything for netstat connectedip:'%s'", val)
			prints = append(prints, resStr)
		}
	}
	for val, res := range el.ListeningPort {
		if matchOnly && len(res) < 1 {
			continue
		}
		for _, el := range res {
			resStr := fmt.Sprintf("found listening port %.0f for netstat listeningport:'%s'", el.LocalPort, val)
			resStr += printNamespaceId(el.Namespace)
			prints = append(prints, resStr)
		}
		if len(res) == 0 {
			resStr := fmt.Sprintf("did not find anything for netstat listeningport:'%s'", val)
			prints = append(prints, resStr)
		}
	}
	if matchOnly {
		return
	}
	for _, e := range result.Errors {
		prints = append(prints, fmt.Sprintf("error: %v", e))
	}
	err = result.GetStatistics(&stats)
	if err != nil {
		panic(err)
	}
	resStr := fmt.Sprintf("Statistics: total hits %.0f examined %.0f items exectime %s",
		stats.Totalhits, stats.Examined, stats.Exectime)
	prints = append(prints, resStr)
	return
}
示例#12
0
func commandsToComplianceItems(commands []mig.Command) (items []ComplianceItem, err error) {
	for _, cmd := range commands {
		var bitem ComplianceItem
		bitem.Utctimestamp = cmd.FinishTime.UTC().Format(time.RFC3339Nano)
		bitem.Target = cmd.Agent.Name
		bitem.Policy.Name = cmd.Action.Threat.Type
		bitem.Policy.URL = cmd.Action.Description.URL
		bitem.Policy.Level = cmd.Action.Threat.Level
		bitem.Check.Ref = cmd.Action.Threat.Ref
		bitem.Check.Description = cmd.Action.Name
		bitem.Link = fmt.Sprintf("%s/command?commandid=%.0f", ctx.Server.BaseURL, cmd.ID)
		if _, ok := cmd.Agent.Tags.(map[string]interface{})["operator"]; ok {
			var t ComplianceTags
			t.Operator = cmd.Agent.Tags.(map[string]interface{})["operator"].(string)
			bitem.Tags = t
		}
		for i, result := range cmd.Results {
			buf, err := json.Marshal(result)
			if err != nil {
				return items, err
			}
			if i > (len(cmd.Action.Operations) - 1) {
				// skip this entry if the lookup fails
				continue
			}
			switch cmd.Action.Operations[i].Module {
			case "file":
				var el file.SearchResults
				var r modules.Result
				err = json.Unmarshal(buf, &r)
				if err != nil {
					return items, err
				}
				err = r.GetElements(&el)
				if err != nil {
					return items, err
				}
				for label, sr := range el {
					for _, mf := range sr {
						bitem.Check.Location = mf.File
						bitem.Check.Name = label
						bitem.Check.Test.Type = "file"
						bitem.Check.Test.Value = ""
						for _, v := range mf.Search.Names {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("name='%s'", v)
						}
						for _, v := range mf.Search.Sizes {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("size='%s'", v)
						}
						for _, v := range mf.Search.Modes {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("mode='%s'", v)
						}
						for _, v := range mf.Search.Mtimes {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("mtime='%s'", v)
						}
						for _, v := range mf.Search.Contents {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("content='%s'", v)
						}
						for _, v := range mf.Search.MD5 {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("md5='%s'", v)
						}
						for _, v := range mf.Search.SHA1 {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("sha1='%s'", v)
						}
						for _, v := range mf.Search.SHA2 {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("sha2='%s'", v)
						}
						for _, v := range mf.Search.SHA3 {
							if len(bitem.Check.Test.Value) > 0 {
								bitem.Check.Test.Value += " and "
							}
							bitem.Check.Test.Value += fmt.Sprintf("sha3='%s'", v)
						}
						if mf.File == "" {
							for i, p := range mf.Search.Paths {
								if i > 0 {
									bitem.Check.Location += ", "
								}
								bitem.Check.Location += p
							}
							bitem.Compliance = false
						} else {
							bitem.Compliance = true
						}
						items = append(items, bitem)
					}
				}
			}
		}
	}
	return
}