osel/qualys/assets.go
Nate Johnston ca0e1ca769 Initial import of osel code
This is an initial import of the osel codebase.  The osel tool is a tool that
initiates external security scans (initially through Qualys) upon reciept of
AMQP events that indicate certain sensitive events have occurred, like a
security group rule change.

The commit history had to be thrown away because it contained some non-public
data, so I would like to call out the following contributors:

This uses go 1.10 and vgo for dependency management.

Co-Authored-By: Charles Bitter <Charles_Bitter@cable.comcast.com>
Co-Authored-By: Olivier Gagnon <Olivier_Gagnon@cable.comcast.com>
Co-Authored-By: Joseph Sleiman <Joseph_Sleiman@comcast.com>

Change-Id: Ib6abe2024fd91978b783ceee4cff8bb4678d7b15
2018-03-24 15:30:57 +00:00

171 lines
4.4 KiB
Go

package qualys
import (
"bytes"
"fmt"
"net"
"strings"
)
const (
assetsBasePath = "asset"
groupsBasePath = "group"
)
// AssetsService is an interface for interfacing with the Assets
// endpoints of the Qualys API
type AssetsService interface {
ListAssetGroups(*ListAssetGroupOptions) ([]AssetGroup, *Response, error)
GetAssetGroupByID(groupID string) (*AssetGroup, *Response, error)
AddIPsToGroup(*AddIPsToGroupOptions) (*Response, error)
}
// AssetsServiceOp handles communication with the asset related methods of the
// Qualys API.
type AssetsServiceOp struct {
client *Client
}
var _ AssetsService = &AssetsServiceOp{}
// AssetGroup represents a Qualys HostGroup
type AssetGroup struct {
ID string `xml:"ID"`
Title string `xml:"TITLE"`
OwnerUserID string `xml:"OWNER_USER_ID"`
OwnerUnitID string `xml:"OWNER_UNIT_ID"`
IPs AssetGroupIPs `xml:"IP_SET"`
}
// AssetGroupIPs represents one or more IP addresses assigned to the AssetGroup
type AssetGroupIPs struct {
IPs []string `xml:"IP"`
IPRanges []string `xml:"IP_RANGE"`
}
type ipRange struct {
Min net.IP
Max net.IP
}
func newIPRange(rangeString string) *ipRange {
var r = strings.Split(rangeString, "-")
return &ipRange{Min: net.ParseIP(r[0]), Max: net.ParseIP(r[1])}
}
func (ip *ipRange) Contains(ipString string) bool {
var myIP = net.ParseIP(ipString)
if bytes.Compare(myIP, ip.Min) >= 0 && bytes.Compare(myIP, ip.Max) <= 0 {
return true
}
return false
}
// ContainsIP returns true when the AssetGroupIPs matches the provided IP
func (agp *AssetGroupIPs) ContainsIP(ip string) bool {
if containsString(agp.IPs, ip) {
return true
}
if agp.IPRanges != nil && len(agp.IPRanges) > 0 {
for _, ipRange := range agp.IPRanges {
if newIPRange(ipRange).Contains(ip) {
return true
}
}
}
return false
}
// ContainsIP returns true when the AssetGroup has any assets matching the provided IP
func (ag *AssetGroup) ContainsIP(ip string) bool {
return ag.IPs.ContainsIP(ip)
}
type assetGroupsRoot struct {
AssetGroups []AssetGroup `xml:"RESPONSE>ASSET_GROUP_LIST>ASSET_GROUP"`
}
// AssetGroupUpdateRequest represents a request to update a group
type AssetGroupUpdateRequest struct {
}
// ListAssetGroupOptions represents the AssetGroup retrieval options
type ListAssetGroupOptions struct {
Ids []string `url:"ids,comma,omitempty"`
Action string `url:"action,omitempty"`
}
// AddIPsToGroupOptions represents the update request for an AssetGroup
type AddIPsToGroupOptions struct {
GroupID string `url:"id,omitempty"`
IPs []string `url:"add_ips,comma,omitempty"`
}
// ListAssetGroups retrieves a list of AssetGroups
func (s *AssetsServiceOp) ListAssetGroups(opt *ListAssetGroupOptions) ([]AssetGroup, *Response, error) {
return s.listAssetGroups(opt)
}
// GetAssetGroupByID retrieves an AssetGroup by id.
func (s *AssetsServiceOp) GetAssetGroupByID(groupID string) (*AssetGroup, *Response, error) {
return s.getAssetGroup(groupID)
}
// AddIPsToGroup adds the IPs in AddIPsToGroupOptions to the AssetGroup
func (s *AssetsServiceOp) AddIPsToGroup(opt *AddIPsToGroupOptions) (*Response, error) {
return s.addIPsToGroup(opt)
}
func (s *AssetsServiceOp) getAssetGroup(groupID string) (*AssetGroup, *Response, error) {
opts := ListAssetGroupOptions{Ids: []string{groupID}}
groups, response, err := s.listAssetGroups(&opts)
if err != nil {
return nil, response, err
}
if len(groups) == 0 {
return nil, response, nil
}
return &groups[0], response, nil
}
func (s *AssetsServiceOp) addIPsToGroup(opt *AddIPsToGroupOptions) (*Response, error) {
path := fmt.Sprintf("%s/%s/?action=edit", assetsBasePath, groupsBasePath)
req, err := s.client.NewRequest("POST", path, opt)
if err != nil {
return nil, err
}
resp, err := s.client.MakeRequest(req, nil)
if err != nil {
return resp, err
}
return resp, err
}
// Helper method for listing asset groups
func (s *AssetsServiceOp) listAssetGroups(listOpt *ListAssetGroupOptions) ([]AssetGroup, *Response, error) {
path := fmt.Sprintf("%s/%s/", assetsBasePath, groupsBasePath)
if listOpt == nil {
listOpt = &ListAssetGroupOptions{}
}
listOpt.Action = "list"
path, err := addURLParameters(path, listOpt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(assetGroupsRoot)
resp, err := s.client.MakeRequest(req, root)
if err != nil {
return nil, resp, err
}
return root.AssetGroups, resp, err
}