FIXES: Added wazuh integration and loads more
This commit is contained in:
+31
-16
@@ -606,11 +606,14 @@ class AppBase:
|
||||
if "len" in thistype or "length" in thistype or "lenght" in thistype:
|
||||
tmp = ""
|
||||
try:
|
||||
tmpdata = data.replace("\'", "\"")
|
||||
tmp = json.loads(tmpdata)
|
||||
except:
|
||||
print("Passing bug")
|
||||
pass
|
||||
try:
|
||||
tmpdata = data.replace("\'", "\"")
|
||||
tmp = json.loads(tmpdata)
|
||||
except:
|
||||
print("[ERROR] Parsing bug for length in app sdk")
|
||||
pass
|
||||
|
||||
if isinstance(tmp, list):
|
||||
return len(tmp)
|
||||
@@ -654,9 +657,9 @@ class AppBase:
|
||||
def parse_wrapper(data):
|
||||
try:
|
||||
if "(" not in data or ")" not in data:
|
||||
return data
|
||||
return (data, False)
|
||||
except TypeError:
|
||||
return data
|
||||
return (data, False)
|
||||
|
||||
#print("Running %s" % data)
|
||||
|
||||
@@ -671,7 +674,7 @@ class AppBase:
|
||||
break
|
||||
|
||||
if not found:
|
||||
return data
|
||||
return (data, False)
|
||||
|
||||
# Do stuff here.
|
||||
innervalue = parse_nested_param(data, maxDepth(data)-0)
|
||||
@@ -692,10 +695,11 @@ class AppBase:
|
||||
|
||||
parsed_value = parse_type(innervalue[0], thistype.lower())
|
||||
print("Parsed value from %s: %s" % (thistype, parsed_value))
|
||||
return parsed_value
|
||||
return (parsed_value, True)
|
||||
|
||||
print("DATA: %s\n" % data)
|
||||
return parse_wrapper(data)
|
||||
return (parse_wrapper(data)[0], True)
|
||||
|
||||
|
||||
# Looks for parantheses to grab special cases within a string, e.g:
|
||||
# int(1) lower(HELLO) or length(what's the length)
|
||||
@@ -737,15 +741,20 @@ class AppBase:
|
||||
if len(newstring) > 0:
|
||||
newdata.append(newstring)
|
||||
|
||||
print("Newdata: ", newdata)
|
||||
parsedlist = []
|
||||
non_string = False
|
||||
parsed = False
|
||||
for item in newdata:
|
||||
ret = parse_wrapper(item)
|
||||
if not isinstance(ret, str):
|
||||
if not isinstance(ret[0], str):
|
||||
non_string = True
|
||||
|
||||
parsedlist.append(ret)
|
||||
parsedlist.append(ret[0])
|
||||
if ret[1]:
|
||||
parsed = True
|
||||
|
||||
if not parsed:
|
||||
return data
|
||||
|
||||
if len(parsedlist) > 0 and not non_string:
|
||||
print("Returning parsed list: ", parsedlist)
|
||||
@@ -949,7 +958,6 @@ class AppBase:
|
||||
if len(parsersplit) == 1:
|
||||
return str(baseresult)+str(appendresult), False
|
||||
|
||||
baseresult = baseresult.replace("\'", "\"")
|
||||
baseresult = baseresult.replace(" True,", " true,")
|
||||
baseresult = baseresult.replace(" False", " false,")
|
||||
|
||||
@@ -958,8 +966,12 @@ class AppBase:
|
||||
try:
|
||||
basejson = json.loads(baseresult)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print("Parser issue with JSON: %s" % e)
|
||||
return str(baseresult)+str(appendresult), False
|
||||
try:
|
||||
baseresult = baseresult.replace("\'", "\"")
|
||||
basejson = json.loads(baseresult)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print("Parser issue with JSON: %s" % e)
|
||||
return str(baseresult)+str(appendresult), False
|
||||
|
||||
print("After fourth parser return as JSON")
|
||||
|
||||
@@ -1365,14 +1377,17 @@ class AppBase:
|
||||
if replacement.startswith("\"") and replacement.endswith("\""):
|
||||
replacement = replacement[1:len(replacement)-1]
|
||||
|
||||
replacement = replacement.replace("\'", "\"", -1)
|
||||
print("POST replacement: %s" % replacement)
|
||||
|
||||
json_replacement = replacement
|
||||
try:
|
||||
json_replacement = json.loads(replacement)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print("JSON error singular: %s" % e)
|
||||
try:
|
||||
replacement = replacement.replace("\'", "\"", -1)
|
||||
json_replacement = json.loads(replacement)
|
||||
except:
|
||||
print("JSON error singular: %s" % e)
|
||||
|
||||
if len(json_replacement) > minlength:
|
||||
minlength = len(json_replacement)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
NAME=shuffle-app_sdk
|
||||
VERSION=0.8.32
|
||||
VERSION=0.8.4
|
||||
|
||||
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
|
||||
docker build . -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
|
||||
|
||||
@@ -230,6 +230,7 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin
|
||||
}
|
||||
|
||||
// Build the actual image
|
||||
log.Printf("Building %s. This may take up to a few minutes.", dockerfileFolder)
|
||||
imageBuildResponse, err := client.ImageBuild(
|
||||
ctx,
|
||||
dockerFileTarReader,
|
||||
|
||||
@@ -2483,7 +2483,7 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) {
|
||||
if len(users) != 1 {
|
||||
log.Printf(`Found multiple or no users with the same username: %s: %d`, data.Username, len(users))
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Found %d users with the same username: %s"}`, len(users), data.Username)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %d users with username %s"}`, len(users), data.Username)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4785,7 +4785,7 @@ func getDocList(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
if len(item1) == 0 {
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No docs available."`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No docs available."}`)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4844,7 +4844,7 @@ func getDocs(resp http.ResponseWriter, request *http.Request) {
|
||||
location := strings.Split(request.URL.String(), "/")
|
||||
if len(location) != 5 {
|
||||
resp.WriteHeader(404)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"}`)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4868,7 +4868,7 @@ func getDocs(resp http.ResponseWriter, request *http.Request) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"}`)))
|
||||
resp.WriteHeader(404)
|
||||
//setBadMemcache(ctx, docPath)
|
||||
return
|
||||
@@ -4877,7 +4877,7 @@ func getDocs(resp http.ResponseWriter, request *http.Request) {
|
||||
newresp, err := client.Do(req)
|
||||
if err != nil {
|
||||
resp.WriteHeader(404)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad path. Use e.g. /api/v1/docs/workflows.md"}`)))
|
||||
//setBadMemcache(ctx, docPath)
|
||||
return
|
||||
}
|
||||
@@ -4885,7 +4885,7 @@ func getDocs(resp http.ResponseWriter, request *http.Request) {
|
||||
body, err := ioutil.ReadAll(newresp.Body)
|
||||
if err != nil {
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't parse data"`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't parse data"}`)))
|
||||
//setBadMemcache(ctx, docPath)
|
||||
return
|
||||
}
|
||||
@@ -5958,7 +5958,7 @@ func echoOpenapiData(resp http.ResponseWriter, request *http.Request) {
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] URLbody error: %s", err)
|
||||
resp.WriteHeader(500)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't get data from selected uri"`)))
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Can't get data from selected uri"}`)))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4015,7 +4015,7 @@ func deleteWorkflowApp(resp http.ResponseWriter, request *http.Request) {
|
||||
log.Printf("ID: %s", fileId)
|
||||
app, err := getApp(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Error getting app %s: %s", app.Name, err)
|
||||
log.Printf("Error getting app (delete) %s: %s", fileId, err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
@@ -4169,7 +4169,7 @@ func getWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) {
|
||||
ctx := context.Background()
|
||||
app, err := getApp(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Error getting app: %s", app.Name)
|
||||
log.Printf("Error getting app (app config): %s", fileId)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
@@ -4438,7 +4438,7 @@ func updateWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) {
|
||||
ctx := context.Background()
|
||||
app, err := getApp(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Error getting app: %s (update app)", app.Name)
|
||||
log.Printf("Error getting app (update app): %s", fileId)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ version: '3'
|
||||
services:
|
||||
frontend:
|
||||
#build: ./frontend
|
||||
image: ghcr.io/frikky/shuffle-frontend:0.8.4
|
||||
image: ghcr.io/frikky/shuffle-frontend:0.8.42
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
- database
|
||||
orborus:
|
||||
#build: ./functions/onprem/orborus
|
||||
image: ghcr.io/frikky/shuffle-orborus:0.8.31
|
||||
image: ghcr.io/frikky/shuffle-orborus:0.8.32
|
||||
container_name: shuffle-orborus
|
||||
hostname: shuffle-orborus
|
||||
networks:
|
||||
|
||||
@@ -470,6 +470,33 @@ const Admin = (props) => {
|
||||
})
|
||||
}
|
||||
|
||||
const flushQueue = (name) => {
|
||||
// Just use this one?
|
||||
const url = globalUrl + '/api/v1/flush_queue';
|
||||
fetch(url, {
|
||||
method: 'DELETE',
|
||||
credentials: "include",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(response =>
|
||||
response.json().then(responseJson => {
|
||||
if (responseJson["success"] === false) {
|
||||
alert.error(responseJson.reason)
|
||||
getEnvironments()
|
||||
} else {
|
||||
setLoginInfo("")
|
||||
setModalOpen(false)
|
||||
getEnvironments()
|
||||
}
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
console.log("Error when deleting: ", error)
|
||||
})
|
||||
}
|
||||
|
||||
const deleteEnvironment = (name) => {
|
||||
// FIXME - add some check here ROFL
|
||||
alert.info("Deleting environment " + name)
|
||||
@@ -2094,6 +2121,7 @@ const Admin = (props) => {
|
||||
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||
>
|
||||
<Button disabled={environment.archived} variant="outlined" style={{borderRadius: "0px"}} onClick={() => deleteEnvironment(environment.Name)} color="primary">Archive</Button>
|
||||
{/*<Button disabled={environment.archived} variant="outlined" style={{borderRadius: "0px"}} onClick={() => flushQueue(environment.Name)} color="primary">Flush Queue</Button>*/}
|
||||
</ListItemText>
|
||||
<ListItemText
|
||||
style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}}
|
||||
|
||||
@@ -192,7 +192,7 @@ const AngularWorkflow = (props) => {
|
||||
const cloudSyncEnabled = props.userdata !== undefined && props.userdata.active_org !== null && props.userdata.active_org !== undefined ? props.userdata.active_org.cloud_sync === true : false
|
||||
//const triggerEnvironments = cloudSyncEnabled ? ["cloud", "onprem"] : environments
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
||||
const triggerEnvironments = isCloud ? ["cloud"] : ["cloud", "onprem"]
|
||||
const triggerEnvironments = isCloud ? ["cloud"] : ["onprem", "cloud"]
|
||||
|
||||
const unloadText = 'Are you sure you want to leave without saving (CTRL+S)?'
|
||||
useBeforeunload(() => {
|
||||
@@ -2663,7 +2663,6 @@ const AngularWorkflow = (props) => {
|
||||
foundResult.result = foundResult.result.split(" None").join(" \"None\"")
|
||||
foundResult.result = foundResult.result.split(" False").join(" false")
|
||||
foundResult.result = foundResult.result.split(" True").join(" true")
|
||||
foundResult.result = foundResult.result.split("\'").join("\"")
|
||||
|
||||
var jsonvalid = true
|
||||
try {
|
||||
@@ -2672,7 +2671,15 @@ const AngularWorkflow = (props) => {
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
jsonvalid = false
|
||||
try {
|
||||
foundResult.result = foundResult.result.split("\'").join("\"")
|
||||
const tmp = String(JSON.parse(foundResult.result))
|
||||
if (!foundResult.result.includes("{") && !foundResult.result.includes("[")) {
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
jsonvalid = false
|
||||
}
|
||||
}
|
||||
|
||||
// Finds the FIRST json only
|
||||
@@ -5874,7 +5881,6 @@ const AngularWorkflow = (props) => {
|
||||
const parsedExecutionArgument = () => {
|
||||
var showResult = executionData.execution_argument.trim()
|
||||
showResult = showResult.split(" None").join(" \"None\"")
|
||||
showResult = showResult.split("\'").join("\"")
|
||||
showResult = showResult.split(" False").join(" false")
|
||||
showResult = showResult.split(" True").join(" true")
|
||||
|
||||
@@ -5885,7 +5891,16 @@ const AngularWorkflow = (props) => {
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
jsonvalid = false
|
||||
showResult = showResult.split("\'").join("\"")
|
||||
|
||||
try {
|
||||
const tmp = String(JSON.parse(showResult))
|
||||
if (!showResult.includes("{") && !showResult.includes("[")) {
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
jsonvalid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonvalid) {
|
||||
@@ -6070,21 +6085,28 @@ const AngularWorkflow = (props) => {
|
||||
//
|
||||
// FIXME: The latter replace doens't really work if ' is used in a string
|
||||
var showResult = data.result.trim()
|
||||
//console.log(showResult)
|
||||
showResult = showResult.split(" None").join(" \"None\"")
|
||||
showResult = showResult.split(" False").join(" false")
|
||||
showResult = showResult.split(" True").join(" true")
|
||||
showResult = showResult.split("\'").join("\"")
|
||||
|
||||
var jsonvalid = true
|
||||
try {
|
||||
const tmp = String(JSON.parse(showResult))
|
||||
if (!showResult.includes("{") && !showResult.includes("[")) {
|
||||
//console.log("IN HERE: ", tmp)
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
//console.log("Error: ", e)
|
||||
jsonvalid = false
|
||||
showResult = showResult.split("\'").join("\"")
|
||||
|
||||
try {
|
||||
const tmp = String(JSON.parse(showResult))
|
||||
if (!showResult.includes("{") && !showResult.includes("[")) {
|
||||
jsonvalid = false
|
||||
}
|
||||
} catch (e) {
|
||||
jsonvalid = false
|
||||
}
|
||||
}
|
||||
|
||||
const curapp = apps.find(a => a.name === data.action.app_name && a.app_version === data.action.app_version)
|
||||
|
||||
@@ -46,6 +46,10 @@ const inputColor = "#383B40"
|
||||
export const GetParsedPaths = (inputdata, basekey) => {
|
||||
const splitkey = " > "
|
||||
var parsedValues = []
|
||||
if (inputdata === undefined || inputdata === null) {
|
||||
return parsedValues
|
||||
}
|
||||
|
||||
if (typeof(inputdata) !== "object") {
|
||||
return parsedValues
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ const LoginDialog = props => {
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setLoginInfo("Error in userdata: ", error)
|
||||
setLoginInfo("Error logging in: ", error)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ const LoginDialog = props => {
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
setLoginInfo("")
|
||||
// FIXME - add some check here ROFL
|
||||
|
||||
// Just use this one?
|
||||
@@ -114,7 +115,7 @@ const LoginDialog = props => {
|
||||
}),
|
||||
)
|
||||
.catch(error => {
|
||||
setLoginInfo("Error in userdata: " + error)
|
||||
setLoginInfo("Error logging in: " + error)
|
||||
});
|
||||
} else {
|
||||
url = baseurl + '/api/v1/users/register';
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# Created by Shuffle, AS. <frikky@shuffler.io>.
|
||||
|
||||
WPYTHON_BIN="framework/python/bin/python3"
|
||||
|
||||
SCRIPT_PATH_NAME="$0"
|
||||
|
||||
DIR_NAME="$(cd $(dirname ${SCRIPT_PATH_NAME}); pwd -P)"
|
||||
SCRIPT_NAME="$(basename ${SCRIPT_PATH_NAME})"
|
||||
|
||||
case ${DIR_NAME} in
|
||||
*/active-response/bin | */wodles*)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/../..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
*/bin)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${WAZUH_PATH}/framework/scripts/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
*/integrations)
|
||||
if [ -z "${WAZUH_PATH}" ]; then
|
||||
WAZUH_PATH="$(cd ${DIR_NAME}/..; pwd)"
|
||||
fi
|
||||
|
||||
PYTHON_SCRIPT="${DIR_NAME}/${SCRIPT_NAME}.py"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
${WAZUH_PATH}/${WPYTHON_BIN} ${PYTHON_SCRIPT} "$@"
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python
|
||||
# Created by Shuffle, AS. <frikky@shuffler.io>.
|
||||
# Based on the Slack integration using Webhooks
|
||||
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
|
||||
try:
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
except Exception as e:
|
||||
print("No module 'requests' found. Install: pip install requests")
|
||||
sys.exit(1)
|
||||
|
||||
# ADD THIS TO ossec.conf configuration:
|
||||
# <integration>
|
||||
# <name>custom-shuffle</name>
|
||||
# <hook_url>http://<IP>:3001/api/v1/hooks/<HOOK_ID></hook_url>
|
||||
# <level>3</level>
|
||||
# <alert_format>json</alert_format>
|
||||
# </integration>
|
||||
|
||||
# Global vars
|
||||
|
||||
debug_enabled = False
|
||||
pwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
json_alert = {}
|
||||
now = time.strftime("%a %b %d %H:%M:%S %Z %Y")
|
||||
|
||||
# Set paths
|
||||
log_file = '{0}/logs/integrations.log'.format(pwd)
|
||||
|
||||
|
||||
def main(args):
|
||||
debug("# Starting")
|
||||
|
||||
# Read args
|
||||
alert_file_location = args[1]
|
||||
webhook = args[3]
|
||||
|
||||
debug("# Webhook")
|
||||
debug(webhook)
|
||||
|
||||
debug("# File location")
|
||||
debug(alert_file_location)
|
||||
|
||||
# Load alert. Parse JSON object.
|
||||
with open(alert_file_location) as alert_file:
|
||||
json_alert = json.load(alert_file)
|
||||
debug("# Processing alert")
|
||||
debug(json_alert)
|
||||
|
||||
debug("# Generating message")
|
||||
msg = generate_msg(json_alert)
|
||||
if isinstance(msg, str):
|
||||
if len(msg) == 0:
|
||||
return
|
||||
debug(msg)
|
||||
|
||||
debug("# Sending message")
|
||||
send_msg(msg, webhook)
|
||||
|
||||
|
||||
def debug(msg):
|
||||
if debug_enabled:
|
||||
msg = "{0}: {1}\n".format(now, msg)
|
||||
print(msg)
|
||||
f = open(log_file, "a")
|
||||
f.write(msg)
|
||||
f.close()
|
||||
|
||||
# Skips container kills to stop self-recursion
|
||||
def filter_msg(alert):
|
||||
# These are things that recursively happen because Shuffle starts Docker containers
|
||||
# Docker integration rules: https://github.com/wazuh/wazuh-ruleset/blob/ae36745db1d3f312db0392f5925c2f2b0ec009a9/rules/0560-docker_integration_rules.xml
|
||||
skip = ["87924", "87900", "87901", "87902", "87903", "87904", "86001", "86002", "86003", "87932", "80710", "87929", "87928",]
|
||||
if alert["rule"]["id"] in skip:
|
||||
return False
|
||||
|
||||
#try:
|
||||
# if "docker" in alert["rule"]["description"].lower() and "
|
||||
#msg['text'] = alert.get('full_log')
|
||||
#except:
|
||||
# pass
|
||||
#msg['title'] = alert['rule']['description'] if 'description' in alert['rule'] else "N/A"
|
||||
|
||||
return True
|
||||
|
||||
def generate_msg(alert):
|
||||
if not filter_msg(alert):
|
||||
print("Skipping rule %s" % alert["rule"]["id"])
|
||||
return ""
|
||||
|
||||
level = alert['rule']['level']
|
||||
|
||||
if (level <= 4):
|
||||
color = "good"
|
||||
elif (level >= 5 and level <= 7):
|
||||
color = "warning"
|
||||
else:
|
||||
color = "danger"
|
||||
|
||||
msg = {}
|
||||
msg['color'] = color
|
||||
msg['pretext'] = "WAZUH Alert"
|
||||
msg['title'] = alert['rule']['description'] if 'description' in alert['rule'] else "N/A"
|
||||
msg['text'] = alert.get('full_log')
|
||||
msg['rule_id'] = alert["rule"]["id"]
|
||||
msg['timestamp'] = alert["timestamp"]
|
||||
msg['id'] = alert['id']
|
||||
msg["all_fields"] = alert
|
||||
|
||||
#msg['fields'] = []
|
||||
# msg['fields'].append({
|
||||
# "title": "Agent",
|
||||
# "value": "({0}) - {1}".format(
|
||||
# alert['agent']['id'],
|
||||
# alert['agent']['name']
|
||||
# ),
|
||||
# })
|
||||
#if 'agentless' in alert:
|
||||
# msg['fields'].append({
|
||||
# "title": "Agentless Host",
|
||||
# "value": alert['agentless']['host'],
|
||||
# })
|
||||
|
||||
#msg['fields'].append({"title": "Location", "value": alert['location']})
|
||||
#msg['fields'].append({
|
||||
# "title": "Rule ID",
|
||||
# "value": "{0} _(Level {1})_".format(alert['rule']['id'], level),
|
||||
#})
|
||||
|
||||
#attach = {'attachments': [msg]}
|
||||
|
||||
return json.dumps(msg)
|
||||
|
||||
|
||||
def send_msg(msg, url):
|
||||
headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'}
|
||||
res = requests.post(url, data=msg, headers=headers)
|
||||
debug(res)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
# Read arguments
|
||||
bad_arguments = False
|
||||
if len(sys.argv) >= 4:
|
||||
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 '',
|
||||
)
|
||||
debug_enabled = (len(sys.argv) > 4 and sys.argv[4] == 'debug')
|
||||
else:
|
||||
msg = '{0} Wrong arguments'.format(now)
|
||||
bad_arguments = True
|
||||
|
||||
# Logging the call
|
||||
f = open(log_file, 'a')
|
||||
f.write(msg + '\n')
|
||||
f.close()
|
||||
|
||||
if bad_arguments:
|
||||
debug("# Exiting: Bad arguments.")
|
||||
sys.exit(1)
|
||||
|
||||
# Main function
|
||||
main(sys.argv)
|
||||
|
||||
except Exception as e:
|
||||
debug(str(e))
|
||||
raise
|
||||
@@ -1,7 +1,5 @@
|
||||
<integration>
|
||||
<name>Shuffle</name>
|
||||
<name>custom-shuffle</name>
|
||||
<hook_url>http://<IP>:3001/api/v1/hooks/webhook_<HOOK_ID></hook_url>
|
||||
<level>2</level>
|
||||
<group>multiple_drops|authentication_failures</group>
|
||||
<alert_format>json</alert_format>
|
||||
</integration>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
requests
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Based on
|
||||
# https://wazuh.com/blog/how-to-integrate-external-software-using-integrator/
|
||||
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
# Set the project attributes
|
||||
project_alias = 'TI'
|
||||
issue_name ='FIM'
|
||||
|
||||
# Read configuration parameters
|
||||
alert_file = open(sys.argv[1])
|
||||
user = sys.argv[2].split(':')[0]
|
||||
api_key = sys.argv[2].split(':')[1]
|
||||
hook_url = sys.argv[3]
|
||||
|
||||
# Read the alert file
|
||||
alert_json = json.loads(alert_file.read())
|
||||
alert_file.close()
|
||||
|
||||
# Extract issue fields
|
||||
alert_level = alert_json['rule']['level']
|
||||
description = alert_json['rule']['description']
|
||||
path = alert_json['syscheck']['path']
|
||||
|
||||
# Generate request
|
||||
msg_data = {}
|
||||
msg_data['fields'] = {}
|
||||
msg_data['fields']['project'] = {}
|
||||
msg_data['fields']['project']['key'] = project_alias
|
||||
msg_data['fields']['summary'] = 'FIM alert on [' + path + ']'
|
||||
msg_data['fields']['description'] = '- State: ' + description + '\n- Alert level: ' + str(alert_level)
|
||||
msg_data['fields']['issuetype'] = {}
|
||||
msg_data['fields']['issuetype']['name'] = issue_name
|
||||
headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'}
|
||||
|
||||
# Send the request
|
||||
requests.post(hook_url, data=json.dumps(msg_data), headers=headers, auth=(user, api_key))
|
||||
|
||||
sys.exit(0)
|
||||
@@ -1,5 +1,5 @@
|
||||
NAME=shuffle-orborus
|
||||
VERSION=0.8.31
|
||||
VERSION=0.8.32
|
||||
|
||||
echo "Running docker build with $NAME:$VERSION"
|
||||
#docker rmi frikky/shuffle:$NAME --force
|
||||
|
||||
@@ -562,6 +562,7 @@ func zombiecheck(workerTimeout int) error {
|
||||
|
||||
stopContainers := []string{}
|
||||
removeContainers := []string{}
|
||||
log.Printf("Workertimeout: %d", int64(workerTimeout))
|
||||
for _, container := range containers {
|
||||
// Skip random containers. Only handle things related to Shuffle.
|
||||
if !strings.Contains(container.Image, baseimagename) {
|
||||
@@ -587,10 +588,10 @@ func zombiecheck(workerTimeout int) error {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[INFO] NAME: %s", name)
|
||||
currenttime := time.Now().Unix()
|
||||
log.Printf("[INFO] (%s) NAME: %s. TIME: %d", container.State, name, currenttime-container.Created)
|
||||
|
||||
// Need to check time here too because a container can be removed the same instant as its created
|
||||
currenttime := time.Now().Unix()
|
||||
if container.State != "running" && currenttime-container.Created > int64(workerTimeout) {
|
||||
removeContainers = append(removeContainers, container.ID)
|
||||
containerNames[container.ID] = name
|
||||
@@ -606,6 +607,7 @@ func zombiecheck(workerTimeout int) error {
|
||||
}
|
||||
|
||||
// FIXME - add killing of apps with same execution ID too
|
||||
log.Printf("[INFO] Should STOP %d containers.", len(stopContainers))
|
||||
for _, containername := range stopContainers {
|
||||
log.Printf("[INFO] Stopping and removing container %s", containerNames[containername])
|
||||
go dockercli.ContainerStop(ctx, containername, nil)
|
||||
@@ -617,6 +619,7 @@ func zombiecheck(workerTimeout int) error {
|
||||
Force: true,
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Should REMOVE %d containers.", len(removeContainers))
|
||||
for _, containername := range removeContainers {
|
||||
go dockercli.ContainerRemove(ctx, containername, removeOptions)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user