From 3e88fe35d70ae80b4aaf8521e04fd7fac2eaf3b7 Mon Sep 17 00:00:00 2001
From: Marco Caselli
Date: Tue, 22 Jun 2021 08:18:56 +0200
Subject: [PATCH 001/428] new: update to handle network connection objects
---
app/Lib/Export/NidsExport.php | 235 +++++++++++++++++++++++++---------
1 file changed, 172 insertions(+), 63 deletions(-)
diff --git a/app/Lib/Export/NidsExport.php b/app/Lib/Export/NidsExport.php
index 2b691314d..dd881693f 100644
--- a/app/Lib/Export/NidsExport.php
+++ b/app/Lib/Export/NidsExport.php
@@ -16,11 +16,14 @@ class NidsExport
'fields' => array('threat_level_id')
)
),
- 'flatten' => 1
+ #'flatten' => 1
);
public function handler($data, $options = array())
{
+
+ //NOTES: Here the scope "Object" should be probably checked
+
$continue = empty($format);
$this->checkWhitelist = false;
if ($options['scope'] === 'Attribute') {
@@ -38,15 +41,16 @@ class NidsExport
$this->__convertFromEventFormat($data['Attribute'], $data, $options, $continue);
}
if (!empty($data['Object'])) {
- foreach ($data['Object'] as $object) {
- $this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
- }
+ #foreach ($data['Object'] as $object) {
+ $this->__convertFromEventFormatObject($data['Object'], $data, $options, $continue);
+ #}
}
}
return '';
}
private function __convertFromEventFormat($attributes, $event, $options = array(), $continue = false) {
+
$rearranged = array();
foreach ($attributes as $attribute) {
$attributeTag = array();
@@ -69,6 +73,44 @@ class NidsExport
return true;
}
+
+ private function __convertFromEventFormatObject($objects, $event, $options = array(), $continue = false) {
+
+ #CakeLog::debug("ConvertFromEventFormatObject");
+ #CakeLog::debug(json_encode($event));
+
+ $rearranged = array();
+ foreach ($objects as $object) {
+
+ $objectTag = array();
+
+ foreach($object['Attribute'] as $attribute) {
+
+ if (!empty($attribute['AttributeTag'])) {
+ $objectTag = array_merge($objectTag, $attribute['AttributeTag']);
+ unset($attribute['AttributeTag']);
+ }
+
+ }
+
+ $rearranged[] = array(
+ 'Attribute' => $object, //NOTES: Using 'Attribute' instead of 'Object' to comply with function export
+ 'AttributeTag' => $objectTag, //NOTES: Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
+ 'Event' => $event['Event']
+ );
+
+ }
+
+ $this->export(
+ $rearranged,
+ $options['user']['nids_sid'],
+ $options['returnFormat'],
+ $continue
+
+ );
+ return true;
+
+ }
public function header($options = array())
{
@@ -142,68 +184,135 @@ class NidsExport
$sid = $startSid + ($item['Attribute']['id'] * 10); // leave 9 possible rules per attribute type
$sid++;
- switch ($item['Attribute']['type']) {
- // LATER nids - test all the snort attributes
- // LATER nids - add the tag keyword in the rules to capture network traffic
- // LATER nids - sanitize every $attribute['value'] to not conflict with snort
- case 'ip-dst':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-dst|port':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src|port':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-src':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-dst':
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-subject':
- $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-attachment':
- $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain':
- $this->domainRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain|ip':
- $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'hostname':
- $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'url':
- $this->urlRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'user-agent':
- $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3-fingerprint-md5':
- $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
- $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'snort':
- $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
- // no break
- default:
- break;
- }
+
+ if(!empty($item['Attribute']['type'])) { //NOTES: Item is an 'Attribute'
+
+ switch ($item['Attribute']['type']) {
+ // LATER nids - test all the snort attributes
+ // LATER nids - add the tag keyword in the rules to capture network traffic
+ // LATER nids - sanitize every $attribute['value'] to not conflict with snort
+ case 'ip-dst':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-dst|port':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src|port':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-src':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-dst':
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-subject':
+ $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-attachment':
+ $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain':
+ $this->domainRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain|ip':
+ $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'hostname':
+ $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'url':
+ $this->urlRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'user-agent':
+ $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3-fingerprint-md5':
+ $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
+ $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'snort':
+ $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
+ // no break
+ default:
+ break;
+ }
+
+ } else if(!empty($item['Attribute']['name'])) { //NOTES: Item is an 'Object'
+
+ switch ($item['Attribute']['name']) {
+ case 'network-connection':
+ $this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ default:
+ break;
+ }
+
+ }
+
}
return $this->rules;
}
+
+ public function networkConnectionRule($ruleFormat, $object, &$sid)
+ {
+
+ $attributes = NidsExport::getObjectAttributes($object);
+
+ if(!array_key_exists('layer4-protocol', $attributes)){
+ $attributes['layer4-protocol'] = 'IP'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('IP')
+ }
+ if(!array_key_exists('ip-src', $attributes)){
+ $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
+ }
+ if(!array_key_exists('ip-dst', $attributes)){
+ $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
+ }
+ if(!array_key_exists('src-port', $attributes)){
+ $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
+ }
+ if(!array_key_exists('dst-port', $attributes)){
+ $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
+ }
+
+ $this->rules[] = sprintf(
+ $ruleFormat,
+ false,
+ $attributes['layer4-protocol'], // proto
+ $attributes['ip-src'], // src_ip
+ $attributes['src-port'], // src_port
+ '->', // direction
+ $attributes['ip-dst'], // dst_ip
+ $attributes['dst-port'], // dst_port
+ 'Network connection between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
+ );
+
+ }
+
+ public static function getObjectAttributes($object)
+ {
+
+ $attributes = array();
+
+ foreach ($object['Attribute'] as $attribute) {
+ $attributes[$attribute['object_relation']] = $attribute['value'];
+ }
+
+ return $attributes;
+ }
public function domainIpRule($ruleFormat, $attribute, &$sid)
{
From e71e46c11845f62c73f7cb0d79c2fdf77dae7522 Mon Sep 17 00:00:00 2001
From: Marco Caselli
Date: Wed, 15 Sep 2021 12:34:26 +0200
Subject: [PATCH 002/428] fixes + ddos object handling
---
app/Lib/Export/NidsExport.php | 80 +++++++++++++++++++++++++++++------
1 file changed, 67 insertions(+), 13 deletions(-)
diff --git a/app/Lib/Export/NidsExport.php b/app/Lib/Export/NidsExport.php
index dd881693f..20dcff784 100644
--- a/app/Lib/Export/NidsExport.php
+++ b/app/Lib/Export/NidsExport.php
@@ -7,6 +7,8 @@ class NidsExport
public $classtype = 'trojan-activity';
public $format = ""; // suricata (default), snort
+
+ public $supportedObjects = array('network-connection', 'ddos');
public $checkWhitelist = true;
@@ -82,22 +84,32 @@ class NidsExport
$rearranged = array();
foreach ($objects as $object) {
- $objectTag = array();
+ #CakeLog::debug("Checking Object");
- foreach($object['Attribute'] as $attribute) {
+ if(in_array($object['name'], $this->supportedObjects)){ //NOTES: Checking if this is an object supported for the custom export
- if (!empty($attribute['AttributeTag'])) {
- $objectTag = array_merge($objectTag, $attribute['AttributeTag']);
- unset($attribute['AttributeTag']);
+ $objectTag = array();
+
+ foreach($object['Attribute'] as $attribute) {
+
+ if (!empty($attribute['AttributeTag'])) {
+ $objectTag = array_merge($objectTag, $attribute['AttributeTag']);
+ unset($attribute['AttributeTag']);
+ }
+
}
- }
+ $rearranged[] = array(
+ 'Attribute' => $object, //NOTES: Using 'Attribute' instead of 'Object' to comply with function export
+ 'AttributeTag' => $objectTag, //NOTES: Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
+ 'Event' => $event['Event']
+ );
+
+ } else { //NOTES: In case the object is not supported for the custom export, the approach falls back to the attribute case
- $rearranged[] = array(
- 'Attribute' => $object, //NOTES: Using 'Attribute' instead of 'Object' to comply with function export
- 'AttributeTag' => $objectTag, //NOTES: Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
- 'Event' => $event['Event']
- );
+ $this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
+
+ }
}
@@ -253,6 +265,9 @@ class NidsExport
case 'network-connection':
$this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
break;
+ case 'ddos':
+ $this->ddosRule($ruleFormat, $item['Attribute'], $sid);
+ break;
default:
break;
}
@@ -269,7 +284,7 @@ class NidsExport
$attributes = NidsExport::getObjectAttributes($object);
if(!array_key_exists('layer4-protocol', $attributes)){
- $attributes['layer4-protocol'] = 'IP'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('IP')
+ $attributes['layer4-protocol'] = 'ip'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('ip')
}
if(!array_key_exists('ip-src', $attributes)){
$attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
@@ -302,6 +317,45 @@ class NidsExport
}
+ public function ddosRule($ruleFormat, $object, &$sid)
+ {
+
+ $attributes = NidsExport::getObjectAttributes($object);
+
+ if(!array_key_exists('protocol', $attributes)){
+ $attributes['protocol'] = 'ip'; // If protocol is unknown, we roll-back to 'ip'
+ }
+ if(!array_key_exists('ip-src', $attributes)){
+ $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
+ }
+ if(!array_key_exists('ip-dst', $attributes)){
+ $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
+ }
+ if(!array_key_exists('src-port', $attributes)){
+ $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
+ }
+ if(!array_key_exists('dst-port', $attributes)){
+ $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
+ }
+
+ $this->rules[] = sprintf(
+ $ruleFormat,
+ false,
+ $attributes['protocol'], // proto
+ $attributes['ip-src'], // src_ip
+ $attributes['src-port'], // src_port
+ '->', // direction
+ $attributes['ip-dst'], // dst_ip
+ $attributes['dst-port'], // dst_port
+ 'DDOS attack detected between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
+ );
+
+ }
+
public static function getObjectAttributes($object)
{
@@ -826,4 +880,4 @@ class NidsExport
}
return $ipport;
}
-}
+}
\ No newline at end of file
From a04694a5b4db552cb2a8e65ce88a3ec148f3aede Mon Sep 17 00:00:00 2001
From: Marco Caselli
Date: Wed, 15 Sep 2021 12:59:19 +0200
Subject: [PATCH 003/428] Code polishing
---
app/Lib/Export/NidsExport.php | 841 +++++++++++++++++-----------------
1 file changed, 414 insertions(+), 427 deletions(-)
diff --git a/app/Lib/Export/NidsExport.php b/app/Lib/Export/NidsExport.php
index 20dcff784..05d448109 100644
--- a/app/Lib/Export/NidsExport.php
+++ b/app/Lib/Export/NidsExport.php
@@ -10,135 +10,122 @@ class NidsExport
public $supportedObjects = array('network-connection', 'ddos');
- public $checkWhitelist = true;
+ public $checkWhitelist = true;
- public $additional_params = array(
- 'contain' => array(
- 'Event' => array(
- 'fields' => array('threat_level_id')
- )
- ),
- #'flatten' => 1
- );
+ public $additional_params = array(
+ 'contain' => array(
+ 'Event' => array(
+ 'fields' => array('threat_level_id')
+ )
+ ),
- public function handler($data, $options = array())
- {
-
- //NOTES: Here the scope "Object" should be probably checked
-
- $continue = empty($format);
- $this->checkWhitelist = false;
- if ($options['scope'] === 'Attribute') {
- $this->export(
- array($data),
- $options['user']['nids_sid'],
- $options['returnFormat'],
- $continue
- );
- } else if ($options['scope'] === 'Event') {
- if (!empty($data['EventTag'])) {
- $data['Event']['EventTag'] = $data['EventTag'];
- }
- if (!empty($data['Attribute'])) {
- $this->__convertFromEventFormat($data['Attribute'], $data, $options, $continue);
- }
- if (!empty($data['Object'])) {
- #foreach ($data['Object'] as $object) {
- $this->__convertFromEventFormatObject($data['Object'], $data, $options, $continue);
- #}
- }
- }
- return '';
- }
+ );
- private function __convertFromEventFormat($attributes, $event, $options = array(), $continue = false) {
-
- $rearranged = array();
- foreach ($attributes as $attribute) {
- $attributeTag = array();
- if (!empty($attribute['AttributeTag'])) {
- $attributeTag = $attribute['AttributeTag'];
- unset($attribute['AttributeTag']);
- }
- $rearranged[] = array(
- 'Attribute' => $attribute,
- 'AttributeTag' => $attributeTag,
- 'Event' => $event['Event']
- );
- }
- $this->export(
- $rearranged,
- $options['user']['nids_sid'],
- $options['returnFormat'],
- $continue
- );
- return true;
+ public function handler($data, $options = array())
+ {
+ $continue = empty($format);
+ $this->checkWhitelist = false;
+ if ($options['scope'] === 'Attribute') {
+ $this->export(
+ array($data),
+ $options['user']['nids_sid'],
+ $options['returnFormat'],
+ $continue
+ );
+ } else if ($options['scope'] === 'Event') {
+ if (!empty($data['EventTag'])) {
+ $data['Event']['EventTag'] = $data['EventTag'];
+ }
+ if (!empty($data['Attribute'])) {
+ $this->__convertFromEventFormat($data['Attribute'], $data, $options, $continue);
+ }
+ if (!empty($data['Object'])) {
+ $this->__convertFromEventFormatObject($data['Object'], $data, $options, $continue);
+ }
+ }
+ return '';
+ }
- }
-
- private function __convertFromEventFormatObject($objects, $event, $options = array(), $continue = false) {
-
- #CakeLog::debug("ConvertFromEventFormatObject");
- #CakeLog::debug(json_encode($event));
-
- $rearranged = array();
- foreach ($objects as $object) {
-
- #CakeLog::debug("Checking Object");
-
- if(in_array($object['name'], $this->supportedObjects)){ //NOTES: Checking if this is an object supported for the custom export
-
- $objectTag = array();
-
- foreach($object['Attribute'] as $attribute) {
-
- if (!empty($attribute['AttributeTag'])) {
- $objectTag = array_merge($objectTag, $attribute['AttributeTag']);
- unset($attribute['AttributeTag']);
- }
-
- }
-
- $rearranged[] = array(
- 'Attribute' => $object, //NOTES: Using 'Attribute' instead of 'Object' to comply with function export
- 'AttributeTag' => $objectTag, //NOTES: Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
- 'Event' => $event['Event']
- );
-
- } else { //NOTES: In case the object is not supported for the custom export, the approach falls back to the attribute case
-
- $this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
-
- }
-
- }
-
- $this->export(
- $rearranged,
- $options['user']['nids_sid'],
- $options['returnFormat'],
- $continue
-
- );
- return true;
+ private function __convertFromEventFormat($attributes, $event, $options = array(), $continue = false) {
- }
+ $rearranged = array();
+ foreach ($attributes as $attribute) {
+ $attributeTag = array();
+ if (!empty($attribute['AttributeTag'])) {
+ $attributeTag = $attribute['AttributeTag'];
+ unset($attribute['AttributeTag']);
+ }
+ $rearranged[] = array(
+ 'Attribute' => $attribute,
+ 'AttributeTag' => $attributeTag,
+ 'Event' => $event['Event']
+ );
+ }
+ $this->export(
+ $rearranged,
+ $options['user']['nids_sid'],
+ $options['returnFormat'],
+ $continue
+ );
+ return true;
- public function header($options = array())
- {
- $this->explain();
- return '';
- }
+ }
- public function footer()
- {
- return implode ("\n", $this->rules);
- }
+ private function __convertFromEventFormatObject($objects, $event, $options = array(), $continue = false) {
- public function separator()
- {
- return '';
- }
+ $rearranged = array();
+ foreach ($objects as $object) {
+
+ if(in_array($object['name'], $this->supportedObjects)){
+
+ $objectTag = array();
+
+ foreach($object['Attribute'] as $attribute) {
+
+ if (!empty($attribute['AttributeTag'])) {
+ $objectTag = array_merge($objectTag, $attribute['AttributeTag']);
+ unset($attribute['AttributeTag']);
+ }
+
+ }
+
+ $rearranged[] = array(
+ 'Attribute' => $object, // Using 'Attribute' instead of 'Object' to comply with function export
+ 'AttributeTag' => $objectTag, // Using 'AttributeTag' instead of 'ObjectTag' to comply with function export
+ 'Event' => $event['Event']
+ );
+
+ } else { // In case no custom export exists for the object, the approach falls back to the attribute case
+ $this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
+ }
+
+ }
+
+ $this->export(
+ $rearranged,
+ $options['user']['nids_sid'],
+ $options['returnFormat'],
+ $continue
+ );
+ return true;
+
+ }
+
+ public function header($options = array())
+ {
+ $this->explain();
+ return '';
+ }
+
+ public function footer()
+ {
+ return implode ("\n", $this->rules);
+ }
+
+ public function separator()
+ {
+ return '';
+ }
public function explain()
{
@@ -147,7 +134,7 @@ class NidsExport
$this->rules[] = '# These NIDS rules contain some variables that need to exist in your configuration.';
$this->rules[] = '# Make sure you have set:';
$this->rules[] = '#';
- $this->rules[] = '# $HOME_NET - Your internal network range';
+ $this->rules[] = '# $HOME_NET - Your internal network range';
$this->rules[] = '# $EXTERNAL_NET - The network considered as outside';
$this->rules[] = '# $SMTP_SERVERS - All your internal SMTP servers';
$this->rules[] = '# $HTTP_PORTS - The ports used to contain HTTP traffic (not required with suricata export)';
@@ -160,10 +147,10 @@ class NidsExport
public function export($items, $startSid, $format="suricata", $continue = false)
{
$this->format = $format;
- if ($this->checkWhitelist && !isset($this->Whitelist)) {
- $this->Whitelist = ClassRegistry::init('Whitelist');
- $this->whitelist = $this->Whitelist->getBlockedValues();
- }
+ if ($this->checkWhitelist && !isset($this->Whitelist)) {
+ $this->Whitelist = ClassRegistry::init('Whitelist');
+ $this->whitelist = $this->Whitelist->getBlockedValues();
+ }
// output a short explanation
if (!$continue) {
@@ -173,20 +160,20 @@ class NidsExport
foreach ($items as $item) {
// retrieve all tags for this item to add them to the msg
$tagsArray = [];
- if (!empty($item['AttributeTag'])) {
- foreach ($item['AttributeTag'] as $tag_attr) {
- if (array_key_exists('name', $tag_attr['Tag'])) {
- array_push($tagsArray, $tag_attr['Tag']['name']);
- }
- }
- }
- if (!empty($item['Event']['EventTag'])) {
- foreach ($item['Event']['EventTag'] as $tag_event) {
- if (array_key_exists('name', $tag_event['Tag'])) {
- array_push($tagsArray, $tag_event['Tag']['name']);
- }
- }
- }
+ if (!empty($item['AttributeTag'])) {
+ foreach ($item['AttributeTag'] as $tag_attr) {
+ if (array_key_exists('name', $tag_attr['Tag'])) {
+ array_push($tagsArray, $tag_attr['Tag']['name']);
+ }
+ }
+ }
+ if (!empty($item['Event']['EventTag'])) {
+ foreach ($item['Event']['EventTag'] as $tag_event) {
+ if (array_key_exists('name', $tag_event['Tag'])) {
+ array_push($tagsArray, $tag_event['Tag']['name']);
+ }
+ }
+ }
$ruleFormatMsgTags = implode(",", $tagsArray);
# proto src_ip src_port direction dst_ip dst_port msg rule_content tag sid rev
@@ -197,122 +184,122 @@ class NidsExport
$sid = $startSid + ($item['Attribute']['id'] * 10); // leave 9 possible rules per attribute type
$sid++;
- if(!empty($item['Attribute']['type'])) { //NOTES: Item is an 'Attribute'
+ if(!empty($item['Attribute']['type'])) { // item is an 'Attribute'
- switch ($item['Attribute']['type']) {
- // LATER nids - test all the snort attributes
- // LATER nids - add the tag keyword in the rules to capture network traffic
- // LATER nids - sanitize every $attribute['value'] to not conflict with snort
- case 'ip-dst':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-dst|port':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src|port':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-src':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-dst':
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-subject':
- $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-attachment':
- $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain':
- $this->domainRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain|ip':
- $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'hostname':
- $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'url':
- $this->urlRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'user-agent':
- $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3-fingerprint-md5':
- $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
- $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'snort':
- $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
- // no break
- default:
- break;
- }
-
- } else if(!empty($item['Attribute']['name'])) { //NOTES: Item is an 'Object'
-
- switch ($item['Attribute']['name']) {
- case 'network-connection':
- $this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ddos':
- $this->ddosRule($ruleFormat, $item['Attribute'], $sid);
- break;
- default:
- break;
- }
-
- }
-
+ switch ($item['Attribute']['type']) {
+ // LATER nids - test all the snort attributes
+ // LATER nids - add the tag keyword in the rules to capture network traffic
+ // LATER nids - sanitize every $attribute['value'] to not conflict with snort
+ case 'ip-dst':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-dst|port':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src|port':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-src':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-dst':
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-subject':
+ $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-attachment':
+ $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain':
+ $this->domainRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain|ip':
+ $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'hostname':
+ $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'url':
+ $this->urlRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'user-agent':
+ $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3-fingerprint-md5':
+ $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
+ $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'snort':
+ $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
+ // no break
+ default:
+ break;
+ }
+
+ } else if(!empty($item['Attribute']['name'])) { // Item is an 'Object'
+
+ switch ($item['Attribute']['name']) {
+ case 'network-connection':
+ $this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ddos':
+ $this->ddosRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ default:
+ break;
+ }
+
+ }
+
}
return $this->rules;
}
-
+
public function networkConnectionRule($ruleFormat, $object, &$sid)
{
- $attributes = NidsExport::getObjectAttributes($object);
+ $attributes = NidsExport::getObjectAttributes($object);
if(!array_key_exists('layer4-protocol', $attributes)){
- $attributes['layer4-protocol'] = 'ip'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('ip')
+ $attributes['layer4-protocol'] = 'ip'; // If layer-4 protocol is unknown, we roll-back to layer-3 ('ip')
}
if(!array_key_exists('ip-src', $attributes)){
- $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
+ $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('ip-dst', $attributes)){
- $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
+ $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('src-port', $attributes)){
- $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
+ $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
}
if(!array_key_exists('dst-port', $attributes)){
- $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
+ $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
}
- $this->rules[] = sprintf(
+ $this->rules[] = sprintf(
$ruleFormat,
false,
- $attributes['layer4-protocol'], // proto
- $attributes['ip-src'], // src_ip
- $attributes['src-port'], // src_port
- '->', // direction
- $attributes['ip-dst'], // dst_ip
- $attributes['dst-port'], // dst_port
- 'Network connection between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
- '', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ $attributes['layer4-protocol'], // proto
+ $attributes['ip-src'], // src_ip
+ $attributes['src-port'], // src_port
+ '->', // direction
+ $attributes['ip-dst'], // dst_ip
+ $attributes['dst-port'], // dst_port
+ 'Network connection between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -320,49 +307,49 @@ class NidsExport
public function ddosRule($ruleFormat, $object, &$sid)
{
- $attributes = NidsExport::getObjectAttributes($object);
+ $attributes = NidsExport::getObjectAttributes($object);
if(!array_key_exists('protocol', $attributes)){
- $attributes['protocol'] = 'ip'; // If protocol is unknown, we roll-back to 'ip'
+ $attributes['protocol'] = 'ip'; // If protocol is unknown, we roll-back to 'ip'
}
if(!array_key_exists('ip-src', $attributes)){
- $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
+ $attributes['ip-src'] = '$HOME_NET'; // If ip-src is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('ip-dst', $attributes)){
- $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
+ $attributes['ip-dst'] = '$HOME_NET'; // If ip-dst is unknown, we roll-back to $HOME_NET
}
if(!array_key_exists('src-port', $attributes)){
- $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
+ $attributes['src-port'] = 'any'; // If src-port is unknown, we roll-back to 'any'
}
if(!array_key_exists('dst-port', $attributes)){
- $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
+ $attributes['dst-port'] = 'any'; // If dst-port is unknown, we roll-back to 'any'
}
- $this->rules[] = sprintf(
+ $this->rules[] = sprintf(
$ruleFormat,
false,
- $attributes['protocol'], // proto
- $attributes['ip-src'], // src_ip
- $attributes['src-port'], // src_port
- '->', // direction
- $attributes['ip-dst'], // dst_ip
- $attributes['dst-port'], // dst_port
- 'DDOS attack detected between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
- '', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ $attributes['protocol'], // proto
+ $attributes['ip-src'], // src_ip
+ $attributes['src-port'], // src_port
+ '->', // direction
+ $attributes['ip-dst'], // dst_ip
+ $attributes['dst-port'], // dst_port
+ 'DDOS attack detected between ' . $attributes['ip-src'] . ' and ' . $attributes['ip-dst'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
}
public static function getObjectAttributes($object)
{
-
- $attributes = array();
+
+ $attributes = array();
foreach ($object['Attribute'] as $attribute) {
- $attributes[$attribute['object_relation']] = $attribute['value'];
+ $attributes[$attribute['object_relation']] = $attribute['value'];
}
return $attributes;
@@ -388,17 +375,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'ip', // proto
- '$HOME_NET', // src_ip
- 'any', // src_port
- '->', // direction
- $ipport[0], // dst_ip
- $ipport[1], // dst_port
- 'Outgoing To IP: ' . $attribute['value'], // msg
- '', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'ip', // proto
+ '$HOME_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ $ipport[0], // dst_ip
+ $ipport[1], // dst_port
+ 'Outgoing To IP: ' . $attribute['value'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -409,17 +396,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'ip', // proto
- $ipport[0], // src_ip
- $ipport[1], // src_port
- '->', // direction
- '$HOME_NET', // dst_ip
- 'any', // dst_port
- 'Incoming From IP: ' . $attribute['value'], // msg
- '', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'ip', // proto
+ $ipport[0], // src_ip
+ $ipport[1], // src_port
+ '->', // direction
+ '$HOME_NET', // dst_ip
+ 'any', // dst_port
+ 'Incoming From IP: ' . $attribute['value'], // msg
+ '', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -431,17 +418,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$EXTERNAL_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$SMTP_SERVERS', // dst_ip
- '25', // dst_port
- 'Source Email Address: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$EXTERNAL_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$SMTP_SERVERS', // dst_ip
+ '25', // dst_port
+ 'Source Email Address: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -453,17 +440,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$EXTERNAL_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$SMTP_SERVERS', // dst_ip
- '25', // dst_port
- 'Destination Email Address: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$EXTERNAL_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$SMTP_SERVERS', // dst_ip
+ '25', // dst_port
+ 'Destination Email Address: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -476,17 +463,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$EXTERNAL_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$SMTP_SERVERS', // dst_ip
- '25', // dst_port
- 'Bad Email Subject', // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$EXTERNAL_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$SMTP_SERVERS', // dst_ip
+ '25', // dst_port
+ 'Bad Email Subject', // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -499,17 +486,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$EXTERNAL_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$SMTP_SERVERS', // dst_ip
- '25', // dst_port
- 'Bad Email Attachment', // msg
- $content, // rule_content // LATER nids - test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$EXTERNAL_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$SMTP_SERVERS', // dst_ip
+ '25', // dst_port
+ 'Bad Email Attachment', // msg
+ $content, // rule_content // LATER nids - test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -521,33 +508,33 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'udp', // proto
- 'any', // src_ip
- 'any', // src_port
- '->', // direction
- 'any', // dst_ip
- '53', // dst_port
- 'Hostname: ' . $attribute['value'], // msg
- $content, // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'udp', // proto
+ 'any', // src_ip
+ 'any', // src_port
+ '->', // direction
+ 'any', // dst_ip
+ '53', // dst_port
+ 'Hostname: ' . $attribute['value'], // msg
+ $content, // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
$sid++;
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- 'any', // src_ip
- 'any', // src_port
- '->', // direction
- 'any', // dst_ip
- '53', // dst_port
- 'Hostname: ' . $attribute['value'], // msg
- $content. ' flow:established;', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ 'any', // src_ip
+ 'any', // src_port
+ '->', // direction
+ 'any', // dst_ip
+ '53', // dst_port
+ 'Hostname: ' . $attribute['value'], // msg
+ $content. ' flow:established;', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
$sid++;
// also do http requests
@@ -555,17 +542,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$HOME_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$EXTERNAL_NET', // dst_ip
- '$HTTP_PORTS', // dst_port
- 'Outgoing HTTP Hostname: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$HOME_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$EXTERNAL_NET', // dst_ip
+ '$HTTP_PORTS', // dst_port
+ 'Outgoing HTTP Hostname: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -577,33 +564,33 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'udp', // proto
- 'any', // src_ip
- 'any', // src_port
- '->', // direction
- 'any', // dst_ip
- '53', // dst_port
- 'Domain: ' . $attribute['value'], // msg
- $content, // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'udp', // proto
+ 'any', // src_ip
+ 'any', // src_port
+ '->', // direction
+ 'any', // dst_ip
+ '53', // dst_port
+ 'Domain: ' . $attribute['value'], // msg
+ $content, // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
$sid++;
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- 'any', // src_ip
- 'any', // src_port
- '->', // direction
- 'any', // dst_ip
- '53', // dst_port
- 'Domain: ' . $attribute['value'], // msg
- $content. ' flow:established;', // rule_content
- '', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ 'any', // src_ip
+ 'any', // src_port
+ '->', // direction
+ 'any', // dst_ip
+ '53', // dst_port
+ 'Domain: ' . $attribute['value'], // msg
+ $content. ' flow:established;', // rule_content
+ '', // tag
+ $sid, // sid
+ 1 // rev
);
$sid++;
// also do http requests,
@@ -611,17 +598,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$HOME_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$EXTERNAL_NET', // dst_ip
- '$HTTP_PORTS', // dst_port
- 'Outgoing HTTP Domain: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$HOME_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$EXTERNAL_NET', // dst_ip
+ '$HTTP_PORTS', // dst_port
+ 'Outgoing HTTP Domain: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -636,17 +623,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$HOME_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$EXTERNAL_NET', // dst_ip
- '$HTTP_PORTS', // dst_port
- 'Outgoing HTTP URL: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$HOME_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$EXTERNAL_NET', // dst_ip
+ '$HTTP_PORTS', // dst_port
+ 'Outgoing HTTP URL: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -658,17 +645,17 @@ class NidsExport
$this->rules[] = sprintf(
$ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
- 'tcp', // proto
- '$HOME_NET', // src_ip
- 'any', // src_port
- '->', // direction
- '$EXTERNAL_NET', // dst_ip
- '$HTTP_PORTS', // dst_port
- 'Outgoing User-Agent: ' . $attribute['value'], // msg
- $content, // rule_content
- 'tag:session,600,seconds;', // tag
- $sid, // sid
- 1 // rev
+ 'tcp', // proto
+ '$HOME_NET', // src_ip
+ 'any', // src_port
+ '->', // direction
+ '$EXTERNAL_NET', // dst_ip
+ '$HTTP_PORTS', // dst_port
+ 'Outgoing User-Agent: ' . $attribute['value'], // msg
+ $content, // rule_content
+ 'tag:session,600,seconds;', // tag
+ $sid, // sid
+ 1 // rev
);
}
@@ -690,37 +677,37 @@ class NidsExport
$tmpRule = str_replace(array("\r","\n"), " ", $attribute['value']);
// rebuild the rule by overwriting the different keywords using preg_replace()
- // sid - '/sid\s*:\s*[0-9]+\s*;/'
- // rev - '/rev\s*:\s*[0-9]+\s*;/'
+ // sid - '/sid\s*:\s*[0-9]+\s*;/'
+ // rev - '/rev\s*:\s*[0-9]+\s*;/'
// classtype - '/classtype:[a-zA-Z_-]+;/'
- // msg - '/msg\s*:\s*".*?"\s*;/'
+ // msg - '/msg\s*:\s*".*?"\s*;/'
// reference - '/reference\s*:\s*.+?;/'
- // tag - '/tag\s*:\s*.+?;/'
+ // tag - '/tag\s*:\s*.+?;/'
$replaceCount = array();
$tmpRule = preg_replace('/sid\s*:\s*[0-9]+\s*;/', 'sid:' . $sid . ';', $tmpRule, -1, $replaceCount['sid']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
$tmpRule = preg_replace('/rev\s*:\s*[0-9]+\s*;/', 'rev:1;', $tmpRule, -1, $replaceCount['rev']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
$tmpRule = preg_replace('/classtype:[a-zA-Z_-]+;/', 'classtype:' . $this->classtype . ';', $tmpRule, -1, $replaceCount['classtype']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
$tmpRule = preg_replace('/msg\s*:\s*"(.*?)"\s*;/', sprintf($ruleFormatMsg, 'snort-rule | $1') . ';', $tmpRule, -1, $replaceCount['msg']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
$tmpRule = preg_replace('/reference\s*:\s*.+?;/', $ruleFormatReference . ';', $tmpRule, -1, $replaceCount['reference']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
$tmpRule = preg_replace('/reference\s*:\s*.+?;/', $ruleFormatReference . ';', $tmpRule, -1, $replaceCount['reference']);
if (null == $tmpRule) {
return false;
- } // don't output the rule on error with the regex
+ } // don't output the rule on error with the regex
// FIXME nids - implement priority overwriting
// some values were not replaced, so we need to add them ourselves, and insert them in the rule
@@ -830,13 +817,13 @@ class NidsExport
public function checkWhitelist($value)
{
- if ($this->checkWhitelist && is_array($this->whitelist)) {
- foreach ($this->whitelist as $wlitem) {
- if (preg_match($wlitem, $value)) {
- return true;
- }
- }
- }
+ if ($this->checkWhitelist && is_array($this->whitelist)) {
+ foreach ($this->whitelist as $wlitem) {
+ if (preg_match($wlitem, $value)) {
+ return true;
+ }
+ }
+ }
return false;
}
From 0180da6b576f81d70db90aa9083e7d07b3e1319b Mon Sep 17 00:00:00 2001
From: Marco Caselli
Date: Fri, 1 Oct 2021 11:17:02 +0200
Subject: [PATCH 004/428] Fixing mistake ("data" -> "event")
---
app/Lib/Export/NidsExport.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Lib/Export/NidsExport.php b/app/Lib/Export/NidsExport.php
index 05d448109..c8cabe89b 100644
--- a/app/Lib/Export/NidsExport.php
+++ b/app/Lib/Export/NidsExport.php
@@ -96,7 +96,7 @@ class NidsExport
);
} else { // In case no custom export exists for the object, the approach falls back to the attribute case
- $this->__convertFromEventFormat($object['Attribute'], $data, $options, $continue);
+ $this->__convertFromEventFormat($object['Attribute'], $event, $options, $continue);
}
}
From 653fe1c9016bd89971140ec04376c1ca26fa21b4 Mon Sep 17 00:00:00 2001
From: Marco Caselli
Date: Fri, 1 Oct 2021 11:21:49 +0200
Subject: [PATCH 005/428] Fixed indentation
---
app/Lib/Export/NidsExport.php | 147 +++++++++++++++++-----------------
1 file changed, 74 insertions(+), 73 deletions(-)
diff --git a/app/Lib/Export/NidsExport.php b/app/Lib/Export/NidsExport.php
index c8cabe89b..a381556ab 100644
--- a/app/Lib/Export/NidsExport.php
+++ b/app/Lib/Export/NidsExport.php
@@ -186,82 +186,83 @@ class NidsExport
if(!empty($item['Attribute']['type'])) { // item is an 'Attribute'
- switch ($item['Attribute']['type']) {
- // LATER nids - test all the snort attributes
- // LATER nids - add the tag keyword in the rules to capture network traffic
- // LATER nids - sanitize every $attribute['value'] to not conflict with snort
- case 'ip-dst':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-dst|port':
- $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ip-src|port':
- $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-src':
- $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-dst':
- $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-subject':
- $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'email-attachment':
- $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain':
- $this->domainRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'domain|ip':
- $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'hostname':
- $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'url':
- $this->urlRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'user-agent':
- $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3-fingerprint-md5':
- $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
- $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'snort':
- $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
- // no break
- default:
- break;
+ switch ($item['Attribute']['type']) {
+ // LATER nids - test all the snort attributes
+ // LATER nids - add the tag keyword in the rules to capture network traffic
+ // LATER nids - sanitize every $attribute['value'] to not conflict with snort
+ case 'ip-dst':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-dst|port':
+ $this->ipDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ip-src|port':
+ $this->ipSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-src':
+ $this->emailSrcRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-dst':
+ $this->emailDstRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-subject':
+ $this->emailSubjectRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'email-attachment':
+ $this->emailAttachmentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain':
+ $this->domainRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'domain|ip':
+ $this->domainIpRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'hostname':
+ $this->hostnameRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'url':
+ $this->urlRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'user-agent':
+ $this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3-fingerprint-md5':
+ $this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
+ $this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'snort':
+ $this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
+ // no break
+ default:
+ break;
+ }
+
+ } else if(!empty($item['Attribute']['name'])) { // Item is an 'Object'
+
+ switch ($item['Attribute']['name']) {
+ case 'network-connection':
+ $this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ case 'ddos':
+ $this->ddosRule($ruleFormat, $item['Attribute'], $sid);
+ break;
+ default:
+ break;
+ }
+
}
- } else if(!empty($item['Attribute']['name'])) { // Item is an 'Object'
-
- switch ($item['Attribute']['name']) {
- case 'network-connection':
- $this->networkConnectionRule($ruleFormat, $item['Attribute'], $sid);
- break;
- case 'ddos':
- $this->ddosRule($ruleFormat, $item['Attribute'], $sid);
- break;
- default:
- break;
- }
-
- }
-
}
+
return $this->rules;
}
From 5698b91e285c2bc6a650df7588435c499a153551 Mon Sep 17 00:00:00 2001
From: Sami Mokaddem
Date: Tue, 3 May 2022 22:57:59 +0200
Subject: [PATCH 006/428] chg: [ui:main] Fixed overflowing UI creating a
useless X scrollbar
---
app/webroot/css/main.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css
index ef074b902..0aecaa7f6 100644
--- a/app/webroot/css/main.css
+++ b/app/webroot/css/main.css
@@ -640,7 +640,7 @@ dd {
.footerCenterText {
padding-top:12px;
position: absolute;
- width:100%;
+ width: calc(100% - 21px);
text-align:center;
}
From f6213e86f8cf99bc87eca4e42c78d7da3ee3d38b Mon Sep 17 00:00:00 2001
From: Sami Mokaddem
Date: Tue, 3 May 2022 22:59:42 +0200
Subject: [PATCH 007/428] new: [workflow:editor] Initial work on the workflow
editor
---
app/Controller/WorkflowsController.php | 33 ++
app/View/Elements/Workflows/sidebar-block.ctp | 11 +
app/View/Workflows/add.ctp | 0
app/View/Workflows/delete.ctp | 0
app/View/Workflows/editor.ctp | 266 ++++++++++++
app/View/Workflows/index.ctp | 0
app/webroot/css/drawflow.min.css | 1 +
app/webroot/css/workflows-editor.css | 392 ++++++++++++++++++
app/webroot/js/drawflow.min.js | 1 +
.../js/workflows-editor/workflows-editor.js | 192 +++++++++
10 files changed, 896 insertions(+)
create mode 100644 app/Controller/WorkflowsController.php
create mode 100644 app/View/Elements/Workflows/sidebar-block.ctp
create mode 100644 app/View/Workflows/add.ctp
create mode 100644 app/View/Workflows/delete.ctp
create mode 100644 app/View/Workflows/editor.ctp
create mode 100644 app/View/Workflows/index.ctp
create mode 100644 app/webroot/css/drawflow.min.css
create mode 100644 app/webroot/css/workflows-editor.css
create mode 100644 app/webroot/js/drawflow.min.js
create mode 100644 app/webroot/js/workflows-editor/workflows-editor.js
diff --git a/app/Controller/WorkflowsController.php b/app/Controller/WorkflowsController.php
new file mode 100644
index 000000000..b371af17f
--- /dev/null
+++ b/app/Controller/WorkflowsController.php
@@ -0,0 +1,33 @@
+RestResponse->viewData([]);
+ }
+
+ public function add()
+ {
+
+ }
+
+ public function edit()
+ {
+
+ }
+
+ public function delete()
+ {
+
+ }
+
+ public function editor()
+ {
+
+ }
+}
diff --git a/app/View/Elements/Workflows/sidebar-block.ctp b/app/View/Elements/Workflows/sidebar-block.ctp
new file mode 100644
index 000000000..185d98b69
--- /dev/null
+++ b/app/View/Elements/Workflows/sidebar-block.ctp
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/app/View/Workflows/add.ctp b/app/View/Workflows/add.ctp
new file mode 100644
index 000000000..e69de29bb
diff --git a/app/View/Workflows/delete.ctp b/app/View/Workflows/delete.ctp
new file mode 100644
index 000000000..e69de29bb
diff --git a/app/View/Workflows/editor.ctp b/app/View/Workflows/editor.ctp
new file mode 100644
index 000000000..132c91c79
--- /dev/null
+++ b/app/View/Workflows/editor.ctp
@@ -0,0 +1,266 @@
+ 'publish',
+ 'name' => 'Publish',
+ 'icon' => 'upload',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ ],
+ [
+ 'id' => 'new-attribute',
+ 'name' => 'New Attribute',
+ 'icon' => 'cube',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ 'disabled' => true,
+ ],
+ [
+ 'id' => 'new-object',
+ 'name' => 'New Object',
+ 'icon' => 'cubes',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ 'disabled' => true,
+ ],
+ [
+ 'id' => 'email-sent',
+ 'name' => 'Email sent',
+ 'icon' => 'envelope',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ 'disabled' => true,
+ ],
+ [
+ 'id' => 'user-new',
+ 'name' => 'New User',
+ 'icon' => 'user-plus',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ 'disabled' => true,
+ ],
+ [
+ 'id' => 'feed-pull',
+ 'name' => 'Feed pull',
+ 'icon' => 'arrow-alt-circle-down',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'inputs' => 0,
+ 'disabled' => true,
+ ],
+];
+
+$blocks_condition = [
+ [
+ 'id' => 'if',
+ 'name' => 'IF',
+ 'icon' => 'code-branch',
+ 'description' => 'IF conditions',
+ 'outputs' => 2,
+ 'html_template' => 'IF',
+ ],
+];
+
+$blocks_action = [
+ [
+ 'id' => 'add-tag',
+ 'name' => 'Add Tag',
+ 'icon' => 'tag',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'params' => [
+ [
+ 'type' => 'input',
+ 'label' => 'Tag name',
+ 'default' => 'tlp:red',
+ 'placeholder' => __('Enter tag name')
+ ],
+ ],
+ 'outputs' => 0,
+ ],
+ [
+ 'id' => 'enrich-attribute',
+ 'name' => 'Enrich Attribute',
+ 'icon' => 'asterisk',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'outputs' => 0,
+ ],
+ [
+ 'id' => 'slack-message',
+ 'name' => 'Slack Message',
+ 'icon' => 'slack',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'params' => [
+ [
+ 'type' => 'select',
+ 'label' => 'Channel name',
+ 'default' => 'team-4_3_misp',
+ 'options' => [
+ 'team-4_3_misp',
+ 'team-4_0_elite_as_one',
+ ],
+ ],
+ ],
+ 'outputs' => 0,
+ ],
+ [
+ 'id' => 'send-email',
+ 'name' => 'Send Email',
+ 'icon' => 'envelope',
+ 'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
+ 'params' => [
+ [
+ 'type' => 'select',
+ 'label' => 'Email template',
+ 'default' => 'default',
+ 'options' => [
+ 'default',
+ 'TLP marking',
+ ],
+ ],
+ ],
+ 'outputs' => 0,
+ ],
+ [
+ 'name' => 'Do nothing',
+ 'id' => 'dev-null',
+ 'icon' => 'ban',
+ 'description' => 'Essentially a /dev/null',
+ 'outputs' => 0,
+ ],
+ [
+ 'name' => 'Push to ZMQ',
+ 'id' => 'push-zmq',
+ 'icon' => 'wifi',
+ 'icon_class' => 'fa-rotate-90',
+ 'description' => 'Push to the ZMQ channel',
+ 'params' => [
+ [
+ 'type' => 'input',
+ 'label' => 'ZMQ Topic',
+ 'default' => 'from-misp-workflow',
+ ],
+ ],
+ 'outputs' => 0,
+ ],
+];
+
+$blocks_all = array_merge($blocks_trigger, $blocks_condition, $blocks_action);
+$workflows = [
+ ['id' => 1, 'name' => 'Publish workflow', 'data' => []],
+ ['id' => 2, 'name' => 'My test worklow1', 'data' => []],
+];
+?>
+
+
+
+
+
Workflows
+
+
+
+
+
+
+
Blocks
+
+
+
+
+
+
+
+ = $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
+
+
+
+
+ = $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
+
+
+
+
+ = $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+element('genericElements/assetLoader', [
+ 'css' => ['drawflow.min'],
+ 'js' => ['jquery-ui', 'drawflow.min', 'doT'],
+]);
+echo $this->element('genericElements/assetLoader', [
+ 'css' => ['workflows-editor'],
+ 'js' => ['workflows-editor/workflows-editor'],
+]);
+?>
+
+
\ No newline at end of file
diff --git a/app/View/Workflows/index.ctp b/app/View/Workflows/index.ctp
new file mode 100644
index 000000000..e69de29bb
diff --git a/app/webroot/css/drawflow.min.css b/app/webroot/css/drawflow.min.css
new file mode 100644
index 000000000..c9c944248
--- /dev/null
+++ b/app/webroot/css/drawflow.min.css
@@ -0,0 +1 @@
+.drawflow,.drawflow .parent-node{position:relative}.parent-drawflow{display:flex;overflow:hidden;touch-action:none;outline:0}.drawflow{width:100%;height:100%;user-select:none;perspective:0}.drawflow .drawflow-node{display:flex;align-items:center;position:absolute;background:#0ff;width:160px;min-height:40px;border-radius:4px;border:2px solid #000;color:#000;z-index:2;padding:15px}.drawflow .drawflow-node.selected{background:red}.drawflow .drawflow-node:hover{cursor:move}.drawflow .drawflow-node .inputs,.drawflow .drawflow-node .outputs{width:0}.drawflow .drawflow-node .drawflow_content_node{width:100%;display:block}.drawflow .drawflow-node .input,.drawflow .drawflow-node .output{position:relative;width:20px;height:20px;background:#fff;border-radius:50%;border:2px solid #000;cursor:crosshair;z-index:1;margin-bottom:5px}.drawflow .drawflow-node .input{left:-27px;top:2px;background:#ff0}.drawflow .drawflow-node .output{right:-3px;top:2px}.drawflow svg{z-index:0;position:absolute;overflow:visible!important}.drawflow .connection{position:absolute;pointer-events:none;aspect-ratio:1/1}.drawflow .connection .main-path{fill:none;stroke-width:5px;stroke:#4682b4;pointer-events:all}.drawflow .connection .main-path:hover{stroke:#1266ab;cursor:pointer}.drawflow .connection .main-path.selected{stroke:#43b993}.drawflow .connection .point{cursor:move;stroke:#000;stroke-width:2;fill:#fff;pointer-events:all}.drawflow .connection .point.selected,.drawflow .connection .point:hover{fill:#1266ab}.drawflow .main-path{fill:none;stroke-width:5px;stroke:#4682b4}.drawflow-delete{position:absolute;display:block;width:30px;height:30px;background:#000;color:#fff;z-index:4;border:2px solid #fff;line-height:30px;font-weight:700;text-align:center;border-radius:50%;font-family:monospace;cursor:pointer}.drawflow>.drawflow-delete{margin-left:-15px;margin-top:15px}.parent-node .drawflow-delete{right:-15px;top:-15px}
\ No newline at end of file
diff --git a/app/webroot/css/workflows-editor.css b/app/webroot/css/workflows-editor.css
new file mode 100644
index 000000000..8a31c5505
--- /dev/null
+++ b/app/webroot/css/workflows-editor.css
@@ -0,0 +1,392 @@
+.root-container {
+ display: flex;
+ height: calc(100vh - 2*42px); /* total space available minus header and footer */
+ margin-top: -20px;
+}
+
+.main-container {
+ display: flex;
+ height: 100%;
+ width: 100%;
+}
+
+.side-panel {
+ z-index: 18;
+ position: absolute;
+ box-sizing: border-box;
+ height: calc(100% - 2*42px);
+ width: var(--editor-sidepanel-width);
+ box-shadow: 2px 0px 6px #cecece;
+ background-color: #fff;
+ padding: 0.5em 0.25em;
+ display: flex;
+ flex-direction: column;
+}
+
+.side-panel #block-tabs {
+ margin-bottom: 10px;
+}
+
+.side-panel .workflow-selector-container {
+ display: flex;
+}
+
+.side-panel .workflow-selector-container > select {
+ flex-grow: 1;
+}
+.side-panel .workflow-selector-container > button {
+ margin-left: 0.25em;
+}
+
+.side-panel .chosen-container.workflows {
+ margin-bottom: 0;
+}
+
+.side-panel .chosen-container.blocks {
+ width: 100%;
+ margin-bottom: 1.25em;
+}
+
+.side-panel #block-tabs {
+ margin-top: 1.25em;
+}
+
+.side-panel .tab-pane.active {
+ overflow-y: auto;
+}
+
+.sidebar-workflow-block {
+ display: flex;
+ width: 300px;
+ background-color: #fff;
+ border-radius: 5px;
+ margin: 0.75em 0.5em;
+ padding: 0.25em;
+ transition-property: box-shadow;
+ transition-duration: .1s;
+ box-shadow: 0px 3px 6px 2px #33333311;
+}
+
+.sidebar-workflow-block:hover {
+ box-shadow: 0px 3px 6px 2px #33333333;
+ cursor: pointer;
+}
+
+.sidebar-workflow-block.disabled {
+ cursor: not-allowed !important;
+ filter: blur(1px);
+}
+
+.sidebar-workflow-block > .icon {
+ width: 1.25em;
+ align-items: flex-start;
+ display: flex;
+ font-size: large;
+ padding: 0 0.25em;
+}
+
+.canvas {
+ height: 100%;
+ width: calc(100% - var(--editor-sidepanel-width));
+ margin-left: var(--editor-sidepanel-width);
+}
+
+.canvas-workflow-block {
+ display: flex;
+ width: 300px;
+ background-color: #fff;
+ border-radius: 5px;
+ padding: 0.25em 0.75em;
+ box-shadow: 0px 3px 6px 2px #33333333;
+}
+
+.canvas-workflow-block.small {
+ height: 50px;
+}
+
+.canvas-workflow-block br {
+ width: 100%;
+ height: 1px;
+ background-color: #e9e9e9;
+}
+
+.canvas-workflow-block>.icon {
+ width: 1.25em;
+ align-items: flex-start;
+ display: flex;
+ font-size: large;
+ padding: 0 0.25em;
+}
+
+#drawflow {
+ height: 100%;
+ width: 100%;
+}
+
+/* Canvas blocks */
+.default-main-container {
+ display: flex;
+ font-size: large;
+ padding: 0.25em 0;
+ border-bottom: 1px solid #efefef;
+}
+
+.canvas-workflow-block.small .default-main-container-small {
+ display: flex;
+ font-size: large;
+ padding: 0.25em;
+ height: 100%;
+ box-sizing: border-box;
+ align-items: center;
+}
+
+.default-main-container-small .then-else-container {
+ font-size: small;
+ display: flex;
+ flex-direction: column;
+ margin-left: auto;
+ font-family: monospace;
+ margin-top: -1px;
+ margin-right: 2px;
+
+}
+
+.default-main-container .description {
+ user-select: none;
+ margin: 0.25em 0.5em;
+}
+
+
+
+/* Drawflow CSS */
+
+:root {
+ --editor-sidepanel-width: 340px;
+
+ --dfBackgroundColor: #ffffff;
+ --dfBackgroundSize: 15px;
+ --dfBackgroundImage: linear-gradient(to right, #66666615 1px, transparent 1px),
+ linear-gradient(to bottom, #66666615 1px, transparent 1px);
+
+ --dfNodeType: flex;
+ --dfNodeTypeFloat: none;
+ --dfNodeBackgroundColor: #ffffff;
+ --dfNodeTextColor: #000000;
+ --dfNodeBorderSize: 0px;
+ --dfNodeBorderColor: #000000;
+ --dfNodeBorderRadius: 4px;
+ --dfNodeMinHeight: 40px;
+ --dfNodeMinWidth: 160px;
+ --dfNodePadding: 0px;
+ --dfNodePaddingTop: 15px;
+ --dfNodePaddingBottom: 15px;
+
+ --dfNodeHoverBackgroundColor: #ffffff;
+ --dfNodeHoverTextColor: #000000;
+
+ --dfNodeHoverBoxShadowHL: 0px;
+ --dfNodeHoverBoxShadowVL: 2px;
+ --dfNodeHoverBoxShadowBR: 6px;
+ --dfNodeHoverBoxShadowS: 0px;
+ --dfNodeHoverBoxShadowColor: #0088cc99;
+
+ --dfNodeSelectedBackgroundColor: #ffffff;
+ --dfNodeSelectedTextColor: #000000;
+
+ --dfNodeSelectedBoxShadowHL: 0px;
+ --dfNodeSelectedBoxShadowVL: 2px;
+ --dfNodeSelectedBoxShadowBR: 6px;
+ --dfNodeSelectedBoxShadowS: 0px;
+ --dfNodeSelectedBoxShadowColor: #0088cc99;
+
+ --dfInputBackgroundColor: #ffffff;
+ --dfInputBorderSize: 2px;
+ --dfInputBorderColor: #5a5a5a;
+ --dfInputBorderRadius: 50px;
+ --dfInputLeft: -7px;
+ --dfInputHeight: 10px;
+ --dfInputWidth: 10px;
+
+ --dfInputHeightHover: 16px;
+ --dfInputWidthHover: 16px;
+ --dfInputLeftHover: -10px;
+
+ --dfInputHoverBackgroundColor: #ffffff;
+ --dfInputHoverBorderSize: 2px;
+ --dfInputHoverBorderColor: #5a5a5a;
+ --dfInputHoverBorderRadius: 50px;
+
+ --dfOutputBackgroundColor: #ffffff;
+ --dfOutputBorderSize: 2px;
+ --dfOutputBorderColor: #5a5a5a;
+ --dfOutputBorderRadius: 50px;
+ --dfOutputRight: 6px;
+ --dfOutputHeight: 10px;
+ --dfOutputWidth: 10px;
+
+ --dfOutputHoverBackgroundColor: #ffffff;
+ --dfOutputHoverBorderSize: 2px;
+ --dfOutputHoverBorderColor: #5a5a5a;
+ --dfOutputHoverBorderRadius: 50px;
+
+ --dfOutputHeightHover: 16px;
+ --dfOutputWidthHover: 16px;
+ --dfOutputRightHover: 9px;
+
+ --dfLineWidth: 5px;
+ --dfLineColor: #4682b4;
+ --dfLineHoverColor: #4682b4;
+ --dfLineSelectedColor: #43b993;
+
+ --dfRerouteBorderWidth: 2px;
+ --dfRerouteBorderColor: #000000;
+ --dfRerouteBackgroundColor: #ffffff;
+
+ --dfRerouteHoverBorderWidth: 2px;
+ --dfRerouteHoverBorderColor: #000000;
+ --dfRerouteHoverBackgroundColor: #ffffff;
+
+ --dfDeleteDisplay: block;
+ --dfDeleteColor: #ffffff;
+ --dfDeleteBackgroundColor: #e37b78;
+ --dfDeleteBorderSize: 2px;
+ --dfDeleteBorderColor: #ffffff;
+ --dfDeleteBorderRadius: 50px;
+ --dfDeleteTop: -11px;
+ --dfDeleteRight: -11px;
+ --dfDeleteHeight: 15px;
+ --dfDeleteWidth: 15px;
+ --dfDeleteLineHeight: 16px;
+
+ --dfDeleteHoverColor: #ffffff;
+ --dfDeleteHoverBackgroundColor: #da4e49;
+ --dfDeleteHoverBorderSize: 2px;
+ --dfDeleteHoverBorderColor: #ffffff;
+ --dfDeleteHoverBorderRadius: 50px;
+}
+
+#drawflow {
+ background: var(--dfBackgroundColor);
+ background-size: var(--dfBackgroundSize) var(--dfBackgroundSize);
+ background-image: var(--dfBackgroundImage);
+}
+
+.drawflow .drawflow-node {
+ display: var(--dfNodeType);
+ background: var(--dfNodeBackgroundColor);
+ color: var(--dfNodeTextColor);
+ border: var(--dfNodeBorderSize) solid var(--dfNodeBorderColor);
+ border-radius: var(--dfNodeBorderRadius);
+ min-height: var(--dfNodeMinHeight);
+ width: auto;
+ min-width: var(--dfNodeMinWidth);
+ padding-top: var(--dfNodePaddingTop);
+ padding-bottom: var(--dfNodePaddingBottom);
+ padding: var(--dfNodePadding);
+ -webkit-box-shadow: var(--dfNodeBoxShadowHL) var(--dfNodeBoxShadowVL) var(--dfNodeBoxShadowBR) var(--dfNodeBoxShadowS) var(--dfNodeBoxShadowColor);
+ box-shadow: var(--dfNodeBoxShadowHL) var(--dfNodeBoxShadowVL) var(--dfNodeBoxShadowBR) var(--dfNodeBoxShadowS) var(--dfNodeBoxShadowColor);
+}
+
+.drawflow .drawflow-node:hover {
+ -webkit-box-shadow: var(--dfNodeHoverBoxShadowHL) var(--dfNodeHoverBoxShadowVL) var(--dfNodeHoverBoxShadowBR) var(--dfNodeHoverBoxShadowS) var(--dfNodeHoverBoxShadowColor);
+ box-shadow: var(--dfNodeHoverBoxShadowHL) var(--dfNodeHoverBoxShadowVL) var(--dfNodeHoverBoxShadowBR) var(--dfNodeHoverBoxShadowS) var(--dfNodeHoverBoxShadowColor);
+}
+
+.drawflow .drawflow-node.selected {
+ background: var(--dfNodeSelectedBackgroundColor);
+ color: var(--dfNodeSelectedTextColor);
+ -webkit-box-shadow: var(--dfNodeSelectedBoxShadowHL) var(--dfNodeSelectedBoxShadowVL) var(--dfNodeSelectedBoxShadowBR) var(--dfNodeSelectedBoxShadowS) var(--dfNodeSelectedBoxShadowColor);
+ box-shadow: var(--dfNodeSelectedBoxShadowHL) var(--dfNodeSelectedBoxShadowVL) var(--dfNodeSelectedBoxShadowBR) var(--dfNodeSelectedBoxShadowS) var(--dfNodeSelectedBoxShadowColor);
+}
+
+.drawflow .drawflow-node .input {
+ left: var(--dfInputLeft);
+ background: var(--dfInputBackgroundColor);
+ border: var(--dfInputBorderSize) solid var(--dfInputBorderColor);
+ border-radius: var(--dfInputBorderRadius);
+ height: var(--dfInputHeight);
+ width: var(--dfInputWidth);
+ transition-property: height,width,left;
+ transition-duration: .1s;
+}
+
+.drawflow .drawflow-node .input:hover {
+ background: var(--dfInputHoverBackgroundColor);
+ border: var(--dfInputHoverBorderSize) solid var(--dfInputHoverBorderColor);
+ border-radius: var(--dfInputHoverBorderRadius);
+ height: var(--dfInputHeightHover);
+ width: var(--dfInputWidthHover);
+ left: var(--dfInputLeftHover);
+}
+
+.drawflow .drawflow-node .outputs {
+ float: var(--dfNodeTypeFloat);
+}
+
+.drawflow .drawflow-node .output {
+ right: var(--dfOutputRight);
+ background: var(--dfOutputBackgroundColor);
+ border: var(--dfOutputBorderSize) solid var(--dfOutputBorderColor);
+ border-radius: var(--dfOutputBorderRadius);
+ height: var(--dfOutputHeight);
+ width: var(--dfOutputWidth);
+ transition-property: height,width,right;
+ transition-duration: .1s;
+}
+
+.drawflow .drawflow-node .output:hover {
+ background: var(--dfOutputHoverBackgroundColor);
+ border: var(--dfOutputHoverBorderSize) solid var(--dfOutputHoverBorderColor);
+ border-radius: var(--dfOutputHoverBorderRadius);
+ height: var(--dfOutputHeightHover);
+ width: var(--dfOutputWidthHover);
+ right: var(--dfOutputRightHover);
+}
+
+.drawflow .connection .main-path {
+ stroke-width: var(--dfLineWidth);
+ stroke: var(--dfLineColor);
+}
+
+.drawflow .connection .main-path:hover {
+ stroke: var(--dfLineHoverColor);
+}
+
+.drawflow .connection .main-path.selected {
+ stroke: var(--dfLineSelectedColor);
+}
+
+.drawflow .connection .point {
+ stroke: var(--dfRerouteBorderColor);
+ stroke-width: var(--dfRerouteBorderWidth);
+ fill: var(--dfRerouteBackgroundColor);
+}
+
+.drawflow .connection .point:hover {
+ stroke: var(--dfRerouteHoverBorderColor);
+ stroke-width: var(--dfRerouteHoverBorderWidth);
+ fill: var(--dfRerouteHoverBackgroundColor);
+}
+
+.drawflow-delete {
+ display: var(--dfDeleteDisplay);
+ color: var(--dfDeleteColor);
+ background: var(--dfDeleteBackgroundColor);
+ border: var(--dfDeleteBorderSize) solid var(--dfDeleteBorderColor);
+ border-radius: var(--dfDeleteBorderRadius);
+}
+
+.parent-node .drawflow-delete {
+ top: var(--dfDeleteTop);
+ right: var(--dfDeleteRight);
+ height: var(--dfDeleteHeight);
+ width: var(--dfDeleteWidth);
+ line-height: var(--dfDeleteLineHeight);
+}
+
+.drawflow-delete:hover {
+ color: var(--dfDeleteHoverColor);
+ background: var(--dfDeleteHoverBackgroundColor);
+ border: var(--dfDeleteHoverBorderSize) solid var(--dfDeleteHoverBorderColor);
+ border-radius: var(--dfDeleteHoverBorderRadius);
+}
\ No newline at end of file
diff --git a/app/webroot/js/drawflow.min.js b/app/webroot/js/drawflow.min.js
new file mode 100644
index 000000000..922aebc31
--- /dev/null
+++ b/app/webroot/js/drawflow.min.js
@@ -0,0 +1 @@
+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Drawflow=t():e.Drawflow=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var s=t[i]={i:i,l:!1,exports:{}};return e[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(i,s,function(t){return e[t]}.bind(null,s));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return i}));class i{constructor(e,t=null,n=null){this.events={},this.container=e,this.precanvas=null,this.nodeId=1,this.ele_selected=null,this.node_selected=null,this.drag=!1,this.reroute=!1,this.reroute_fix_curvature=!1,this.curvature=.5,this.reroute_curvature_start_end=.5,this.reroute_curvature=.5,this.reroute_width=6,this.drag_point=!1,this.editor_selected=!1,this.connection=!1,this.connection_ele=null,this.connection_selected=null,this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_x_start=0,this.pos_y=0,this.pos_y_start=0,this.mouse_x=0,this.mouse_y=0,this.line_path=5,this.first_click=null,this.force_first_input=!1,this.draggable_inputs=!0,this.useuuid=!1,this.parent=n,this.noderegister={},this.render=t,this.drawflow={drawflow:{Home:{data:{}}}},this.module="Home",this.editor_mode="edit",this.zoom=1,this.zoom_max=1.6,this.zoom_min=.5,this.zoom_value=.1,this.zoom_last_value=1,this.evCache=new Array,this.prevDiff=-1}start(){this.container.classList.add("parent-drawflow"),this.container.tabIndex=0,this.precanvas=document.createElement("div"),this.precanvas.classList.add("drawflow"),this.container.appendChild(this.precanvas),this.container.addEventListener("mouseup",this.dragEnd.bind(this)),this.container.addEventListener("mousemove",this.position.bind(this)),this.container.addEventListener("mousedown",this.click.bind(this)),this.container.addEventListener("touchend",this.dragEnd.bind(this)),this.container.addEventListener("touchmove",this.position.bind(this)),this.container.addEventListener("touchstart",this.click.bind(this)),this.container.addEventListener("contextmenu",this.contextmenu.bind(this)),this.container.addEventListener("keydown",this.key.bind(this)),this.container.addEventListener("wheel",this.zoom_enter.bind(this)),this.container.addEventListener("input",this.updateNodeValue.bind(this)),this.container.addEventListener("dblclick",this.dblclick.bind(this)),this.container.onpointerdown=this.pointerdown_handler.bind(this),this.container.onpointermove=this.pointermove_handler.bind(this),this.container.onpointerup=this.pointerup_handler.bind(this),this.container.onpointercancel=this.pointerup_handler.bind(this),this.container.onpointerout=this.pointerup_handler.bind(this),this.container.onpointerleave=this.pointerup_handler.bind(this),this.load()}pointerdown_handler(e){this.evCache.push(e)}pointermove_handler(e){for(var t=0;t100&&(n>this.prevDiff&&this.zoom_in(),n=n&&(n=parseInt(e)+1)}))})),this.nodeId=n}removeReouteConnectionSelected(){this.dispatch("connectionUnselected",!0),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})}click(e){if(this.dispatch("click",e),"fixed"===this.editor_mode){if(e.preventDefault(),"parent-drawflow"!==e.target.classList[0]&&"drawflow"!==e.target.classList[0])return!1;this.ele_selected=e.target.closest(".parent-drawflow")}else"view"===this.editor_mode?(null!=e.target.closest(".drawflow")||e.target.matches(".parent-drawflow"))&&(this.ele_selected=e.target.closest(".parent-drawflow"),e.preventDefault()):(this.first_click=e.target,this.ele_selected=e.target,0===e.button&&this.contextmenuDel(),null!=e.target.closest(".drawflow_content_node")&&(this.ele_selected=e.target.closest(".drawflow_content_node").parentElement));switch(this.ele_selected.classList[0]){case"drawflow-node":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected!=this.ele_selected&&this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.node_selected!=this.ele_selected&&this.dispatch("nodeSelected",this.ele_selected.id.slice(5)),this.node_selected=this.ele_selected,this.node_selected.classList.add("selected"),this.draggable_inputs?"SELECT"!==e.target.tagName&&(this.drag=!0):"INPUT"!==e.target.tagName&&"TEXTAREA"!==e.target.tagName&&"SELECT"!==e.target.tagName&&!0!==e.target.hasAttribute("contenteditable")&&(this.drag=!0);break;case"output":this.connection=!0,null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.drawConnection(e.target);break;case"parent-drawflow":case"drawflow":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.editor_selected=!0;break;case"main-path":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.connection_selected=this.ele_selected,this.connection_selected.classList.add("selected");const t=this.connection_selected.parentElement.classList;this.dispatch("connectionSelected",{output_id:t[2].slice(14),input_id:t[1].slice(13),output_class:t[3],input_class:t[4]}),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.add("selected")});break;case"point":this.drag_point=!0,this.ele_selected.classList.add("selected");break;case"drawflow-delete":this.node_selected&&this.removeNodeId(this.node_selected.id),this.connection_selected&&this.removeConnection(),null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null)}"touchstart"===e.type?(this.pos_x=e.touches[0].clientX,this.pos_x_start=e.touches[0].clientX,this.pos_y=e.touches[0].clientY,this.pos_y_start=e.touches[0].clientY):(this.pos_x=e.clientX,this.pos_x_start=e.clientX,this.pos_y=e.clientY,this.pos_y_start=e.clientY),(this.drag||["input","output","main-path"].includes(this.ele_selected.classList[0]))&&e.preventDefault(),this.dispatch("clickEnd",e)}position(e){if("touchmove"===e.type)var t=e.touches[0].clientX,n=e.touches[0].clientY;else t=e.clientX,n=e.clientY;if(this.connection&&this.updateConnection(t,n),this.editor_selected&&(i=this.canvas_x+-(this.pos_x-t),s=this.canvas_y+-(this.pos_y-n),this.dispatch("translate",{x:i,y:s}),this.precanvas.style.transform="translate("+i+"px, "+s+"px) scale("+this.zoom+")"),this.drag){var i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n,this.ele_selected.style.top=this.ele_selected.offsetTop-s+"px",this.ele_selected.style.left=this.ele_selected.offsetLeft-i+"px",this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_x=this.ele_selected.offsetLeft-i,this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_y=this.ele_selected.offsetTop-s,this.updateConnectionNodes(this.ele_selected.id)}if(this.drag_point){i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n;var o=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),l=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));this.ele_selected.setAttributeNS(null,"cx",o),this.ele_selected.setAttributeNS(null,"cy",l);const e=this.ele_selected.parentElement.classList[2].slice(9),c=this.ele_selected.parentElement.classList[1].slice(13),d=this.ele_selected.parentElement.classList[3],a=this.ele_selected.parentElement.classList[4];let r=Array.from(this.ele_selected.parentElement.children).indexOf(this.ele_selected)-1;if(this.reroute_fix_curvature){r-=this.ele_selected.parentElement.querySelectorAll(".main-path").length-1,r<0&&(r=0)}const h=e.slice(5),u=this.drawflow.drawflow[this.module].data[h].outputs[d].connections.findIndex((function(e,t){return e.node===c&&e.output===a}));this.drawflow.drawflow[this.module].data[h].outputs[d].connections[u].points[r]={pos_x:o,pos_y:l};const p=this.ele_selected.parentElement.classList[2].slice(9);this.updateConnectionNodes(p)}"touchmove"===e.type&&(this.mouse_x=t,this.mouse_y=n),this.dispatch("mouseMove",{x:t,y:n})}dragEnd(e){if("touchend"===e.type)var t=this.mouse_x,n=this.mouse_y,i=document.elementFromPoint(t,n);else t=e.clientX,n=e.clientY,i=e.target;if(this.drag&&(this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("nodeMoved",this.ele_selected.id.slice(5))),this.drag_point&&(this.ele_selected.classList.remove("selected"),this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("rerouteMoved",this.ele_selected.parentElement.classList[2].slice(14))),this.editor_selected&&(this.canvas_x=this.canvas_x+-(this.pos_x-t),this.canvas_y=this.canvas_y+-(this.pos_y-n),this.editor_selected=!1),!0===this.connection)if("input"===i.classList[0]||this.force_first_input&&(null!=i.closest(".drawflow_content_node")||"drawflow-node"===i.classList[0])){if(!this.force_first_input||null==i.closest(".drawflow_content_node")&&"drawflow-node"!==i.classList[0])s=i.parentElement.parentElement.id,o=i.classList[1];else{if(null!=i.closest(".drawflow_content_node"))var s=i.closest(".drawflow_content_node").parentElement.id;else var s=i.id;if(0===Object.keys(this.getNodeFromId(s.slice(5)).inputs).length)var o=!1;else var o="input_1"}var l=this.ele_selected.parentElement.parentElement.id,c=this.ele_selected.classList[1];if(l!==s&&!1!==o){if(0===this.container.querySelectorAll(".connection.node_in_"+s+".node_out_"+l+"."+c+"."+o).length){this.connection_ele.classList.add("node_in_"+s),this.connection_ele.classList.add("node_out_"+l),this.connection_ele.classList.add(c),this.connection_ele.classList.add(o);var d=s.slice(5),a=l.slice(5);this.drawflow.drawflow[this.module].data[a].outputs[c].connections.push({node:d,output:o}),this.drawflow.drawflow[this.module].data[d].inputs[o].connections.push({node:a,input:c}),this.updateConnectionNodes("node-"+a),this.updateConnectionNodes("node-"+d),this.dispatch("connectionCreated",{output_id:a,input_id:d,output_class:c,input_class:o})}else this.dispatch("connectionCancel",!0),this.connection_ele.remove();this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null;this.drag=!1,this.drag_point=!1,this.connection=!1,this.ele_selected=null,this.editor_selected=!1,this.dispatch("mouseUp",e)}contextmenu(e){if(this.dispatch("contextmenu",e),e.preventDefault(),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;if(this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove(),this.node_selected||this.connection_selected){var t=document.createElement("div");t.classList.add("drawflow-delete"),t.innerHTML="x",this.node_selected&&this.node_selected.appendChild(t),this.connection_selected&&(t.style.top=e.clientY*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))+"px",t.style.left=e.clientX*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))+"px",this.precanvas.appendChild(t))}}contextmenuDel(){this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove()}key(e){if(this.dispatch("keydown",e),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;("Delete"===e.key||"Backspace"===e.key&&e.metaKey)&&(null!=this.node_selected&&"INPUT"!==this.first_click.tagName&&"TEXTAREA"!==this.first_click.tagName&&!0!==this.first_click.hasAttribute("contenteditable")&&this.removeNodeId(this.node_selected.id),null!=this.connection_selected&&this.removeConnection())}zoom_enter(e,t){e.ctrlKey&&(e.preventDefault(),e.deltaY>0?this.zoom_out():this.zoom_in())}zoom_refresh(){this.dispatch("zoom",this.zoom),this.canvas_x=this.canvas_x/this.zoom_last_value*this.zoom,this.canvas_y=this.canvas_y/this.zoom_last_value*this.zoom,this.zoom_last_value=this.zoom,this.precanvas.style.transform="translate("+this.canvas_x+"px, "+this.canvas_y+"px) scale("+this.zoom+")"}zoom_in(){this.zoomthis.zoom_min&&(this.zoom-=this.zoom_value,this.zoom_refresh())}zoom_reset(){1!=this.zoom&&(this.zoom=1,this.zoom_refresh())}createCurvature(e,t,n,i,s,o){var l=e,c=t,d=n,a=i,r=s;switch(o){case"open":if(e>=n)var h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"close":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*r;else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"other":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;default:return" M "+l+" "+c+" C "+(h=l+Math.abs(d-l)*r)+" "+c+" "+(u=d-Math.abs(d-l)*r)+" "+a+" "+d+" "+a}}drawConnection(e){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");this.connection_ele=t;var n=document.createElementNS("http://www.w3.org/2000/svg","path");n.classList.add("main-path"),n.setAttributeNS(null,"d",""),t.classList.add("connection"),t.appendChild(n),this.precanvas.appendChild(t);var i=e.parentElement.parentElement.id.slice(5),s=e.classList[1];this.dispatch("connectionStart",{output_id:i,output_class:s})}updateConnection(e,t){const n=this.precanvas,i=this.zoom;let s=n.clientWidth/(n.clientWidth*i);s=s||0;let o=n.clientHeight/(n.clientHeight*i);o=o||0;var l=this.connection_ele.children[0],c=this.ele_selected.offsetWidth/2+(this.ele_selected.getBoundingClientRect().x-n.getBoundingClientRect().x)*s,d=this.ele_selected.offsetHeight/2+(this.ele_selected.getBoundingClientRect().y-n.getBoundingClientRect().y)*o,a=e*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),r=t*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom)),h=this.curvature,u=this.createCurvature(c,d,a,r,h,"openclose");l.setAttributeNS(null,"d",u)}addConnection(e,t,n,i){var s=this.getModuleFromNodeId(e);if(s===this.getModuleFromNodeId(t)){var o=this.getNodeFromId(e),l=!1;for(var c in o.outputs[n].connections){var d=o.outputs[n].connections[c];d.node==t&&d.output==i&&(l=!0)}if(!1===l){if(this.drawflow.drawflow[s].data[e].outputs[n].connections.push({node:t.toString(),output:i}),this.drawflow.drawflow[s].data[t].inputs[i].connections.push({node:e.toString(),input:n}),this.module===s){var a=document.createElementNS("http://www.w3.org/2000/svg","svg"),r=document.createElementNS("http://www.w3.org/2000/svg","path");r.classList.add("main-path"),r.setAttributeNS(null,"d",""),a.classList.add("connection"),a.classList.add("node_in_node-"+t),a.classList.add("node_out_node-"+e),a.classList.add(n),a.classList.add(i),a.appendChild(r),this.precanvas.appendChild(a),this.updateConnectionNodes("node-"+e),this.updateConnectionNodes("node-"+t)}this.dispatch("connectionCreated",{output_id:e,input_id:t,output_class:n,input_class:i})}}}updateConnectionNodes(e){const t="node_in_"+e,n="node_out_"+e;this.line_path;const i=this.container,s=this.precanvas,o=this.curvature,l=this.createCurvature,c=this.reroute_curvature,d=this.reroute_curvature_start_end,a=this.reroute_fix_curvature,r=this.reroute_width,h=this.zoom;let u=s.clientWidth/(s.clientWidth*h);u=u||0;let p=s.clientHeight/(s.clientHeight*h);p=p||0;const f=i.querySelectorAll("."+n);Object.keys(f).map((function(t,n){if(null===f[t].querySelector(".point")){var g=i.querySelector("#"+e),m=f[t].classList[1].replace("node_in_",""),_=i.querySelector("#"+m).querySelectorAll("."+f[t].classList[4])[0],w=_.offsetWidth/2+(_.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=_.offsetHeight/2+(_.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=g.querySelectorAll("."+f[t].classList[3])[0],C=y.offsetWidth/2+(y.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,x=y.offsetHeight/2+(y.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(C,x,w,v,o,"openclose");f[t].children[0].setAttributeNS(null,"d",n)}else{const n=f[t].querySelectorAll(".point");let o="";const g=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),m=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,m,_,d,"open");o+=y,g.push(y);f=t;var C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0];m=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,m,_,d,"close");o+=y,g.push(y)}else if(0===a){var L;f=i.querySelector("#"+e),m=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,m,_,d,"open");o+=y,g.push(y);f=t,m=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,m,_,c,"other");o+=y,g.push(y)}else if(a===n.length-1){var E,R;f=t,C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0],m=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,m,_,d,"close");o+=y,g.push(y)}else{f=t,m=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,m,_,c,"other");o+=y,g.push(y)}}),a?g.forEach((e,n)=>{f[t].children[n].setAttributeNS(null,"d",e)}):f[t].children[0].setAttributeNS(null,"d",o)}}));const g=i.querySelectorAll("."+t);Object.keys(g).map((function(t,n){if(null===g[t].querySelector(".point")){var h=i.querySelector("#"+e),f=g[t].classList[2].replace("node_out_",""),m=i.querySelector("#"+f).querySelectorAll("."+g[t].classList[3])[0],_=m.offsetWidth/2+(m.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=m.offsetHeight/2+(m.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=(h=h.querySelectorAll("."+g[t].classList[4])[0]).offsetWidth/2+(h.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,y=h.offsetHeight/2+(h.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(_,w,v,y,o,"openclose");g[t].children[0].setAttributeNS(null,"d",n)}else{const n=g[t].querySelectorAll(".point");let o="";const h=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),g=((C=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,m=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,_=(E=f.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=l(g,m,_,w,d,"close");o+=v,h.push(v);f=t;var y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0];g=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,m=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(g,m,_,w,d,"open");o+=v,h.push(v)}else if(0===a){var x;f=t,y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0],g=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,m=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(g,m,_,w,d,"open");o+=v,h.push(v);f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,g=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,m=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(g,m,_,w,c,"other");o+=v,h.push(v)}else if(a===n.length-1){var L,E;f=t,y=t.parentElement.classList[1].replace("node_in_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[4])[0],_=(E=L.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,g=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,m=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(g,m,_,w,d,"close");o+=v,h.push(v)}else{f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,g=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,m=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(g,m,_,w,c,"other");o+=v,h.push(v)}}),a?h.forEach((e,n)=>{g[t].children[n].setAttributeNS(null,"d",e)}):g[t].children[0].setAttributeNS(null,"d",o)}}))}dblclick(e){null!=this.connection_selected&&this.reroute&&this.createReroutePoint(this.connection_selected),"point"===e.target.classList[0]&&this.removeReroutePoint(e.target)}createReroutePoint(e){this.connection_selected.classList.remove("selected");const t=this.connection_selected.parentElement.classList[2].slice(9),n=this.connection_selected.parentElement.classList[1].slice(13),i=this.connection_selected.parentElement.classList[3],s=this.connection_selected.parentElement.classList[4];this.connection_selected=null;const o=document.createElementNS("http://www.w3.org/2000/svg","circle");o.classList.add("point");var l=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),c=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));o.setAttributeNS(null,"cx",l),o.setAttributeNS(null,"cy",c),o.setAttributeNS(null,"r",this.reroute_width);let d=0;if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;var a=document.createElementNS("http://www.w3.org/2000/svg","path");if(a.classList.add("main-path"),a.setAttributeNS(null,"d",""),e.parentElement.insertBefore(a,e.parentElement.children[t]),1===t)e.parentElement.appendChild(o);else{const n=Array.from(e.parentElement.children).indexOf(e);d=n,e.parentElement.insertBefore(o,e.parentElement.children[n+t+1])}}else e.parentElement.appendChild(o);const r=t.slice(5),h=this.drawflow.drawflow[this.module].data[r].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));void 0===this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points&&(this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points=[]),this.reroute_fix_curvature?(d>0||this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points!==[]?this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.splice(d,0,{pos_x:l,pos_y:c}):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),e.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),this.dispatch("addReroute",r),this.updateConnectionNodes(t)}removeReroutePoint(e){const t=e.parentElement.classList[2].slice(9),n=e.parentElement.classList[1].slice(13),i=e.parentElement.classList[3],s=e.parentElement.classList[4];let o=Array.from(e.parentElement.children).indexOf(e);const l=t.slice(5),c=this.drawflow.drawflow[this.module].data[l].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;e.parentElement.children[t-1].remove(),o-=t,o<0&&(o=0)}else o--;this.drawflow.drawflow[this.module].data[l].outputs[i].connections[c].points.splice(o,1),e.remove(),this.dispatch("removeReroute",l),this.updateConnectionNodes(t)}registerNode(e,t,n=null,i=null){this.noderegister[e]={html:t,props:n,options:i}}getNodeFromId(e){var t=this.getModuleFromNodeId(e);return JSON.parse(JSON.stringify(this.drawflow.drawflow[t].data[e]))}getNodesFromName(e){var t=[];const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){for(var o in n[i].data)n[i].data[o].name==e&&t.push(n[i].data[o].id)})),t}addNode(e,t,n,i,s,o,l,c,d=!1){if(this.useuuid)var a=this.getUuid();else a=this.nodeId;const r=document.createElement("div");r.classList.add("parent-node");const h=document.createElement("div");h.innerHTML="",h.setAttribute("id","node-"+a),h.classList.add("drawflow-node"),""!=o&&h.classList.add(...o.split(" "));const u=document.createElement("div");u.classList.add("inputs");const p=document.createElement("div");p.classList.add("outputs");const f={};for(var g=0;ge(this.noderegister[c].html,{props:this.noderegister[c].props}),...this.noderegister[c].options}).$mount();_.appendChild(e.$el)}Object.entries(l).forEach((function(e,t){if("object"==typeof e[1])!function e(t,n,i){if(null===t)t=l[n];else t=t[n];null!==t&&Object.entries(t).forEach((function(n,s){if("object"==typeof n[1])e(t,n[0],i+"-"+n[0]);else for(var o=_.querySelectorAll("[df-"+i+"-"+n[0]+"]"),l=0;lt(this.noderegister[e.html].html,{props:this.noderegister[e.html].props}),...this.noderegister[e.html].options}).$mount();c.appendChild(t.$el)}Object.entries(e.data).forEach((function(t,n){if("object"==typeof t[1])!function t(n,i,s){if(null===n)n=e.data[i];else n=n[i];null!==n&&Object.entries(n).forEach((function(e,i){if("object"==typeof e[1])t(n,e[0],s+"-"+e[0]);else for(var o=c.querySelectorAll("[df-"+s+"-"+e[0]+"]"),l=0;l{const a=e.outputs[s].connections[o].node,r=e.outputs[s].connections[o].output,h=i.querySelector(".connection.node_in_node-"+a+".node_out_node-"+e.id+"."+s+"."+r);if(n&&0===d)for(var u=0;u{this.removeSingleConnection(e.id_output,e.id,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].inputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].inputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].inputs={};const c=t.slice(6);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push(e)}),this.drawflow.drawflow[n].data[e].inputs["input_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .inputs .input").forEach((e,t)=>{const n=e.classList[1].slice(6);parseInt(c){this.drawflow.drawflow[n].data[t.node].outputs[t.input].connections.forEach((i,s)=>{if(i.node==e){const o=i.output.slice(6);if(parseInt(c){this.removeSingleConnection(e.id,e.id_input,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].outputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].outputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].outputs={};const c=t.slice(7);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push({node:e.node,output:e.output})}),this.drawflow.drawflow[n].data[e].outputs["output_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .outputs .output").forEach((e,t)=>{const n=e.classList[1].slice(7);parseInt(c){this.drawflow.drawflow[n].data[t.node].inputs[t.output].connections.forEach((i,s)=>{if(i.node==e){const o=i.input.slice(7);if(parseInt(c)-1){this.module===s&&this.container.querySelector(".connection.node_in_node-"+t+".node_out_node-"+e+"."+n+"."+i).remove();var o=this.drawflow.drawflow[s].data[e].outputs[n].connections.findIndex((function(e,n){return e.node==t&&e.output===i}));this.drawflow.drawflow[s].data[e].outputs[n].connections.splice(o,1);var l=this.drawflow.drawflow[s].data[t].inputs[i].connections.findIndex((function(t,i){return t.node==e&&t.input===n}));return this.drawflow.drawflow[s].data[t].inputs[i].connections.splice(l,1),this.dispatch("connectionRemoved",{output_id:e,input_id:t,output_class:n,input_class:i}),!0}return!1}return!1}removeConnectionNodeId(e){const t="node_in_"+e,n="node_out_"+e,i=this.container.querySelectorAll("."+n);for(var s=i.length-1;s>=0;s--){var o=i[s].classList,l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1);var c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1),i[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}const d=this.container.querySelectorAll("."+t);for(s=d.length-1;s>=0;s--){o=d[s].classList,c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1);l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1),d[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}}getModuleFromNodeId(e){var t;const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){Object.keys(n[i].data).map((function(n,s){n==e&&(t=i)}))})),t}addModule(e){this.drawflow.drawflow[e]={data:{}},this.dispatch("moduleCreated",e)}changeModule(e){this.dispatch("moduleChanged",e),this.module=e,this.precanvas.innerHTML="",this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_y=0,this.mouse_x=0,this.mouse_y=0,this.zoom=1,this.zoom_last_value=1,this.precanvas.style.transform="",this.import(this.drawflow,!1)}removeModule(e){this.module===e&&this.changeModule("Home"),delete this.drawflow.drawflow[e],this.dispatch("moduleRemoved",e)}clearModuleSelected(){this.precanvas.innerHTML="",this.drawflow.drawflow[this.module]={data:{}}}clear(){this.precanvas.innerHTML="",this.drawflow={drawflow:{Home:{data:{}}}}}export(){const e=JSON.parse(JSON.stringify(this.drawflow));return this.dispatch("export",e),e}import(e,t=!0){this.clear(),this.drawflow=JSON.parse(JSON.stringify(e)),this.load(),t&&this.dispatch("import","import")}on(e,t){return"function"!=typeof t?(console.error("The listener callback must be a function, the given type is "+typeof t),!1):"string"!=typeof e?(console.error("The event name must be a string, the given type is "+typeof e),!1):(void 0===this.events[e]&&(this.events[e]={listeners:[]}),void this.events[e].listeners.push(t))}removeListener(e,t){if(!this.events[e])return!1;const n=this.events[e].listeners,i=n.indexOf(t);i>-1&&n.splice(i,1)}dispatch(e,t){if(void 0===this.events[e])return!1;this.events[e].listeners.forEach(e=>{e(t)})}getUuid(){for(var e=[],t=0;t<36;t++)e[t]="0123456789abcdef".substr(Math.floor(16*Math.random()),1);return e[14]="4",e[19]="0123456789abcdef".substr(3&e[19]|8,1),e[8]=e[13]=e[18]=e[23]="-",e.join("")}}}]).default}));
\ No newline at end of file
diff --git a/app/webroot/js/workflows-editor/workflows-editor.js b/app/webroot/js/workflows-editor/workflows-editor.js
new file mode 100644
index 000000000..05fa604d1
--- /dev/null
+++ b/app/webroot/js/workflows-editor/workflows-editor.js
@@ -0,0 +1,192 @@
+var dotBlockDefault = doT.template(' \
+ \
+
\
+
\
+
\
+
\
+ {{=it.name}} \
+ \
+
\
+ \
+ \
+
\
+
{{=it.description}}
\
+ {{=it.block_param_html}} \
+
\
+
')
+
+var dotIF = doT.template(' \
+ \
+
\
+
\
+
\
+
\
+ {{=it.name}} \
+ \
+
\
+ then \
+ else \
+
\
+
\
+
\
+
')
+
+function sanitizeObject(obj) {
+ var newObj = {}
+ for (var key of Object.keys(obj)) {
+ var newVal = $('
').text(obj[key]).html()
+ newObj[key] = newVal
+ }
+ return newObj
+}
+
+
+function initDrawflow() {
+ editor = new Drawflow($drawflow[0]);
+ editor.start();
+
+ $('#block-tabs a').click(function (e) {
+ e.preventDefault();
+ $(this).tab('show');
+ })
+
+ $chosenWorkflows.chosen()
+ .on('change', function (evt, param) {
+ // console.log(param);
+ });
+ $chosenBlocks.chosen()
+ .on('change', function (evt, param) {
+ // console.log(param);
+ });
+
+ $('.sidebar-workflow-block').each(function () {
+ var $block = $(this)
+ all_blocks.forEach(function (block) {
+ if ($block[0].id == block['id']) {
+ $block.data('block', block)
+ }
+ });
+ })
+
+ $('.sidebar-workflow-block').each(function() {
+ if ($(this).data('block').disabled) {
+ $(this).addClass('disabled')
+ }
+ $(this).draggable({
+ helper: "clone",
+ scroll: false,
+ disabled: $(this).data('block').disabled,
+ start: function (event, ui) {
+ $(this).addClass('disabled')
+ },
+ stop: function (event, ui) {
+ $(this).removeClass('disabled')
+ // addNode($(this).data('block'), ui.position)
+ }
+ });
+ })
+
+ $canvas.droppable({
+ drop: function (event, ui) {
+ // console.log(event)
+ // console.log(ui)
+ addNode(ui.draggable.data('block'), ui.position)
+ }
+ });
+
+}
+
+function getTemplateForBlock(block) {
+ var html = block.name
+ if (block.html !== undefined) {
+ html = block.html
+ } else {
+ if (block.html_template !== undefined) {
+ html = window['dot' + block.html_template](block)
+ } else {
+ html = dotBlockDefault(block)
+ }
+ }
+ return html
+}
+
+function genBlockParamHtml(block) {
+ if (!block.params) {
+ return ''
+ }
+ var html = ''
+ block.params.forEach(function(param) {
+ paramHtml = ''
+ switch (param.type) {
+ case 'input':
+ paramHtml = genInput(param)[0].outerHTML
+ break;
+ case 'select':
+ paramHtml = genSelect(param)[0].outerHTML
+ break;
+ default:
+ break;
+ }
+ html += paramHtml
+ })
+ return html
+}
+
+function genSelect(options) {
+ var $select = $('
+
+
-
- 2 days ago
+
+ 2 days ago
+
Blocks
@@ -108,14 +110,16 @@ echo $this->element('genericElements/assetLoader', [
var $chosenWorkflows = $('.root-container .side-panel .chosen-container.workflows')
var $chosenBlocks = $('.root-container .side-panel .chosen-container.blocks')
var $drawflow = $('#drawflow')
+ var $importWorkflowButton = $('#importWorkflow')
+ var $exportWorkflowButton = $('#exportWorkflow')
var $saveWorkflowButton = $('#saveWorkflow')
var $deleteWorkflowButton = $('#deleteWorkflow')
var $lastModifiedField = $('#lastModifiedField')
var editor = false
var all_blocks = = json_encode($modules['blocks_all']) ?>;
var worklow = false
-
- var worklow = = json_encode($workflow) ?>;
+
+ var worklow = = json_encode($workflow) ?>;
$(document).ready(function() {
diff --git a/app/webroot/js/workflows-editor/workflows-editor.js b/app/webroot/js/workflows-editor/workflows-editor.js
index b862899e5..0805f64b5 100644
--- a/app/webroot/js/workflows-editor/workflows-editor.js
+++ b/app/webroot/js/workflows-editor/workflows-editor.js
@@ -68,7 +68,16 @@ function initDrawflow() {
});
$chosenBlocks.chosen()
.on('change', function (evt, param) {
- // console.log(param);
+ var selection = param.selected
+ var selected_module = all_blocks.filter(function(block) {
+ return block.id == selection
+ })
+ var canvasBR = $canvas[0].getBoundingClientRect()
+ var position = {
+ top: canvasBR.height / 2 - canvasBR.top,
+ left: canvasBR.left + canvasBR.width / 2
+ }
+ addNode(selected_module[0], position)
});
$('.sidebar-workflow-block').each(function () {
@@ -105,83 +114,11 @@ function initDrawflow() {
loadWorkflow()
$saveWorkflowButton.click(saveWorkflow)
+ $importWorkflowButton.click(importWorkflow)
+ $exportWorkflowButton.click(exportWorkflow)
}
-function getTemplateForBlock(block) {
- var html = block.name
- if (block.html !== undefined) {
- html = block.html
- } else {
- if (block.html_template !== undefined) {
- html = window['dot' + block.html_template](block)
- } else {
- html = dotBlockDefault(block)
- }
- }
- return html
-}
-
-function genBlockParamHtml(block) {
- if (!block.params) {
- return ''
- }
- var html = ''
- block.params.forEach(function(param) {
- paramHtml = ''
- switch (param.type) {
- case 'input':
- paramHtml = genInput(param)[0].outerHTML
- break;
- case 'select':
- paramHtml = genSelect(param)[0].outerHTML
- break;
- default:
- break;
- }
- html += paramHtml
- })
- return html
-}
-
-function genSelect(options) {
- var $select = $('