You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.5 KiB
228 lines
8.5 KiB
#!/usr/bin/env python
|
|
|
|
import sys
|
|
import os
|
|
import requests
|
|
import time
|
|
import json
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
# Debug log file
|
|
DEBUG_LOG = "/opt/splunk/var/log/socradar/status_update_log.txt"
|
|
|
|
def log_debug(msg):
|
|
os.makedirs(os.path.dirname(DEBUG_LOG), exist_ok=True)
|
|
with open(DEBUG_LOG, 'a') as f:
|
|
f.write(f"[{datetime.now()}] {msg}\n")
|
|
|
|
# Simple version that outputs CSV directly
|
|
print("alarm_id,status,result,message,time")
|
|
|
|
try:
|
|
log_debug("=== NEW STATUS UPDATE REQUEST ===")
|
|
|
|
# Read all input
|
|
input_lines = []
|
|
for line in sys.stdin:
|
|
input_lines.append(line.strip())
|
|
|
|
log_debug(f"Input lines: {input_lines}")
|
|
|
|
# Parse alarm_id and new_status from input
|
|
alarm_id = ""
|
|
new_status = ""
|
|
|
|
for line in input_lines:
|
|
if "alarm_id=" in line:
|
|
parts = line.split(',')
|
|
for part in parts:
|
|
if 'alarm_id=' in part:
|
|
alarm_id = part.split('=', 1)[1].strip().strip('"')
|
|
elif 'new_status=' in part:
|
|
new_status = part.split('=', 1)[1].strip().strip('"')
|
|
|
|
log_debug(f"Parsed: alarm_id={alarm_id}, new_status={new_status}")
|
|
|
|
if not alarm_id:
|
|
print("NONE,No alarm selected,WAITING,Enter an alarm ID," + str(int(time.time())))
|
|
sys.exit(0)
|
|
|
|
# Get credentials
|
|
conf_file = "/opt/splunk/etc/apps/TA-socradar-incidents/local/inputs.conf"
|
|
company_id = None
|
|
api_key = None
|
|
|
|
if os.path.exists(conf_file):
|
|
with open(conf_file, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line.startswith('socradar_company_id'):
|
|
company_id = line.split('=', 1)[1].strip()
|
|
elif line.startswith('socradar_api_key'):
|
|
api_key = line.split('=', 1)[1].strip()
|
|
|
|
if not company_id or not api_key:
|
|
log_debug("Missing credentials")
|
|
print(f"{alarm_id},Error,ERROR,Missing credentials," + str(int(time.time())))
|
|
sys.exit(0)
|
|
|
|
# Status mapping
|
|
status_map = {
|
|
'0': 'Open',
|
|
'1': 'OnHold-Investigating',
|
|
'2': 'Closed-Resolved',
|
|
'3': 'OnHold-Pending',
|
|
'5': 'OnHold-Legal',
|
|
'9': 'Closed-FalsePositive',
|
|
'10': 'Closed-Duplicate',
|
|
'11': 'Closed-ProcessedInternally',
|
|
'12': 'Closed-Mitigated',
|
|
'13': 'Closed-NotApplicable'
|
|
}
|
|
|
|
status_name = status_map.get(new_status, f'Status-{new_status}')
|
|
|
|
# Make API call
|
|
url = f"https://platform.socradar.com/api/company/{company_id}/alarms/status/change?key={api_key}"
|
|
payload = {
|
|
"status": new_status,
|
|
"alarm_ids": alarm_id,
|
|
"comments": f"Updated via Splunk to: {status_name}"
|
|
}
|
|
|
|
log_debug(f"Calling API: {url[:50]}...")
|
|
log_debug(f"Payload: {payload}")
|
|
|
|
response = requests.post(url, json=payload, headers={"Content-Type": "application/json"}, timeout=30)
|
|
response_data = response.json()
|
|
|
|
log_debug(f"API Response: {response.status_code} - {response_data}")
|
|
|
|
if response.status_code == 200 and response_data.get('is_success', False):
|
|
print(f"{alarm_id},{status_name},SUCCESS,Updated successfully," + str(int(time.time())))
|
|
|
|
# Map status to SOCRadar API format
|
|
api_status_map = {
|
|
'Open': 'OPEN',
|
|
'OnHold-Investigating': 'INVESTIGATING',
|
|
'Closed-Resolved': 'RESOLVED',
|
|
'OnHold-Pending': 'PENDING_INFO',
|
|
'OnHold-Legal': 'LEGAL',
|
|
'Closed-FalsePositive': 'FALSE_POSITIVE',
|
|
'Closed-Duplicate': 'DUPLICATE',
|
|
'Closed-ProcessedInternally': 'PROCESSED_INTERNALLY',
|
|
'Closed-Mitigated': 'MITIGATED',
|
|
'Closed-NotApplicable': 'NOT_APPLICABLE'
|
|
}
|
|
|
|
api_status = api_status_map.get(status_name, status_name)
|
|
|
|
# Update checkpoint
|
|
try:
|
|
checkpoint_file = "/opt/splunk/var/lib/splunk/modinputs/socradar_incidents_collector/deneme_002_socradar_v4_processed_alarms"
|
|
|
|
if os.path.exists(checkpoint_file):
|
|
with open(checkpoint_file, 'r') as f:
|
|
checkpoint_data = json.load(f)
|
|
|
|
if 'alarm_status' in checkpoint_data:
|
|
checkpoint_data['alarm_status'][str(alarm_id)] = api_status
|
|
checkpoint_data['last_updated'] = datetime.now().isoformat()
|
|
|
|
with open(checkpoint_file, 'w') as f:
|
|
json.dump(checkpoint_data, f)
|
|
log_debug(f"Updated checkpoint: {alarm_id} -> {api_status}")
|
|
except Exception as e:
|
|
log_debug(f"Checkpoint update error: {e}")
|
|
|
|
# Index the status change immediately using REST API
|
|
try:
|
|
log_debug("Attempting to index status change...")
|
|
|
|
# Create event
|
|
event = {
|
|
"time": time.time(),
|
|
"host": "socradar",
|
|
"source": "status_update",
|
|
"sourcetype": "socradar:incidents",
|
|
"index": "socradar_incidents",
|
|
"event": json.dumps({
|
|
"alarm_id": alarm_id,
|
|
"status": api_status,
|
|
"alarm_status": api_status,
|
|
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"update_source": "manual_status_update",
|
|
"_time": time.time()
|
|
})
|
|
}
|
|
|
|
# Use Splunk REST API to index
|
|
import urllib.request
|
|
import urllib.parse
|
|
import base64
|
|
|
|
# Use HTTP Event Collector endpoint with index specification
|
|
url = "https://localhost:8089/services/receivers/simple?index=socradar_incidents&sourcetype=socradar:incidents"
|
|
data = event['event']
|
|
|
|
# Create request
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=data.encode('utf-8'),
|
|
headers={
|
|
'Authorization': 'Basic ' + base64.b64encode(b'admin:123456789').decode('ascii'),
|
|
'Content-Type': 'application/json'
|
|
}
|
|
)
|
|
|
|
# Disable SSL verification for localhost
|
|
import ssl
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
# Send request
|
|
with urllib.request.urlopen(req, context=ctx) as response:
|
|
result = response.read().decode('utf-8')
|
|
log_debug(f"Index result: {result}")
|
|
|
|
except Exception as e:
|
|
log_debug(f"Indexing error: {e}")
|
|
|
|
# Fallback to oneshot
|
|
try:
|
|
# Get the full alarm data from collector's last run
|
|
event_data = {
|
|
"alarm_id": alarm_id,
|
|
"status": api_status,
|
|
"alarm_status": api_status,
|
|
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"update_source": "manual_status_update",
|
|
"_time": time.time(),
|
|
"alarm_type": "incident",
|
|
"alarm_risk_level": "UNKNOWN"
|
|
}
|
|
event_json = json.dumps(event_data)
|
|
|
|
cmd = ['/opt/splunk/bin/splunk', 'add', 'oneshot', '-',
|
|
'-sourcetype', 'socradar:incidents',
|
|
'-index', 'socradar_incidents',
|
|
'-auth', 'admin:123456789']
|
|
|
|
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
stdout, stderr = proc.communicate(input=event_json.encode())
|
|
log_debug(f"Oneshot result: {proc.returncode}, stdout: {stdout}, stderr: {stderr}")
|
|
except Exception as e2:
|
|
log_debug(f"Oneshot error: {e2}")
|
|
|
|
else:
|
|
msg = response_data.get('message', 'Update failed').replace(',', ';')
|
|
print(f"{alarm_id},{status_name},FAILED,{msg}," + str(int(time.time())))
|
|
log_debug(f"Update failed: {msg}")
|
|
|
|
except Exception as e:
|
|
log_debug(f"EXCEPTION: {e}")
|
|
print(f"ERROR,Error,ERROR,{str(e).replace(',', ';')}," + str(int(time.time()))) |