├── images ├── ossec_log.PNG ├── Email Banner.png ├── wazuh_hits.PNG ├── graylog_response.PNG ├── logo_purple_resize.png └── logo_orange.svg ├── LICENSE ├── README.md └── custom-socfortress.py /images/ossec_log.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/HEAD/images/ossec_log.PNG -------------------------------------------------------------------------------- /images/Email Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/HEAD/images/Email Banner.png -------------------------------------------------------------------------------- /images/wazuh_hits.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/HEAD/images/wazuh_hits.PNG -------------------------------------------------------------------------------- /images/graylog_response.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/HEAD/images/graylog_response.PNG -------------------------------------------------------------------------------- /images/logo_purple_resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/HEAD/images/logo_purple_resize.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 SOCFortress 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /images/logo_orange.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://www.socfortress.co/) 2 | 3 | # SOCFortress Threat Intel Integration [![Awesome](https://img.shields.io/badge/SOCFortress-Worlds%20First%20Free%20Cloud%20SOC-orange)](https://www.socfortress.co/trial.html) 4 | > Integrate your `Wazuh-Manager` or `Graylog` with the SOCFortress Threat Intel API to receive real-time threat intel. 5 | 6 | 7 | [![MIT License][license-shield]][license-url] 8 | [![LinkedIn][linkedin-shield]][linkedin-url] 9 | [![your-own-soc-free-for-life-tier](https://img.shields.io/badge/Get%20Started-Demo%20Walkthrough-orange)](https://youtu.be/2EMb6zYx7_E) 10 | 11 | 12 |
13 |
14 | 15 | Logo 16 | 17 | 18 |

SOCFortress Threat Intel API

19 | 20 |

21 | 💰 Make a Donation » 22 |
23 |
24 |

25 |
26 | 27 | 28 |
29 | Table of Contents 30 |
    31 |
  1. 32 | Threat Intel API 33 |
  2. 34 |
  3. 35 | Wazuh-Manager Integration 36 |
  4. 37 |
  5. 38 | Graylog Integration 39 |
  6. 40 |
41 |
42 | 43 | 44 | 45 | 46 | # Threat Intel API 47 | > The SOCFortress Threat Intel API allows end users to consume SOCFortress's public threat intel. The integration supports both `Wazuh-Manager` and `Graylog`. 48 | 49 | ## API-KEY 50 | > The API key is required to authenticate with the API. To obtain an API key, please use SOCFortress Copilot. 51 | 52 | ## Criteria 53 | > The API is currently **only** built for the following criteria: 54 | * `Windows Sysmon` - Follow our [Wazuh Agent Install Guide](https://medium.com/@socfortress/part-4-wazuh-agent-install-endpoint-monitoring-f24f6a0464ac) to integrate Sysmon with your Windows endpoints. 55 | * `SOCFortress Wazuh Detection Rules` - Follow our [Wazuh Rules Install Guide](https://github.com/socfortress/Wazuh-Rules) to integrate SOCFortress's Wazuh detection rules with your Wazuh-Manager. 56 | * `IoC Type` - The API currently supports IoC types of `IP`, `Domain`, and `SHA256 Hash`. 57 | * `Valid API Key` - Request via [our website](https://www.socfortress.co/request_threat_intel_api.html). 58 | 59 | > ⚠ **NOTE:** API quotas are currently restricted to `500` requests per day. The API is currently in beta and is subject to change. Please contact us at [helpdesk.socfortress.co](https://servicedesk.socfortress.co/help/2979687893) if you have any questions or concerns. 60 | 61 | * `SOCFortress API Wazuh Rules` - [200980-socfortress.xml](https://raw.githubusercontent.com/socfortress/Wazuh-Rules/main/SOCFortress%20API/200980-socfortress.xml) - **NOT REQUIRED IF INTEGRATING WITH GRAYLOG** 62 | 63 | 64 | # Wazuh-Manager Integration 65 | **Not Recommended - Use Graylog Instead If You Can - Graylog's built in Caching will save your API quota** 66 | > Follow the steps below to integrate the SOCFortress Threat Intel API with your Wazuh-Manager. **NOT REQUIRED IF INTEGRATING WITH GRAYLOG** 67 | 1. Download the `custom-socfortress.py` file from the GitHub repository and copy it to `/var/ossec/integrations` of your `Wazuh-Manager`. 68 | 69 | ``` 70 | # Download the custom-socfortress.py file from the GitHub repository 71 | curl -o custom-socfortress.py https://raw.githubusercontent.com/socfortress/SOCFortress-Threat-Intel/main/custom-socfortress.py 72 | 73 | # Copy the custom-socfortress.py file to /var/ossec/integrations 74 | sudo cp custom-socfortress.py /var/ossec/integrations 75 | 76 | # Change ownership to root:wazuh 77 | sudo chown root:wazuh /var/ossec/integrations/custom-socfortress.py 78 | 79 | # Set permissions to -rwxr-x--- 80 | sudo chmod 750 /var/ossec/integrations/custom-socfortress.py 81 | 82 | # Clean up the downloaded file 83 | rm custom-socfortress.py 84 | ``` 85 | 86 | 2. Edit the `/var/ossec/etc/ossec.conf` file and add the following lines to the `ossec.conf` file. 87 | 88 | ``` 89 | 90 | custom-socfortress.py 91 | YOUR_API_KEY 92 | sysmon_event3,sysmon_event_22 93 | json 94 | 95 | ``` 96 | > ⚠ **NOTE:** The `group` parameter is the name of the Wazuh rule groups that you want to integrate with the SOCFortress Threat Intel API. All of the below rule groups are supported: 97 | * `sysmon_event3` - Network Connections 98 | * `sysmon_event_22` - DNS Query 99 | * `sysmon_evnt1` - Process Creation 100 | * `sysmon_event6` - Remote Thread Creation 101 | * `sysmon_event7` - Raw Access Read 102 | * `sysmon_event_15` - File Creation Time 103 | 104 | **I only include the `sysmon_event3` and `sysmon_event_22` groups in the example above because the others will likely result in you hitting your API Limit quickly** 105 | 106 | 107 | > The `alert_format` parameter is the format of the alert that you want to receive from the SOCFortress Threat Intel API. The `api_key` parameter is the API key that you received from SOCFortress. 108 | 109 | 3. Restart the Wazuh-Manager service. 110 | 111 | ``` 112 | sudo systemctl restart wazuh-manager 113 | ``` 114 | 115 | 4. If you have any issues, set the `integrator_debug` to `2` in the `/var/ossec/etc/local_internal_options.conf` file and restart the Wazuh-Manager service. 116 | 117 | * Tail the `ossec.log` file and ensure you see valid responses from the SOCFortress Threat Intel API. `tail -f /var/ossec/logs/ossec.log | grep socfortress` 118 | 119 |
120 | 121 | Logo 122 | 123 | 124 |

Ossec.log File

125 |

126 |
127 | 128 | If working correctly, rule id `200983` will trigger when a positive IoC is found. 129 | 130 |
131 | 132 | Logo 133 | 134 | 135 |

SOCFortress Threat Intel Fields

136 |

137 |
138 | 139 | 140 | 141 | 142 | # Graylog Integration 143 | > Follow the steps below to integrate the SOCFortress Threat Intel API with your Graylog instance. 144 | 1. Create `SOCFortress Threat Intel` Data Adapter. 145 | * `Title` - SOCFortress Threat Intel 146 | * `Description` - SOCFortress Threat Intel 147 | * `Name` - socfortress-threat-intel 148 | * `Lookup URL` - https://intel.socfortress.co/search?value=${key} 149 | * `Single value JSONPath` - $.success 150 | * `Multi value JSONPath` - $.data 151 | * `HTTP Headers`- 152 | * `Content-Type` - application/json 153 | * `module-version` - 1.0 154 | * `x-api-key` - YOUR_API_KEY 155 | 156 | > ⚠ **NOTE:** Verify connection to the SOCFortress Threat Intel API. 157 | 158 |
159 | 160 | Logo 161 | 162 | 163 |

Graylog Response

164 |

165 |
166 | 167 | 2. Create `SOCFortress Threat Intel` Cache. 168 | * `Cache Type` - Node-local, in-memory cache 169 | * `Title` - SOCFortress Threat Intel Cache 170 | * `Description` - SOCFortress Threat Intel Cache 171 | * `Name` - socfortress-threat-intel-cache 172 | * `Maximum Entries` - 1000 173 | * `Expire after access` - 1 hour 174 | 3. Create `SOCFortress Threat Intel` Lookup Table. 175 | * `Title` - SOCFortress Threat Intel Lookup Table 176 | * `Description` - SOCFortress Threat Intel Lookup Table 177 | * `Name` - socfortress_threat_intel 178 | * `Data Adapter` - SOCFortress Threat Intel 179 | * `Cache` - SOCFortress Threat Intel Cache 180 | 4. Create Pipeline Rules to invoke the SOCFortress Threat Intel Lookup Table. 181 | 1. Sysmon Event 3 - Network Connections 182 | ``` 183 | rule "WINDOWS SYSMON EVENT 3 - SOCFortress THREAT INTEL" 184 | when 185 | $message.rule_group1 == "windows" AND $message.rule_group3 == "sysmon_event3" AND $message.data_win_eventdata_destinationIp != "127.0.0.1" AND $message.data_win_eventdata_destinationIp != "255.255.255.255" AND $message.data_win_eventdata_destinationIp != "0.0.0.0" AND $message.data_win_eventdata_destinationIsIpv6 == "false" AND ! in_private_net(to_string($message.data_win_eventdata_destinationIp)) 186 | then 187 | let ldata = lookup( 188 | lookup_table: "socfortress_threat_intel", 189 | key: to_string($message.data_win_eventdata_destinationIp) 190 | ); 191 | set_fields( 192 | fields: ldata, 193 | prefix: "socfortress_" 194 | ); 195 | end 196 | ``` 197 | 2. Sysmon Event 22 - DNS Query 198 | ``` 199 | rule "WINDOWS SYSMON EVENT 22 - SOCFortress THREAT INTEL" 200 | when 201 | $message.rule_group1 == "windows" AND $message.rule_group3 == "sysmon_event_22" 202 | then 203 | let ldata = lookup( 204 | lookup_table: "socfortress_threat_intel", 205 | key: to_string($message.data_win_eventdata_queryName) 206 | ); 207 | set_fields( 208 | fields: ldata, 209 | prefix: "socfortress_" 210 | ); 211 | end 212 | ``` 213 | > **NOTE:** I'll leave the other rule groups for you to create 😉 214 | 215 |
216 | 217 | 218 | # Contact 219 | 220 | SOCFortress - [![LinkedIn][linkedin-shield]][linkedin-url] - info@socfortress.co 221 | 222 |
223 |

Let SOCFortress Take Your Open Source SIEM to the Next Level

224 | 225 | Banner 226 | 227 | 228 | 229 |
230 | 231 | 232 | 233 | 234 | 235 | 236 | [contributors-shield]: https://img.shields.io/github/contributors/socfortress/Wazuh-Rules 237 | [contributors-url]: https://github.com/socfortress/Wazuh-Rules/graphs/contributors 238 | [forks-shield]: https://img.shields.io/github/forks/socfortress/Wazuh-Rules 239 | [forks-url]: https://github.com/socfortress/Wazuh-Rules/network/members 240 | [stars-shield]: https://img.shields.io/github/stars/socfortress/Wazuh-Rules 241 | [stars-url]: https://github.com/socfortress/Wazuh-Rules/stargazers 242 | [issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=for-the-badge 243 | [issues-url]: https://github.com/othneildrew/Best-README-Template/issues 244 | [license-shield]: https://img.shields.io/badge/Help%20Desk-Help%20Desk-blue 245 | [license-url]: https://servicedesk.socfortress.co/help/2979687893 246 | [linkedin-shield]: https://img.shields.io/badge/Visit%20Us-www.socfortress.co-orange 247 | [linkedin-url]: https://www.socfortress.co/ 248 | -------------------------------------------------------------------------------- /custom-socfortress.py: -------------------------------------------------------------------------------- 1 | #!/var/ossec/framework/python/bin/python3 2 | # Copyright (C) 2023, SOCFortress, LLP. 3 | import json 4 | import sys 5 | import time 6 | import os 7 | import ipaddress 8 | import re 9 | from socket import socket, AF_UNIX, SOCK_DGRAM 10 | try: 11 | import requests 12 | from requests.auth import HTTPBasicAuth 13 | except Exception as e: 14 | print("No module 'requests' found. Install: pip install requests") 15 | sys.exit(1) 16 | # Global vars 17 | debug_enabled = False 18 | pwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 19 | json_alert = {} 20 | now = time.strftime("%a %b %d %H:%M:%S %Z %Y") 21 | # Set paths 22 | log_file = '{0}/logs/integrations.log'.format(pwd) 23 | socket_addr = '{0}/queue/sockets/queue'.format(pwd) 24 | def main(args): 25 | debug("# Starting") 26 | # Read args 27 | alert_file_location = args[1] 28 | apikey = args[2] 29 | debug("# API Key") 30 | debug(apikey) 31 | debug("# File location") 32 | debug(alert_file_location) 33 | # Load alert. Parse JSON object. 34 | with open(alert_file_location) as alert_file: 35 | json_alert = json.load(alert_file) 36 | debug("# Processing alert") 37 | debug(json_alert) 38 | # Request SOCFortress info 39 | msg = request_socfortress_api(json_alert,apikey) 40 | # If positive match, send event to Wazuh Manager 41 | if msg: 42 | send_event(msg, json_alert["agent"]) 43 | 44 | def debug(msg): 45 | if debug_enabled: 46 | msg = "{0}: {1}\n".format(now, msg) 47 | print(msg) 48 | f = open(log_file,"a") 49 | f.write(msg) 50 | f.close() 51 | 52 | def collect_source_1(data): 53 | comment = data['comment'] 54 | value = data['value'] 55 | timestamp = data['timestamp'] 56 | if timestamp is None: 57 | timestamp = '1679526769' 58 | category = data['category'] 59 | type = data['type'] 60 | virustotal_url = data['virustotal_url'] 61 | return comment, value, timestamp, category, type, virustotal_url 62 | 63 | def collect_source_2(data): 64 | comment = data['comment'] 65 | value = data['value'] 66 | type = data['type'] 67 | last_seen = data['last_seen'] 68 | report_id = data['report_id'] 69 | report_url = data['report_url'] 70 | virustotal_url = data['virustotal_url'] 71 | return comment, value, type, last_seen, report_id, report_url, virustotal_url 72 | 73 | def collect_source_3(data): 74 | comment = data['comment'] 75 | value = data['value'] 76 | type = data['type'] 77 | last_seen = data['last_seen'] 78 | virustotal_url = data['virustotal_url'] 79 | return comment, value, type, last_seen, virustotal_url 80 | 81 | def is_ipv4(value): 82 | try: 83 | ipaddress.IPv4Address(value) 84 | debug(f"{value} is a valid IPv4 address.") 85 | return True 86 | except ipaddress.AddressValueError: 87 | debug(f"{value} is not a valid IPv4 address.") 88 | return False 89 | 90 | def is_domain(value): 91 | try: 92 | domain_regex = re.compile(r"^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$", re.IGNORECASE) 93 | if domain_regex.match(value): 94 | debug(f"{value} is a valid domain name.") 95 | return True 96 | else: 97 | debug(f"{value} is not a valid domain name.") 98 | return False 99 | except Exception as e: 100 | debug(f"Error: {e}") 101 | return False 102 | 103 | def ioc_source(data, ioc_value): 104 | result = data['ioc_source'] 105 | if result == '1': 106 | return True 107 | return False 108 | 109 | def has_report(data, ioc_value): 110 | if 'report_found' in data: 111 | return True 112 | return False 113 | 114 | def query_api(ioc_value, apikey): 115 | params = {'value': ioc_value,} 116 | headers = { 117 | 'Accept': 'application/json', 118 | 'Content-Type': 'application/json', 119 | "x-api-key": apikey, 120 | "module-version": "1.0", 121 | } 122 | response = requests.get('https://intel.socfortress.co/search', params=params, headers=headers) 123 | if response.status_code == 200: 124 | json_response = response.json() 125 | data = json_response.get('data') 126 | return data 127 | elif response.status_code == 403 or response.status_code == 429: 128 | json_response = response.json() 129 | data = json_response 130 | alert_output = {} 131 | alert_output["socfortress"] = {} 132 | alert_output["integration"] = "custom-socfortress" 133 | alert_output["socfortress"]["status_code"] = response.status_code 134 | alert_output["socfortress"]["message"] = json_response['message'] 135 | send_event(alert_output) 136 | exit(0) 137 | else: 138 | alert_output = {} 139 | alert_output["socfortress"] = {} 140 | alert_output["integration"] = "custom-socfortress" 141 | json_response = response.json() 142 | debug("# Error: The SOCFortress integration encountered an error") 143 | alert_output["socfortress"]["status_code"] = response.status_code 144 | alert_output["socfortress"]["message"] = json_response['error'] 145 | send_event(alert_output) 146 | exit(0) 147 | 148 | def request_socfortress_api(alert, apikey): 149 | alert_output = {} 150 | # Collect the IoC Type - Currently only Supports SYSMON For Windows 151 | event_source = alert["rule"]["groups"][0] 152 | if 'windows' in event_source.lower(): 153 | if 'hashes' in alert["data"]["win"]["eventdata"]: 154 | ## Regex Pattern used based on SHA256 lenght (64 characters) 155 | regex_file_hash = re.compile('\w{64}') 156 | ioc_value = regex_file_hash.search(alert["data"]["win"]["eventdata"]["hashes"]).group(0) 157 | data = query_api(ioc_value, apikey) 158 | elif 'destinationIp' in alert["data"]["win"]["eventdata"]: 159 | ioc_value = alert["data"]["win"]["eventdata"]["destinationIp"] 160 | ipv4 = is_ipv4(ioc_value) 161 | if ipv4 and ipaddress.ip_address(ioc_value).is_global: 162 | data = query_api(ioc_value, apikey) 163 | else: 164 | return(0) 165 | elif 'queryName' in alert["data"]["win"]["eventdata"]: 166 | ioc_value = alert["data"]["win"]["eventdata"]["queryName"] 167 | domain = is_domain(ioc_value) 168 | if domain: 169 | data = query_api(ioc_value, apikey) 170 | else: 171 | return(0) 172 | else: 173 | return(0) 174 | else: 175 | return(0) 176 | # Create alert 177 | alert_output["socfortress"] = {} 178 | alert_output["integration"] = "custom-socfortress" 179 | alert_output["socfortress"]["found"] = 0 180 | alert_output["socfortress"]["source"] = {} 181 | alert_output["socfortress"]["source"]["alert_id"] = alert["id"] 182 | alert_output["socfortress"]["source"]["agent_name"] = alert["agent"]["name"] 183 | alert_output["socfortress"]["source"]["rule"] = alert["rule"]["id"] 184 | alert_output["socfortress"]["source"]["description"] = alert["rule"]["description"] 185 | alert_output["socfortress"]["source"]["processGuid"] = alert["data"]["win"]["eventdata"]["processGuid"] 186 | alert_output["socfortress"]["source"]["ioc_value"] = ioc_value 187 | ioc_value = ioc_value 188 | # Check if SOCFortress has any info about the IoC 189 | if ioc_source(data, ioc_value): 190 | alert_output["socfortress"]["ioc_source"] = 1 191 | else: 192 | alert_output["socfortress"]["ioc_source"] = 2 193 | # Check if SOCFortress has any reports about the IoC 194 | report_found = has_report(data, ioc_value) 195 | if report_found: 196 | alert_output["socfortress"]["report_found"] = 1 197 | else: 198 | alert_output["socfortress"]["report_found"] = 0 199 | if alert_output["socfortress"]["ioc_source"] == 2 and alert_output["socfortress"]["report_found"] == 1: 200 | comment, value, type, last_seen, report_id, report_url, virustotal_url = collect_source_2(data) 201 | # Populate JSON Output with SOCFortress results 202 | alert_output["socfortress"]["status_code"] = 200 203 | alert_output["socfortress"]["comment"] = comment 204 | alert_output["socfortress"]["value"] = value 205 | alert_output["socfortress"]["last_seen"] = last_seen 206 | alert_output["socfortress"]["type"] = type 207 | alert_output["socfortress"]["report_id"] = report_id 208 | alert_output["socfortress"]["report_url"] = report_url 209 | alert_output["socfortress"]["virustotal_url"] = virustotal_url 210 | 211 | if alert_output["socfortress"]["ioc_source"] == 2 and alert_output["socfortress"]["report_found"] == 0: 212 | comment, value, type, last_seen, virustotal_url = collect_source_3(data) 213 | alert_output["socfortress"]["status_code"] = 200 214 | alert_output["socfortress"]["comment"] = comment 215 | alert_output["socfortress"]["value"] = value 216 | alert_output["socfortress"]["last_seen"] = last_seen 217 | alert_output["socfortress"]["type"] = type 218 | alert_output["socfortress"]["virustotal_url"] = virustotal_url 219 | 220 | # Info about the IoC found in SOCFortress 221 | if alert_output["socfortress"]["ioc_source"] == 1: 222 | comment, value, timestamp, category, type, virustotal_url = collect_source_1(data) 223 | # Populate JSON Output object with SOCFortress results 224 | alert_output["socfortress"]["status_code"] = 200 225 | alert_output["socfortress"]["comment"] = comment 226 | alert_output["socfortress"]["value"] = value 227 | alert_output["socfortress"]["timestamp"] = timestamp 228 | alert_output["socfortress"]["category"] = category 229 | alert_output["socfortress"]["type"] = type 230 | alert_output["socfortress"]["virustotal_url"] = virustotal_url 231 | 232 | debug(alert_output) 233 | return(alert_output) 234 | 235 | def send_event(msg, agent = None): 236 | if not agent or agent["id"] == "000": 237 | string = '1:socfortress:{0}'.format(json.dumps(msg)) 238 | else: 239 | string = '1:[{0}] ({1}) {2}->socfortress:{3}'.format(agent["id"], agent["name"], agent["ip"] if "ip" in agent else "any", json.dumps(msg)) 240 | debug(string) 241 | sock = socket(AF_UNIX, SOCK_DGRAM) 242 | sock.connect(socket_addr) 243 | sock.send(string.encode()) 244 | sock.close() 245 | 246 | if __name__ == "__main__": 247 | try: 248 | # Read arguments 249 | bad_arguments = False 250 | if len(sys.argv) >= 4: 251 | msg = '{0} {1} {2} {3} {4}'.format(now, sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] if len(sys.argv) > 4 else '') 252 | debug_enabled = (len(sys.argv) > 4 and sys.argv[4] == 'debug') 253 | else: 254 | msg = '{0} Wrong arguments'.format(now) 255 | bad_arguments = True 256 | # Logging the call 257 | f = open(log_file, 'a') 258 | f.write(msg +'\n') 259 | f.close() 260 | if bad_arguments: 261 | debug("# Exiting: Bad arguments.") 262 | sys.exit(1) 263 | # Main function 264 | main(sys.argv) 265 | except Exception as e: 266 | debug(str(e)) 267 | raise 268 | --------------------------------------------------------------------------------