BigFix

HCL BigFix Patch provides an automated, simplified patching process that is administered from a single console.

Vulnerability Management · HCL BigFix

Details

IDBigFix
ProviderHCL Technologies
CategoryVulnerability Management
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Overview


Use the BigFix integration to manage patching processes.

 

Configure BigFix on Cortex XSOAR


  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for BigFix.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Server url (e.g https://192.168.10.1:52311)
    • Username
    • Trust any certificate (not secure)
    • Use system proxy
  4. Click Test to validate the URLs, token, and connection.

Commands


You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

  1. Get all sites: bigfix-get-sites
  2. Get a single site: bigfix-get-site
  3. Get all patches for a site: bigfix-get-patches
  4. Get all endpoints: bigfix-get-endpoints
  5. Get the details of an endpoint: bigfix-get-endpoint
  6. Create an action to run on target computers: bigfix-deploy-patch
  7. Get a patch by fixlet ID: bigfix-get-patch
  8. Delete an action: bigfix-action-delete
  9. Get the status of an action: bigfix-action-status
  10. Stop an action: bigfix-action-stop
  11. Evaluate an expression and get the result: bigfix-query

1. Get all sites


Retrieves all the sites.

Base Command
bigfix-get-sites
Context Output
Path Type Description
Bigfix.Site unknown Site
Bigfix.Site.Name string Name of the site
Bigfix.Site.Description string Description of the site
Bigfix.Site.Resource string Link to the endpoint resource
Bigfix.Site.Type string Type of the site ("master", "custom", "external", "operator")
Bigfix.Site.Domain string Site domain
Bigfix.Site.GatherURL string Gather URL
Bigfix.Site.GlobalReadPermission string Whether the Global Read Permission is available
 
Command Example
!bigfix-get-sites
Context Example
{
    "Bigfix": {
        "Site": [
            {
                "Resource": "https://xsoar-example:20021/api/site/master/ActionSite",
                "Description": null,
                "GatherURL": "http://WIN-CQD6UQJIA7J:52311/cgi-bin/bfgather.exe/actionsite",
                "GlobalReadPermission": "false",
                "Name": "ActionSite",
                "Type": "master",
                "Subscription": {
                    "Mode": "All"
                }
            },
            {
                "Resource": "https://xsoar-example:20021/api/site/external/BES Support",
                "Description": null,
                "GatherURL": "http://sync.bigfix.com/cgi-bin/bfgather/bessupport",
                "GlobalReadPermission": "true",
                "Name": "BES Support",
                "Type": "external",
                "Subscription": {
                    "Mode": "All"
                }
            },
            {
                "Resource": "https://xsoar-example:20021/api/site/external/BES Inventory and License",
                "Description": null,
                "GatherURL": "http://sync.bigfix.com/cgi-bin/bfgather/besinventory",
                "GlobalReadPermission": "false",
                "Name": "BES Inventory and License",
                "Type": "external",
                "Subscription": {
                    "Mode": "None"
                }
            },
            {
                "Resource": "https://xsoar-example:20021/api/site/external/BES Asset Discovery",
                "Description": null,
                "GatherURL": "http://sync.bigfix.com/cgi-bin/bfgather/assetdiscovery",
                "GlobalReadPermission": "false",
                "Name": "BES Asset Discovery",
                "Type": "external",
                "Subscription": {
                    "Mode": "None"
                }
            },
            {
                "GatherURL": "http://WIN-CQD6UQJIA7J:52311/cgi-bin/bfgather.exe/actionsite",
                "Type": "operator",
                "Resource": "https://xsoar-example:20021/api/site/operator/admin",
                "Name": "admin"
            }
        ]
    }
}
Human Readable Output

image

2. Get a single site


Retrieves a single site by name and type.

Base Command
bigfix-get-site
Input
Argument Name Description Required
site_name Name of the site. If the site is external or operator then the site must be specified. Optional
site_type Site type ("external", "operator", "master", "custom") Required
 
Context Output
Path Type Description
Bigfix.Site unknown Site
Bigfix.Site.Name string Name of the site
Bigfix.Site.Description string Description of the site
Bigfix.Site.Resource string Link to the endpoint resource.
Bigfix.Site.Type string Type of the site ("master", "custom", "external", "operator")
Bigfix.Site.Domain string Site domain
Bigfix.Site.GatherURL string Gather URL
Bigfix.Site.GlobalReadPermission string Whether the Global Read Permission is available
 
Command Example
!bigfix-get-site site_type=master
!bigfix-get-site site_type=external site_name="BES Support"
Context Example
{
    "Bigfix": {
        "Site": {
            "Resource": "https://xsoar-example:20021/api/site/master/None",
            "Description": null,
            "GatherURL": "http://WIN-CQD6UQJIA7J:52311/cgi-bin/bfgather.exe/actionsite",
            "GlobalReadPermission": "false",
            "Name": "ActionSite",
            "Type": "master",
            "Subscription": {
                "Mode": "All"
            }
        }
    }
}
Human Readable Output

image

3. Get all patches for a site


Retrieves all the patches (fixlets) of site.

Base Command
bigfix-get-patches
Input
Argument Name Description Required
site_type Type of the site ("external", "operator", "master", "custom") Required
site_name Name of the site. If the site is external or operator then the site must be specified. Optional
 
Context Output
Path Type Description
Bigfix.Patch.ID string Patch (fixlet) ID
Bigfix.Patch.LastModified date Last modified timestamp
Bigfix.Patch.Name string Name of the patch requested
Bigfix.Patch.Resource string The link for the patch
Bigfix.Patch.Description string Description of the requested patch
Bigfix.Patch.Relevance string Relevance of the requested patch
Bigfix.Patch.Category string Category of the requested patch
Bigfix.Patch.DownloadSize string Download size
Bigfix.Patch.Source string Source where the patch originates
Bigfix.Patch.SourceID string Source ID of the requested patch
Bigfix.Patch.SourceSeverity string Source severity of the requested patch
Bigfix.Patch.SourceReleaseDate string Source release date of the requested patch
Bigfix.Patch.ActionID string Action ID of the patch requested.
Bigfix.Patch.ActionScript string Action script of the patch requested.
 
Command Example
!bigfix-get-patches site_type="master"
Context Example
{
    "Bigfix": {
        "Patch": [
            {
                "Category": null,
                "Resource": "https://xsoar-example:20021/api/fixlet/master/38",
                "Description": "This is a description of foo patch",
                "DownloadSize": null,
                "SourceID": null,
                "LastModified": "Fri, 15 Jun 2018 13:31:17 +0000",
                "SourceReleaseDate": "2018-06-15",
                "Source": "Internal",
                "ActionID": "Action1",
                "ActionScript": {
                    "@MIMEType": "application/x-sh",
                    "#text": "#!/bin/sh\n# Enter your action script here\necho \"Hello World\""
                },
                "Relevance": null,
                "SourceSeverity": null,
                "ID": "38",
                "Name": "Anar Fixlet"
            },
            {
                "Category": null,
                "Resource": "https://xsoar-example:20021/api/fixlet/master/39",
                "Description": "Test Fixlet",
                "DownloadSize": null,
                "SourceID": null,
                "LastModified": "Fri, 15 Jun 2018 19:37:29 +0000",
                "SourceReleaseDate": "2018-06-15",
                "Source": "Internal",
                "ActionID": "Action1",
                "ActionScript": {
                    "@MIMEType": "application/x-Fixlet-Windows-Shell",
                    "#text": "// Enter your action script here"
                },
                "Relevance": "true",
                "SourceSeverity": null,
                "ID": "39",
                "Name": "Custom Fixlet"
            }
        ]
    }
}
Human Readable Output

image

4. Get all endpoints


Retrieves all endpoints (computers).

Base Command
bigfix-get-endpoints
Input
Argument Name Description Required
get_endpoint_details Whether to get endpoint full details of each endpoint or just basic details, such as ID and last reported time. We recommend setting this to false if there are many endpoints to retrieve. Optional
 
Context Output
Path Type Description
Bigfix.Endpoint unknown Endpoint (computer)
Bigfix.Endpoint.ID string The IDof the endpoint (computer ID)
Bigfix.Endpoint.Resource string URL to the endpoint details
Bigfix.Endpoint.LastReportTime date Last report time of the endpoint
Bigfix.Endpoint.ActiveDirectoryPath string Active directory path of the endpoint device
Bigfix.Endpoint.AgentType string Agent Type of the endpoint
Bigfix.Endpoint.AgentVersion string Agent Version of the endpoint.
Bigfix.Endpoint.BESRelaySelectionMethod unknown Relay selection method of the endpoint
Bigfix.Endpoint.BESRelayServiceInstalled unknown Relay service installed of the endpoint.
Bigfix.Endpoint.BESRootServer string Root server of the endpoint.
Bigfix.Endpoint.BIOS string BIOS of the endpoint.
Bigfix.Endpoint.CPU string CPU of the endpoint
Bigfix.Endpoint.ClientSettings unknown Client settings of the endpoint
Bigfix.Endpoint.ComputerName string Computer name of the endpoint
Bigfix.Endpoint.ComputerType string Computer Type of the endpoint
Bigfix.Endpoint.DNSName string DNS Name of the endpoint
Bigfix.Endpoint.DeviceType string Device Type of the endpoint device
Bigfix.Endpoint.DistancetoBESRelay unknown Distance to BES Relay of the endpoint
Bigfix.Endpoint.FreeSpaceonSystemDrive unknown Free space on sytem drive of the endpoint
Bigfix.Endpoint.IPAddress string IP of the endpoint
Bigfix.Endpoint.LicenseType unknown License of the endpoint
Bigfix.Endpoint.Locked unknown Locked of the endpoint
Bigfix.Endpoint.OS string OS of the endpoint
Bigfix.Endpoint.RAM number RAM of the endpoint
Bigfix.Endpoint.Relay string Relay of the endpoint
Bigfix.Endpoint.RelayNameOfClient string Relay Name of the client
Bigfix.Endpoint.SubnetAddress string Subnet Address of the endpoint
Bigfix.Endpoint.SubscribedSites string Subscribed sites
Bigfix.Endpoint.TotalSizeofSystemDrive number Total size of system drive
Bigfix.Endpoint.UserName string User name
 
Command Example
!bigfix-get-endpoints
Context Example
{
    "Bigfix": {
        "Endpoint": [
            {
                "UserName": "Administrator",
                "BESRootServer": "win-cqd6uqjia7j (0)",
                "TotalSizeofSystemDrive": "101896 MB",
                "AgentType": "Native",
                "DNSName": "WIN-CQD6UQJIA7J",
                "Resource": "https://xsoar-example:20021/api/computer/2696130",
                "CPU": "2200 MHz Xeon Gold 5120",
                "LicenseType": "Windows Server",
                "Relay": "BES Root Server",
                "BESRelayServiceInstalled": "Automatic",
                "RAM": "8192 MB",
                "BIOS": "07/28/2017",
                "AgentVersion": "9.5.9.62",
                "IPAddress": "192.168.1.57",
                "RelayNameOfClient": "WIN-CQD6UQJIA7J",
                "FreeSpaceonSystemDrive": "71399 MB",
                "BESRelaySelectionMethod": "Automatic",
                "ComputerName": "WIN-CQD6UQJIA7J",
                "SubscribedSites": "http://sync.bigfix.com/cgi-bin/bfgather/bessupport",
                "Locked": "No",
                "ClientSettings": [
                    "CVE-2014-0160=rotated",
                    "_BESClient_ActionManager_SkipVoluntaryOnForceShutdown=1",
                    "_BESClient_LastShutdown_Reason=Service manager shutdown request",
                    "_BESClient_Resource_StartupNormalSpeed=0",
                    "_BESClient_Upgrade_UTF8Settings=1",
                    "_BESClient_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Client\\__BESData\\__Global\\Upload",
                    "_BESGather_Comm_UseDownloadService=0",
                    "_BESGather_Download_CacheLimitMB=1024",
                    "_BESGather_Download_CheckInternetFlag=1",
                    "_BESGather_Download_CheckParentFlag=0",
                    "_BESGather_Download_InactivityTimeout=300",
                    "_BESRelay_HTTPServer_HttpLogDirectoryPath=",
                    "_BESRelay_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESRelay.log",
                    "_BESRelay_HTTPServer_PortNumber=52311",
                    "_BESRelay_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\wwwrootbes\\",
                    "_BESRelay_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\UploadManagerData\\BufferDir",
                    "_BESRelay_UploadManager_ParentURL=",
                    "_Enterprise Server_ClientRegister_RegistrationListReplicatePeriod=3600",
                    "_Enterprise Server_ClientRegister_UDPMessagePort=52311",
                    "_HTTPServer_Referrer_CheckEnabled=1",
                    "_WebReports_HTTPRedirect_PortNumber=8080",
                    "_WebReports_HTTPServer_HostName=http://WIN-CQD6UQJIA7J:52311",
                    "_WebReports_HTTPServer_HttpLogDirectoryPath=",
                    "_WebReports_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESWebReportsServer.log",
                    "_WebReports_HTTPServer_PortNumber=8083",
                    "_WebReports_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsServer\\wwwroot\\",
                    "_WebReports_HTTPServer_SSLCertificateFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsData\\SelfWRCertificate.pem",
                    "_WebReports_HTTPServer_UseSSLFlag=1",
                    "__LockState=false",
                    "__RelaySelect_Automatic=1",
                    "__RelayServer1=",
                    "__RelayServer2=",
                    "__Relay_Control_Server1=",
                    "__Relay_Control_Server2="
                ],
                "SubnetAddress": "192.168.1.0",
                "DeviceType": "Server",
                "ID": "2696130",
                "ActiveDirectoryPath": "<none>",
                "LastReportTime": "Mon, 29 Oct 2018 22:12:04 +0000",
                "DistancetoBESRelay": "0",
                "OS": "Win2016 10.0.14393.2248 (1607)",
                "ComputerType": "Virtual"
            },
            {
                "UserName": "Administrator",
                "BESRootServer": "win-cqd6uqjia7j (0)",
                "TotalSizeofSystemDrive": "101896 MB",
                "AgentType": "Native",
                "DNSName": "WIN-CQD6UQJIA7J",
                "Resource": "https://xsoar-example:20021/api/computer/3385267",
                "CPU": "2200 MHz Xeon",
                "LicenseType": "Windows Server",
                "Relay": "BES Root Server",
                "BESRelayServiceInstalled": "Manual",
                "RAM": "8192 MB",
                "BIOS": "04/05/2016",
                "AgentVersion": "9.5.9.62",
                "IPAddress": "192.168.1.57",
                "RelayNameOfClient": "WIN-CQD6UQJIA7J",
                "FreeSpaceonSystemDrive": "70978 MB",
                "BESRelaySelectionMethod": "Manual",
                "ComputerName": "WIN-CQD6UQJIA7J",
                "SubscribedSites": "http://sync.bigfix.com/cgi-bin/bfgather/bessupport",
                "Locked": "No",
                "ClientSettings": [
                    "_BESClient_ActionManager_SkipVoluntaryOnForceShutdown=1",
                    "_BESClient_Resource_StartupNormalSpeed=0",
                    "_BESClient_Upgrade_UTF8Settings=1",
                    "_BESClient_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Client\\__BESData\\__Global\\Upload",
                    "_BESGather_Comm_UseDownloadService=0",
                    "_BESGather_Download_CacheLimitMB=1024",
                    "_BESGather_Download_CheckInternetFlag=1",
                    "_BESGather_Download_CheckParentFlag=0",
                    "_BESGather_Download_InactivityTimeout=300",
                    "_BESRelay_HTTPServer_HttpLogDirectoryPath=",
                    "_BESRelay_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESRelay.log",
                    "_BESRelay_HTTPServer_PortNumber=52311",
                    "_BESRelay_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\wwwrootbes\\",
                    "_BESRelay_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\UploadManagerData\\BufferDir",
                    "_BESRelay_UploadManager_ParentURL=",
                    "_Enterprise Server_ClientRegister_RegistrationListReplicatePeriod=3600",
                    "_Enterprise Server_ClientRegister_UDPMessagePort=52311",
                    "_HTTPServer_Referrer_CheckEnabled=1",
                    "_WebReports_HTTPRedirect_PortNumber=8080",
                    "_WebReports_HTTPServer_HostName=http://WIN-CQD6UQJIA7J:52311",
                    "_WebReports_HTTPServer_HttpLogDirectoryPath=",
                    "_WebReports_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESWebReportsServer.log",
                    "_WebReports_HTTPServer_PortNumber=8083",
                    "_WebReports_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsServer\\wwwroot\\",
                    "_WebReports_HTTPServer_SSLCertificateFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsData\\SelfWRCertificate.pem",
                    "_WebReports_HTTPServer_UseSSLFlag=1",
                    "__LockState=false",
                    "__RelayServer1=",
                    "__RelayServer2=",
                    "__Relay_Control_Server1=",
                    "__Relay_Control_Server2="
                ],
                "SubnetAddress": "192.168.1.0",
                "DeviceType": "Server",
                "ID": "3385267",
                "ActiveDirectoryPath": "<none>",
                "LastReportTime": "Wed, 27 Jun 2018 00:16:15 +0000",
                "DistancetoBESRelay": "0",
                "OS": "Win2016 10.0.14393.1944 (1607)",
                "ComputerType": "Virtual"
            }
        ]
    }
}
Human Readable Output

image

5. Get the details of an endpoint


Retrieves the details of an endpoint (computer).

Base Command
bigfix-get-endpoint
Input
Argument Name Description Required
computer_id Computer ID Required
 
Context Output
Path Type Description
Bigfix.Endpoint unknown Endpoint (computer)
Bigfix.Endpoint.ID string The if of the endpoint (computer ID)
Bigfix.Endpoint.Resource string URL to the endpoint details
Bigfix.Endpoint.LastReportTime date Last report time of the endpoint
Bigfix.Endpoint.ActiveDirectoryPath string Active directory path of the endpoint device
Bigfix.Endpoint.AgentType string Agent Type of the endpoint
Bigfix.Endpoint.AgentVersion string Agent version of the endpoint
Bigfix.Endpoint.BESRelaySelectionMethod unknown Relay selection method of the endpoint
Bigfix.Endpoint.BESRelayServiceInstalled unknown Relay service installed of the endpoint
Bigfix.Endpoint.BESRootServer string Root server of the endpoint
Bigfix.Endpoint.BIOS string BIOS of the endpoint
Bigfix.Endpoint.CPU string CPU of the endpoint
Bigfix.Endpoint.ClientSettings unknown Client settings of the endpoint
Bigfix.Endpoint.ComputerName string Computer name of the endpoint
Bigfix.Endpoint.ComputerType string Computer type of the endpoint
Bigfix.Endpoint.DNSName string DNS name of the endpoint
Bigfix.Endpoint.DeviceType string Device type of the endpoint device
Bigfix.Endpoint.DistancetoBESRelay unknown Distance to BES Relay of the endpoint
Bigfix.Endpoint.FreeSpaceonSystemDrive unknown Free space on system drive of the endpoint
Bigfix.Endpoint.IPAddress string IP of the endpoint
Bigfix.Endpoint.LicenseType unknown License of the endpoint
Bigfix.Endpoint.Locked unknown Locked of the endpoint
Bigfix.Endpoint.OS string OS of the endpoint
Bigfix.Endpoint.RAM number RAM of the endpoint
Bigfix.Endpoint.Relay string Relay of the endpoint
Bigfix.Endpoint.RelayNameOfClient string Relay Name of the client
Bigfix.Endpoint.SubnetAddress string Subnet Address of the endpoint
Bigfix.Endpoint.SubscribedSites string Subscribed sites
Bigfix.Endpoint.TotalSizeofSystemDrive number Total size of system drive
Bigfix.Endpoint.UserName string User name
 
Command Example
!bigfix-get-endpoint computer_id=3385267
Context Example
{
    "Bigfix": {
        "Endpoint": {
            "UserName": "Administrator",
            "BESRootServer": "win-cqd6uqjia7j (0)",
            "TotalSizeofSystemDrive": "101896 MB",
            "AgentType": "Native",
            "DNSName": "WIN-CQD6UQJIA7J",
            "Resource": "https://xsoar-example:20021/api/computer/3385267",
            "CPU": "2200 MHz Xeon",
            "LicenseType": "Windows Server",
            "Relay": "BES Root Server",
            "BESRelayServiceInstalled": "Manual",
            "RAM": "8192 MB",
            "BIOS": "04/05/2016",
            "AgentVersion": "9.5.9.62",
            "IPAddress": "192.168.1.57",
            "RelayNameOfClient": "WIN-CQD6UQJIA7J",
            "FreeSpaceonSystemDrive": "70978 MB",
            "BESRelaySelectionMethod": "Manual",
            "ComputerName": "WIN-CQD6UQJIA7J",
            "SubscribedSites": "http://sync.bigfix.com/cgi-bin/bfgather/bessupport",
            "Locked": "No",
            "ClientSettings": [
                "_BESClient_ActionManager_SkipVoluntaryOnForceShutdown=1",
                "_BESClient_Resource_StartupNormalSpeed=0",
                "_BESClient_Upgrade_UTF8Settings=1",
                "_BESClient_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Client\\__BESData\\__Global\\Upload",
                "_BESGather_Comm_UseDownloadService=0",
                "_BESGather_Download_CacheLimitMB=1024",
                "_BESGather_Download_CheckInternetFlag=1",
                "_BESGather_Download_CheckParentFlag=0",
                "_BESGather_Download_InactivityTimeout=300",
                "_BESRelay_HTTPServer_HttpLogDirectoryPath=",
                "_BESRelay_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESRelay.log",
                "_BESRelay_HTTPServer_PortNumber=52311",
                "_BESRelay_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\wwwrootbes\\",
                "_BESRelay_UploadManager_BufferDirectory=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\UploadManagerData\\BufferDir",
                "_BESRelay_UploadManager_ParentURL=",
                "_Enterprise Server_ClientRegister_RegistrationListReplicatePeriod=3600",
                "_Enterprise Server_ClientRegister_UDPMessagePort=52311",
                "_HTTPServer_Referrer_CheckEnabled=1",
                "_WebReports_HTTPRedirect_PortNumber=8080",
                "_WebReports_HTTPServer_HostName=http://WIN-CQD6UQJIA7J:52311",
                "_WebReports_HTTPServer_HttpLogDirectoryPath=",
                "_WebReports_HTTPServer_LogFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESWebReportsServer.log",
                "_WebReports_HTTPServer_PortNumber=8083",
                "_WebReports_HTTPServer_ServerRootPath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsServer\\wwwroot\\",
                "_WebReports_HTTPServer_SSLCertificateFilePath=C:\\Program Files (x86)\\BigFix Enterprise\\BES Server\\BESReportsData\\SelfWRCertificate.pem",
                "_WebReports_HTTPServer_UseSSLFlag=1",
                "__LockState=false",
                "__RelayServer1=",
                "__RelayServer2=",
                "__Relay_Control_Server1=",
                "__Relay_Control_Server2="
            ],
            "SubnetAddress": "192.168.1.0",
            "DeviceType": "Server",
            "ID": "3385267",
            "ActiveDirectoryPath": "<none>",
            "LastReportTime": "Wed, 27 Jun 2018 00:16:15 +0000",
            "DistancetoBESRelay": "0",
            "OS": "Win2016 10.0.14393.1944 (1607)",
            "ComputerType": "Virtual"
        }
    }
}
Human Readable Output

image

6. Create an action to run on target computers


Create an action on BigFix that will run the given action from the given fixlet on target computers. The computerID parameter takes a comma-separated list of BigFix computer IDs. If no computers are specified, the action will be run on the default computers configured on BigFix. If the action should run on all computers set the computerID parameter to all.

Base Command
bigfix-deploy-patch
Input
Argument Name Description Required
site_name Name of the site. If the site is external or operator then the site must be specified Required
computer_ids Provide IDs of computers to deploy the patch. Pass 'all' to deploy to all the computers. Required
fixlet_id The Fixlet ID. To use the action script from the original Fixlet or Task Message. Required
action_id The action ID. The specified action will run on target computers. Required
 
Context Output
Path Type Description
Bigfix.Action.ID number Action ID
Bigfix.Action.Name string Action name
Bigfix.Action.SiteName string Site name
Bigfix.Action.ComputerIDs unknown Computers IDs the patch was applied to
Bigfix.Action.AllComputers boolean true if patch was applied to all the computers
Bigfix.Action.Resource string Link to action in BigFix
 
Command Example
!bigfix-deploy-patch site_name="BES Support" computer_ids="3385267" fixlet_id="1759" action_id="Action2"
Context Example
Human Readable Output

image

7. Get a patch by fixlet ID


Retrieves a patch (fixlet) by ID.

Base Command
bigfix-get-patch
Input
Argument Name Description Required
id Fixlet ID Required
site_type Type of the site ("external", "operator", "master", "custom" Required
site_name Name of the site. If the site is external or operator then site must be provided Optional
 
Context Output
Path Type Description
Bigfix.Patch.ID unknown Patch(fixlet) ID
Bigfix.Patch.Name unknown Patch name
Bigfix.Patch.Resource unknown Link (URL) to the patch
Bigfix.Patch.Description unknown Description
Bigfix.Patch.Relevance unknown Relevance of the patch requested
Bigfix.Patch.Category string Category of the patch requested
Bigfix.Patch.DownloadSize unknown Download size
Bigfix.Patch.Source unknown Source from where the patch is coming from
Bigfix.Patch.SourceID unknown Source ID of the patch requested
Bigfix.Patch.SourceSeverity unknown Source severity of the patch requested
Bigfix.Patch.SourceReleaseDate unknown Source release date of the patch requested
Bigfix.Patch.ActionID string Action ID of the patch requested
Bigfix.Patch.ActionScript string Action script of the patch requested
 
Command Example
!bigfix-get-patch id=38 site_type=master
Context Example
{
    "Bigfix": {
        "Patch": {
            "Category": null,
            "Resource": "https://xsoar-example:20021/api/fixlet/master/38",
            "Description": "This is a description of foo patch",
            "DownloadSize": null,
            "SourceID": null,
            "SourceReleaseDate": "2018-06-15",
            "Source": "Internal",
            "ActionID": "Action1",
            "ActionScript": {
                "@MIMEType": "application/x-sh",
                "#text": "#!/bin/sh\n# Enter your action script here\necho \"Hello World\""
            },
            "Relevance": null,
            "SourceSeverity": null,
            "ID": "38",
            "Name": "Anar Fixlet"
        }
    }
}
Human Readable Output

image

8. Delete an action


Stops and deletes the specified action. Note: You cannot delete actions that are members of a Multiple Action Group. This note applies to HCL BigFix V9.2 and later.

Base Command
bigfix-action-delete
Input
Argument Name Description Required
action_id Action ID Required
 
Context Output

There is no context output for this command.

Command Example
!bigfix-action-delete action_id
Human Readable Output

image

9. Get the status of an action


Gets the status of an action against its targets.

Base Command
bigfix-action-status
Input
Argument Name Description Required
action_id Action ID Required
 
Context Output
Path Type Description
Bigfix.Action.ID string Action ID
Bigfix.Action.Status string Action status (e.g., "Open", "Stopped")
 
Command Example
!bigfix-action-status action_id=56
Context Example
{
    "Bigfix": {
        "Action": {
            "Status": "Open",
            "ID": "56"
        }
    }
}
Human Readable Output

image

10. Stop an action


Stops the specified action.

Base Command
bigfix-action-stop
Input
Argument Name Description Required
action_id Action ID Required
 
Context Output

There is no context output for this command.

Command Example
!bigfix-action-stop action_id
Human Readable Output

image

11. Evaluate an expression and get the result


Evaluates an expression and gets the result. The request is processed through the server to WebReports.

Base Command
bigfix-query
Input
Argument Name Description Required
relevance Relevance query (example: names of bes computers) Required
 
Context Output
Path Type Description
Bigfix.QueryResults unknown The results of the query
 
Command Example
!bigfix-query relevance="cpus of bes computers"
!bigfix-query relevance="names of bes computers"
Context Example
{
    "Bigfix": {
        "QueryResults": [
            "2200 MHz Xeon Gold 5120",
            "2200 MHz Xeon"
        ]
    }
}
Human Readable Output

image

Configuration parameters

  • url — Server url (e.g https://192.168.10.1:52311) (required)
  • credentials — Username (required)
  • unsecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (11)

  • bigfix-action-delete

    Stops and deletes the specified action. Note: You cannot delete actions that are members of a Multiple Action Group. This note applies to HCL BigFix V9.2 and later.

  • bigfix-action-status

    Gets the status of an action against it's targets.

  • bigfix-action-stop

    Stops the specified action.

  • bigfix-deploy-patch

    Create an action on BigFix that will run the given action from the given fixlet on target computers. The computerID parameter takes a comma-separated list of BigFix computer IDs. If no computers are given, the action will be run on the default computers configured on BigFix. If the action should run on all computers set the computerID parameter to all.

  • bigfix-get-endpoint

    Retrieve endpoint (computer) details.

  • bigfix-get-endpoints

    Retrieve all the endpoints (computers).

  • bigfix-get-patch

    Retrieve patch (fixlet) by id.

  • bigfix-get-patches

    Retrieve all the patches (fixlets) of site.

  • bigfix-get-site

    Retrieve single site by name and type.

  • bigfix-get-sites

    Retrieves all the sites.

  • bigfix-query

    Evaluate a relevance expression and get the result. This runs request is processed through the server to WebReports.

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401
import requests

import urllib3.util

urllib3.disable_warnings()

BASE_URL = demisto.params().get("url")
VERIFY_CERTIFICATE = not demisto.params().get("unsecure")

USERNAME = demisto.params()["credentials"]["identifier"]
PASSWORD = demisto.params()["credentials"]["password"]

handle_proxy(proxy_param_name="proxy")


def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default


def get_sites():
    fullurl = f"{BASE_URL}/api/sites"
    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get sites.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_sites = json.loads(xml2json(res.content))

    if not raw_sites or "BESAPI" not in raw_sites:
        return []

    sites = []
    master_sites = demisto.get(raw_sites, "BESAPI.ActionSite")

    if master_sites and not isinstance(master_sites, list):
        master_sites = [master_sites]
    if master_sites:
        for idx, _site in enumerate(master_sites):
            master_sites[idx]["Type"] = "master"
            master_sites[idx]["Resource"] = master_sites[idx]["@Resource"]
            del master_sites[idx]["@Resource"]
    else:
        master_sites = []

    external_sites = demisto.get(raw_sites, "BESAPI.ExternalSite")
    if external_sites and not isinstance(external_sites, list):
        external_sites = [external_sites]
    if external_sites:
        for idx, _site in enumerate(external_sites):
            external_sites[idx]["Type"] = "external"
            external_sites[idx]["Resource"] = external_sites[idx]["@Resource"]
            del external_sites[idx]["@Resource"]
    else:
        external_sites = []

    operator_sites = demisto.get(raw_sites, "BESAPI.OperatorSite")
    if operator_sites and not isinstance(operator_sites, list):
        operator_sites = [operator_sites]
    if operator_sites:
        for idx, _site in enumerate(operator_sites):
            operator_sites[idx]["Type"] = "operator"
            operator_sites[idx]["Resource"] = operator_sites[idx]["@Resource"]
            del operator_sites[idx]["@Resource"]
    else:
        operator_sites = []

    custom_sites = demisto.get(raw_sites, "BESAPI.CustomSite")
    if custom_sites and not isinstance(custom_sites, list):
        custom_sites = [custom_sites]

    if custom_sites:
        for idx, _site in enumerate(custom_sites):
            custom_sites[idx]["Type"] = "custom"
            custom_sites[idx]["Resource"] = custom_sites[idx]["@Resource"]
            del custom_sites[idx]["@Resource"]
    else:
        custom_sites = []

    sites = master_sites + external_sites + operator_sites + custom_sites
    for idx, site in enumerate(sites):
        site_details = get_site(site["Type"], site["Name"])
        sites[idx] = site_details

    return sites


def get_sites_command():
    sites = get_sites()
    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": sites,
            "HumanReadable": tableToMarkdown(
                "BigFix Sites", sites, ["Name", "Type", "GatherURL", "Description", "GlobalReadPermissions", "Subscription"]
            ),
            "EntryContext": {"Bigfix.Site(val.Resource==obj.Resource)": sites},
        }
    )


def get_site(site_type, site_name):
    fullurl = BASE_URL + "/api/site/" + site_type
    if site_type != "master":
        # if site name is not empty the add to url
        fullurl += "/" + site_name

    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get site {site_name}.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_site = json.loads(xml2json(res.content))

    if not raw_site or "BES" not in raw_site:
        return None

    site = None
    if site_type == "master":
        site = demisto.get(raw_site, "BES.ActionSite")
    elif site_type == "external":
        site = demisto.get(raw_site, "BES.ExternalSite")
    elif site_type == "custom":
        site = demisto.get(raw_site, "BES.CustomSite")
    elif site_type == "operator":
        site = demisto.get(raw_site, "BES.OperatorSite")

    if site is not None:
        site["Type"] = site_type
        site["Resource"] = BASE_URL + f"/api/site/{site_type}/{site_name}"

    return site


def get_site_command():
    site_name = demisto.args().get("site_name")
    site_type = demisto.args().get("site_type")
    site = get_site(site_type, site_name)

    if site is None:
        demisto.results("No site found")
        sys.exit(0)

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": site,
            "HumanReadable": tableToMarkdown(
                f"BigFix Site: {site_type} - {site_name}",
                [site],
                ["Name", "Type", "GatherURL", "Description", "GlobalReadPermissions", "Subscription"],
            ),
            "EntryContext": {"Bigfix.Site(val.Resource==obj.Resource)": site},
        }
    )


def get_endpoints(should_get_endpoint_details):
    fullurl = BASE_URL + "/api/computers"

    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get endpoints.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_endpoints = json.loads(xml2json(res.content))

    if not raw_endpoints or "BESAPI" not in raw_endpoints:
        return None

    raw_endpoints = demisto.get(raw_endpoints, "BESAPI.Computer")
    if raw_endpoints and not isinstance(raw_endpoints, list):
        raw_endpoints = [raw_endpoints]

    for idx, _endpoint in enumerate(raw_endpoints):
        raw_endpoints[idx]["Resource"] = raw_endpoints[idx]["@Resource"]
        del raw_endpoints[idx]["@Resource"]

    if should_get_endpoint_details:
        endpoints_with_details = []
        for raw_endpoint in raw_endpoints:
            endpoint = get_endpoint_details(raw_endpoint.get("ID"))
            endpoints_with_details.append(endpoint)
        return endpoints_with_details
    else:
        return raw_endpoints


def get_endpoints_command():
    should_get_endpoint_details = demisto.args().get("get_endpoint_details") == "true"
    endpoints = get_endpoints(should_get_endpoint_details)
    headers = ["ID", "Resource", "LastReportTime"]
    if should_get_endpoint_details:
        headers.extend(
            [
                "ActiveDirectoryPath",
                "AgentType",
                "AgentVersion",
                "BESRelaySelectionMethod",
                "BESRelayServiceInstalled",
                "BESRootServer",
                "BIOS",
                "CPU",
                "ClientSettings",
                "ComputerName",
                "ComputerType",
                "DNSName",
                "DeviceType",
                "DistancetoBESRelay",
                "FreeSpaceonSystemDrive",
                "IPAddress",
                "LicenseType",
                "Locked",
                "OS",
                "RAM",
                "Relay",
                "RelayNameOfClient",
                "SubnetAddress",
                "SubscribedSites",
                "TotalSizeofSystemDrive",
                "UserName",
            ]
        )
    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": endpoints,
            "HumanReadable": tableToMarkdown("BigFix Computers", endpoints, headers=headers),
            "EntryContext": {"Bigfix.Endpoint(val.ID==obj.ID)": endpoints},
        }
    )


def get_endpoint_details(computer_id):
    fullurl = BASE_URL + f"/api/computer/{computer_id}"
    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get computer {computer_id}.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_endpoint = json.loads(xml2json(res.content))
    if not raw_endpoint or "BESAPI" not in raw_endpoint:
        return None

    raw_endpoint = demisto.get(raw_endpoint, "BESAPI.Computer")

    endpoint = {
        "ID": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "ID")=val["#text"]')),
        "Resource": demisto.get(raw_endpoint, "@Resource"),
        "LastReportTime": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Last Report Time")=val["#text"]')),
        "ActiveDirectoryPath": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Active Directory Path")=val["#text"]')
        ),
        "AgentType": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Type")=val["#text"]')),
        "AgentVersion": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Agent Version")=val["#text"]')),
        "BESRelaySelectionMethod": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')
        ),
        "BESRelayServiceInstalled": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Relay Selection Method")=val["#text"]')
        ),
        "BESRootServer": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BES Root Server")=val["#text"]')),
        "BIOS": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "BIOS")=val["#text"]')),
        "CPU": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "CPU")=val["#text"]')),
        "ClientSettings": demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Client Settings")=val["#text"]'),
        "ComputerName": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Name")=val["#text"]')),
        "ComputerType": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Computer Type")=val["#text"]')),
        "DNSName": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "DNS Name")=val["#text"]')),
        "IPAddress": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "IP Address")=val["#text"]')),
        "DeviceType": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Device Type")=val["#text"]')),
        "DistancetoBESRelay": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Distance to BES Relay")=val["#text"]')
        ),
        "FreeSpaceonSystemDrive": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Free Space on System Drive")=val["#text"]')
        ),
        "LicenseType": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "License Type")=val["#text"]')),
        "Locked": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Locked")=val["#text"]')),
        "OS": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "OS")=val["#text"]')),
        "RAM": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "RAM")=val["#text"]')),
        "Relay": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay")=val["#text"]')),
        "RelayNameOfClient": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Relay Name of Client")=val["#text"]')),
        "SubnetAddress": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subnet Address")=val["#text"]')),
        "SubscribedSites": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Subscribed Sites")=val["#text"]')),
        "TotalSizeofSystemDrive": get_first(
            demisto.dt(raw_endpoint, 'Property(val["@Name"] == "Total Size of System Drive")=val["#text"]')
        ),
        "UserName": get_first(demisto.dt(raw_endpoint, 'Property(val["@Name"] == "User Name")=val["#text"]')),
    }

    return endpoint


def get_endpoint_details_command():
    computer_id = demisto.args().get("computer_id")

    endpoint = get_endpoint_details(computer_id)
    if endpoint is None:
        demisto.results(f"Endpoint with id {computer_id} was not found")
        sys.exit(0)

    markdown = tableToMarkdown(
        f"BigFix Endpoint {computer_id}",
        [endpoint],
        headers=[
            "ID",
            "Resource",
            "LastReportTime",
            "ActiveDirectoryPath",
            "AgentType",
            "AgentVersion",
            "BESRelaySelectionMethod",
            "BESRelayServiceInstalled",
            "BESRootServer",
            "BIOS",
            "CPU",
            "ClientSettings",
            "ComputerName",
            "ComputerType",
            "DNSName",
            "DeviceType",
            "DistancetoBESRelay",
            "FreeSpaceonSystemDrive",
            "IPAddress",
            "LicenseType",
            "Locked",
            "OS",
            "RAM",
            "Relay",
            "RelayNameOfClient",
            "SubnetAddress",
            "SubscribedSites",
            "TotalSizeofSystemDrive",
            "UserName",
        ],
    )

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": endpoint,
            "HumanReadable": markdown,
            "EntryContext": {"Bigfix.Endpoint(val.ID==obj.ID)": endpoint},
        }
    )


def get_patches(site_type="", site_name=""):
    fullurl = BASE_URL + f"/api/fixlets/{site_type}"
    if site_type != "master":
        # if site name is not empty the add to url
        fullurl += "/" + site_name

    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get patches. Request URL: {fullurl}\n"  # type: ignore[str-bytes-safe]
            f"StatusCode: {res.status_code}\n"  # type: ignore[str-bytes-safe]
            f"Response Body: {res.content}"  # type: ignore[str-bytes-safe]
        )

    raw_patches = json.loads(xml2json(res.content))
    if not raw_patches or "BESAPI" not in raw_patches:
        return None

    raw_patches = demisto.get(raw_patches, "BESAPI.Fixlet")
    if raw_patches and not isinstance(raw_patches, list):
        raw_patches = [raw_patches]

    patches_with_details = []
    for raw_patch in raw_patches:
        patch = get_patch_details(site_type, site_name, raw_patch.get("ID"))
        patch["LastModified"] = raw_patch["@LastModified"]
        patches_with_details.append(patch)

    return patches_with_details


def get_patches_command():
    site_name = demisto.args().get("site_name")
    site_type = demisto.args().get("site_type")
    patches = get_patches(site_type, site_name)

    markdown = tableToMarkdown(
        "BigFix Patches",
        patches,
        headers=[
            "ID",
            "Name",
            "Description",
            "LastModified",
            "Resource",
            "Relevance",
            "Category",
            "DownloadSize",
            "Source",
            "SourceID",
            "SourceReleaseDate",
            "SourceSeverity",
            "ActionID",
            "ActionScript",
        ],
    )

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": patches,
            "HumanReadable": markdown,
            "EntryContext": {"Bigfix.Patch(val.ID==obj.ID)": patches},
        }
    )


def get_patch_details(site_type, site_name, patch_id):
    if site_type == "master":
        fullurl = BASE_URL + f"/api/fixlet/master/{patch_id}"
    else:
        fullurl = BASE_URL + f"/api/fixlet/{site_type}/{site_name}/{patch_id}"

    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get patch/fixlet {patch_id}. Request URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_patch = json.loads(xml2json(res.content))
    if not raw_patch or "BES" not in raw_patch:
        return None

    raw_patch = demisto.get(raw_patch, "BES.Fixlet")
    patch = {
        "ID": patch_id,
        "Name": demisto.get(raw_patch, "Title"),
        "Resource": fullurl,
        "Description": demisto.get(raw_patch, "Description"),
        "Relevance": demisto.get(raw_patch, "Relevance"),
        "Category": demisto.get(raw_patch, "Category"),
        "DownloadSize": demisto.get(raw_patch, "DownloadSize"),
        "Source": demisto.get(raw_patch, "Source"),
        "SourceID": demisto.get(raw_patch, "SourceID"),
        "SourceReleaseDate": demisto.get(raw_patch, "SourceReleaseDate"),
        "SourceSeverity": demisto.get(raw_patch, "SourceSeverity"),
        "ActionID": demisto.get(raw_patch, "DefaultAction.@ID"),
        "ActionScript": demisto.get(raw_patch, "DefaultAction.ActionScript"),
    }

    return patch


def get_patch_details_command():
    site_type = demisto.args().get("site_type")
    site_name = demisto.args().get("site_name")
    patch_id = demisto.args().get("id")

    patch = get_patch_details(site_type, site_name, patch_id)
    markdown = tableToMarkdown(
        f"BigFix Patch {patch_id}",
        [patch],
        headers=[
            "ID",
            "Name",
            "Resource",
            "Description",
            "Relevance",
            "Category",
            "DownloadSize",
            "Source",
            "SourceID",
            "SourceReleaseDate",
            "SourceSeverity",
            "ActionID",
            "ActionScript",
        ],
    )

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": patch,
            "HumanReadable": markdown,
            "EntryContext": {"Bigfix.Patch(val.ID==obj.ID)": patch},
        }
    )


def deploy_patch(site_name, computer_ids, fixlet_id, action_id):
    if "all" in computer_ids:
        target = "<AllComputers>true</AllComputers>"
    else:
        target = "\n".join([f"<ComputerID>{computer_id}</ComputerID>" for computer_id in computer_ids])

    request_body = f"""<?xml version="1.0" encoding="UTF-8"?>
    <BES xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="BES.xsd">
     <SourcedFixletAction>
       <SourceFixlet>
         <Sitename>{site_name}</Sitename>
         <FixletID>{fixlet_id}</FixletID>
         <Action>{action_id}</Action>
       </SourceFixlet>
       <Target>
         {target}
       </Target>
      <Parameter Name="_BESClient_EMsg_Detail">1000</Parameter>
     </SourcedFixletAction>
    </BES>
    """
    LOG(f"deploy_patch - request: {request_body}")

    fullurl = BASE_URL + "/api/actions"
    res = requests.post(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE, data=request_body)

    LOG(f"deploy_patch - raw response: {res.content.decode('utf-8', errors='replace')}")
    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to deploy patch {fixlet_id}.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_action = json.loads(xml2json(res.content))
    if not raw_action or "BESAPI" not in raw_action:
        return None

    raw_action = demisto.get(raw_action, "BESAPI.Action")
    raw_action["FixletID"] = fixlet_id
    raw_action["ComputerIDs"] = computer_ids
    raw_action["SiteName"] = site_name
    raw_action["Resource"] = raw_action["@Resource"]
    del raw_action["@Resource"]
    if "all" in computer_ids:
        raw_action["AllComputers"] = True
        del raw_action["ComputerIDs"]

    return raw_action


def deploy_patch_command():
    site_name = demisto.args().get("site_name")
    computer_ids = argToList(demisto.args().get("computer_ids"))

    fixlet_id = demisto.args().get("fixlet_id")
    action_id = demisto.args().get("action_id")

    action = deploy_patch(site_name, computer_ids, fixlet_id, action_id)

    markdown = tableToMarkdown(
        f"BigFix Action {action_id}", [action], headers=["ID", "Name", "FixletID", "ComputerIDs", "SiteName", "Resource"]
    )

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": action,
            "HumanReadable": markdown,
            "EntryContext": {"Bigfix.Action(val.ID==obj.ID)": action},
        }
    )


def action_delete(action_id):
    fullurl = BASE_URL + "/api/action/" + action_id
    res = requests.delete(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to delete action {action_id}.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )


def action_delete_command():
    action_id = demisto.args().get("action_id")

    action_delete(action_id)

    demisto.results(f"Action {action_id} was deleted successfully")


def get_action_status(action_id):
    fullurl = BASE_URL + "/api/action/" + action_id + "/status"
    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to get action {action_id} status.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_action = json.loads(xml2json(res.content))
    if not raw_action or "BESAPI" not in raw_action:
        return None

    raw_action = demisto.get(raw_action, "BESAPI.ActionResults")
    return raw_action.get("Status")


def get_action_status_command():
    action_id = demisto.args().get("action_id")

    status = get_action_status(action_id)

    output = {"ID": action_id, "Status": status}
    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": output,
            "HumanReadable": f"Action {action_id} status is {status}",
            "EntryContext": {"Bigfix.Action(val.ID==obj.ID)": output},
        }
    )


def action_stop(action_id):
    fullurl = BASE_URL + "/api/action/" + action_id + "/stop"
    res = requests.post(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Failed to stop action {action_id}.\nRequest URL: {fullurl}"
            f"\nStatusCode: {res.status_code}\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )


def action_stop_command():
    action_id = demisto.args().get("action_id")

    action_stop(action_id)

    demisto.results(f"Action {action_id} was stopped successfully")


def query(relevance):
    fullurl = BASE_URL + "/api/query"
    params = {"relevance": relevance}
    res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE, params=params)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(
            f"Query failed.\nRequest URL: {fullurl}\nStatusCode: {res.status_code}"
            f"\nResponse Body: {res.content.decode('utf-8', errors='replace')}"
        )

    raw_action = json.loads(xml2json(res.content))
    if not raw_action or "BESAPI" not in raw_action:
        demisto.info(f"BigFix query has incorrect response format. Response Body: {res.content}")  # type: ignore[str-bytes-safe]
        return_error("The response has incorrect format. Check the logs")

    if demisto.get(raw_action, "BESAPI.Query.Error"):
        error = demisto.get(raw_action, "BESAPI.Query.Error")
        return_error(error)

    raw_query_results = demisto.get(raw_action, "BESAPI.Query")
    return raw_query_results


def query_command():
    relevance = demisto.args().get("relevance")
    results = query(relevance)

    if results is None:
        demisto.results("No results")
        sys.exit(0)

    output = demisto.dt(results, "Result.Answer.#text")
    if not isinstance(output, list):
        output = [output]

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["json"],
            "Contents": results,
            "HumanReadable": tableToMarkdown(f"Query Results: {relevance}", output, ["Results"]),
            "EntryContext": {"Bigfix.QueryResults": output},
        }
    )


try:
    # do requets to /api/help
    # should be good indicator for test connectivity
    def test():
        fullurl = BASE_URL + "/api/help"
        res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE)
        res.raise_for_status()

    if demisto.command() == "test-module":
        # do requests to /api/help
        # should be good indicator for test connectivity
        test()
        demisto.results("ok")

    elif demisto.command() == "bigfix-get-sites":
        get_sites_command()

    elif demisto.command() == "bigfix-get-site":
        get_site_command()

    elif demisto.command() == "bigfix-get-endpoints":
        get_endpoints_command()

    elif demisto.command() == "bigfix-get-endpoint":
        get_endpoint_details_command()

    elif demisto.command() == "bigfix-get-patches":
        get_patches_command()

    elif demisto.command() == "bigfix-get-patch":
        get_patch_details_command()

    elif demisto.command() == "bigfix-deploy-patch":
        deploy_patch_command()

    elif demisto.command() == "bigfix-action-delete":
        action_delete_command()

    elif demisto.command() == "bigfix-action-status":
        get_action_status_command()

    elif demisto.command() == "bigfix-action-stop":
        action_stop_command()

    elif demisto.command() == "bigfix-query":
        query_command()

except Exception as e:
    LOG(e)
    LOG.print_log()
    return_error(e)