mirror of https://github.com/MISP/MISP
new: [eventReport] Added support of tags
parent
fc9c77b917
commit
c2859cdba3
|
@ -27,6 +27,12 @@ class TagsController extends AppController
|
|||
|
||||
public $helpers = array('TextColour');
|
||||
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
$this->Security->unlockedActions[] = 'search';
|
||||
}
|
||||
|
||||
public function index($favouritesOnly = false)
|
||||
{
|
||||
$this->loadModel('Attribute');
|
||||
|
@ -1106,7 +1112,7 @@ class TagsController extends AppController
|
|||
$this->render('/Events/view_graph');
|
||||
}
|
||||
|
||||
public function search($tag = false)
|
||||
public function search($tag = false, $strictTagNameOnly = false)
|
||||
{
|
||||
if (isset($this->request->data['Tag'])) {
|
||||
$this->request->data = $this->request->data['Tag'];
|
||||
|
@ -1119,26 +1125,31 @@ class TagsController extends AppController
|
|||
if (!is_array($tag)) {
|
||||
$tag = array($tag);
|
||||
}
|
||||
$conditions = array();
|
||||
foreach ($tag as $k => $t) {
|
||||
$tag[$k] = strtolower($t);
|
||||
$conditions['OR'][] = array('LOWER(GalaxyCluster.value)' => $tag[$k]);
|
||||
}
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('AND' => array('GalaxyElement.key' => 'synonyms', 'LOWER(GalaxyElement.value) LIKE' => $t));
|
||||
}
|
||||
$this->loadModel('GalaxyCluster');
|
||||
$elements = $this->GalaxyCluster->GalaxyElement->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'contain' => array('GalaxyCluster.tag_name')
|
||||
));
|
||||
foreach ($elements as $element) {
|
||||
$tag[] = strtolower($element['GalaxyCluster']['tag_name']);
|
||||
}
|
||||
$conditions = array();
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('LOWER(Tag.name) LIKE' => $t);
|
||||
if (!$strictTagNameOnly) {
|
||||
foreach ($tag as $k => $t) {
|
||||
$tag[$k] = strtolower($t);
|
||||
$conditions['OR'][] = array('LOWER(GalaxyCluster.value)' => $tag[$k]);
|
||||
}
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('AND' => array('GalaxyElement.key' => 'synonyms', 'LOWER(GalaxyElement.value) LIKE' => $t));
|
||||
}
|
||||
$elements = $this->GalaxyCluster->GalaxyElement->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'contain' => array('GalaxyCluster.tag_name')
|
||||
));
|
||||
foreach ($elements as $element) {
|
||||
$tag[] = strtolower($element['GalaxyCluster']['tag_name']);
|
||||
}
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('LOWER(Tag.name) LIKE' => $t);
|
||||
}
|
||||
} else {
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('Tag.name' => $t);
|
||||
}
|
||||
}
|
||||
$tags = $this->Tag->find('all', array(
|
||||
'conditions' => $conditions,
|
||||
|
@ -1146,9 +1157,10 @@ class TagsController extends AppController
|
|||
));
|
||||
$this->loadModel('Taxonomy');
|
||||
foreach ($tags as $k => $t) {
|
||||
$taxonomy = $this->Taxonomy->getTaxonomyForTag($t['Tag']['name'], true);
|
||||
$taxonomy = $this->Taxonomy->getTaxonomyForTag($t['Tag']['name'], false);
|
||||
if (!empty($taxonomy)) {
|
||||
$tags[$k]['Taxonomy'] = $taxonomy['Taxonomy'];
|
||||
$tags[$k]['TaxonomyPredicate'] = $taxonomy['TaxonomyPredicate'][0];
|
||||
}
|
||||
$cluster = $this->GalaxyCluster->getCluster($t['Tag']['name']);
|
||||
if (!empty($cluster)) {
|
||||
|
|
|
@ -25,11 +25,14 @@ var markdownModelFieldNameForSave = 'content';
|
|||
var dotTemplateAttribute = doT.template("<span class=\"misp-element-wrapper attribute useCursorPointer\" data-scope=\"{{=it.scope}}\" data-elementid=\"{{=it.elementid}}\"><span class=\"bold\"><span>{{=it.type}}</span><span class=\"blue\"> {{=it.value}}</span></span></span>");
|
||||
var dotTemplateAttributePicture = doT.template("<div class=\"misp-picture-wrapper attributePicture useCursorPointer\"><img data-scope=\"{{=it.scope}}\" data-elementid=\"{{=it.elementid}}\" href=\"#\" src=\"{{=it.src}}\" alt=\"{{=it.alt}}\" title=\"\"/></div>");
|
||||
var dotTemplateGalaxyMatrix = doT.template("<div class=\"misp-picture-wrapper embeddedGalaxyMatrix\" data-scope=\"{{=it.scope}}\" data-elementid=\"{{=it.elementid}}\" data-eventid=\"{{=it.eventid}}\"></div>");
|
||||
var dotTemplateTag = doT.template("<span class=\"tag embeddedTag useCursorPointer\" data-scope=\"{{=it.scope}}\" data-elementid=\"{{!it.elementid}}\" data-eventid=\"{{=it.eventid}}\">{{=it.elementid}}</span>");
|
||||
var dotTemplateObject = doT.template("<span class=\"misp-element-wrapper object useCursorPointer\" data-scope=\"{{=it.scope}}\" data-elementid=\"{{=it.elementid}}\"><span class=\"bold\"><span>{{=it.type}}</span><span class=\"value\">{{=it.value}}</span></span></span>");
|
||||
var dotTemplateInvalid = doT.template("<span class=\"misp-element-wrapper invalid\"><span class=\"bold red\">{{=it.scope}}<span class=\"blue\"> ({{=it.id}})</span></span></span>");
|
||||
var dotCloseButtonTemplate = doT.template('<button type="button" class="close" style="margin-left: 5px;" data-scope=\"{{=it.scope}}\" data-elementid=\"{{!it.elementID}}\" onclick="closeThePopover(this)">×</button>');
|
||||
|
||||
var galaxyMatrixTimer;
|
||||
var cache_matrix = {};
|
||||
var galaxyMatrixTimer, tagTimers = {};
|
||||
var cache_matrix = {}, cache_tag = {};
|
||||
proxyMISPElements['tag'] = []
|
||||
|
||||
/**
|
||||
_____ _ __ __ _
|
||||
|
@ -72,6 +75,11 @@ function MISPElementReplacementActions(action) {
|
|||
end = null
|
||||
setCursorTo = {line: start.line, ch: start.ch + replacement.length - 1}
|
||||
break;
|
||||
case 'tag':
|
||||
replacement = '@[tag]()'
|
||||
end = null
|
||||
setCursorTo = {line: start.line, ch: start.ch + replacement.length - 1}
|
||||
break;
|
||||
case 'galaxymatrix':
|
||||
replacement = '@[galaxymatrix]()'
|
||||
end = null
|
||||
|
@ -97,6 +105,7 @@ function insertMISPElementToolbarButtons() {
|
|||
insertTopToolbarButton('cube', 'attribute')
|
||||
insertTopToolbarButton('cubes', 'object')
|
||||
insertTopToolbarButton('image', 'attribute-attachment')
|
||||
insertTopToolbarButton('tag', 'tag')
|
||||
insertTopToolbarButton('table', 'galaxy-matrix')
|
||||
}
|
||||
|
||||
|
@ -280,7 +289,11 @@ function MISPElementRule(state, startLine, endLine, silent) {
|
|||
pos = labelEnd + 1;
|
||||
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
|
||||
start = pos;
|
||||
res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);
|
||||
if (scope == 'tag') { // tags may contain spaces
|
||||
res = parseTag(state.src, pos, state.posMax);
|
||||
} else {
|
||||
res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax);
|
||||
}
|
||||
if (res.ok) {
|
||||
// parseLinkDestination does not support trailing characters such as `.` after the link
|
||||
// so we have to find the matching `)`
|
||||
|
@ -305,9 +318,16 @@ function MISPElementRule(state, startLine, endLine, silent) {
|
|||
}
|
||||
pos++;
|
||||
|
||||
var reUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
|
||||
if (!reUUID.test(elementID)) {
|
||||
return false;
|
||||
if (scope == 'tag') {
|
||||
var reTagName = /^[^\n)]+$/
|
||||
if (!reTagName.test(elementID)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
var reUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/
|
||||
if (!reUUID.test(elementID)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We found the end of the link, and know for a fact it's a valid link;
|
||||
|
@ -332,7 +352,7 @@ function MISPElementRule(state, startLine, endLine, silent) {
|
|||
|
||||
/* Rendering rules */
|
||||
function MISPElementRenderer(tokens, idx, options, env, slf) {
|
||||
var allowedScope = ['attribute', 'object', 'galaxymatrix']
|
||||
var allowedScope = ['attribute', 'object', 'galaxymatrix', 'tag']
|
||||
var token = tokens[idx];
|
||||
var scope = token.content.scope
|
||||
var elementID = token.content.elementID
|
||||
|
@ -384,8 +404,10 @@ function renderMISPElement(scope, elementID) {
|
|||
})
|
||||
return dotTemplateObject(templateVariables);
|
||||
}
|
||||
} else if (scope == 'tag') {
|
||||
return dotTemplateTag(sanitizeObject({scope: 'tag', elementid: elementID, eventid: eventid}));
|
||||
} else if (scope == 'galaxymatrix') {
|
||||
return dotTemplateGalaxyMatrix({scope: 'galaxymatrix', elementid: elementID, eventid: eventid});
|
||||
return dotTemplateGalaxyMatrix(sanitizeObject({scope: 'galaxymatrix', elementid: elementID, eventid: eventid}));
|
||||
}
|
||||
return renderInvalidMISPElement(scope, elementID)
|
||||
}
|
||||
|
@ -441,6 +463,14 @@ function setupMISPElementMarkdownListeners() {
|
|||
title: getTitleFromMISPElementDOM,
|
||||
content: getContentFromMISPElementDOM
|
||||
})
|
||||
$('.embeddedTag').popover({
|
||||
trigger: 'click',
|
||||
html: true,
|
||||
container: 'body',
|
||||
placement: 'top',
|
||||
title: getTitleFromMISPElementDOM,
|
||||
content: getContentFromMISPElementDOM
|
||||
})
|
||||
}
|
||||
|
||||
function attachRemoteMISPElements() {
|
||||
|
@ -459,6 +489,22 @@ function attachRemoteMISPElements() {
|
|||
$div.html(cache_matrix[cacheKey])
|
||||
}
|
||||
})
|
||||
$('.embeddedTag[data-scope="tag"]').each(function() {
|
||||
var $div = $(this)
|
||||
$div.append($('<span/>').append(loadingSpanAnimation))
|
||||
var eventID = $div.data('eventid')
|
||||
var elementID = $div.data('elementid')
|
||||
var cacheKey = eventid + '-' + elementID
|
||||
clearTimeout(tagTimers[cacheKey]);
|
||||
if (cache_tag[cacheKey] === undefined) {
|
||||
tagTimers[cacheKey] = setTimeout(function() {
|
||||
attachTagInfo($div, eventID, elementID)
|
||||
// }, slowDebounceDelay);
|
||||
}, debounceDelay);
|
||||
} else {
|
||||
$div.html(cache_tag[cacheKey])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function attachGalaxyMatrix($elem, eventid, elementID) {
|
||||
|
@ -505,6 +551,54 @@ function attachGalaxyMatrix($elem, eventid, elementID) {
|
|||
})
|
||||
}
|
||||
|
||||
function attachTagInfo($elem, eventid, elementID) {
|
||||
$.ajax({
|
||||
data: {
|
||||
"tag": elementID,
|
||||
},
|
||||
success:function(data, textStatus) {
|
||||
var $tag
|
||||
data = $.parseJSON(data)
|
||||
var tagData;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var tag = data[i];
|
||||
if (tag.Tag.name == elementID) {
|
||||
tagData = data[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (tagData === undefined) {
|
||||
tagData = {}
|
||||
$tag = $('<span/>').text(elementID).addClass('tag').css('box-shadow', '3px 3px 3px #888888')
|
||||
} else {
|
||||
$tag = $('<span/>').text(tagData.Tag.name)
|
||||
.addClass('tag')
|
||||
.css({
|
||||
'box-shadow': '3px 3px 3px #888888',
|
||||
'background-color': tagData.Tag.colour,
|
||||
'color': getTextColour(tagData.Tag.colour)
|
||||
})
|
||||
}
|
||||
$elem.empty().append($tag)
|
||||
proxyMISPElements['tag'][elementID] = tagData
|
||||
var cacheKey = eventid + '-' + elementID
|
||||
cache_tag[cacheKey] = $tag[0].outerHTML;
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
var templateVariables = sanitizeObject({
|
||||
scope: 'Error while fetching tag',
|
||||
id: elementID
|
||||
})
|
||||
var placeholder = dotTemplateInvalid(templateVariables)
|
||||
$elem.empty()
|
||||
.css({'text-align': 'center'})
|
||||
.append($(placeholder))
|
||||
},
|
||||
type:"post",
|
||||
url: baseurl + "/tags/search/0/1"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
_____ _
|
||||
|
@ -590,8 +684,9 @@ function getTitleFromMISPElementDOM() {
|
|||
var title = invalidMessage
|
||||
var dismissButton = ''
|
||||
if (data !== false) {
|
||||
dismissButton = '<button type="button" class="close" style="margin-left: 5px;" data-scope="' + data.scope + '" data-elementid="' + data.elementID + '" onclick="closeThePopover(this)">×</button>';
|
||||
title = data.scope.charAt(0).toUpperCase() + data.scope.slice(1) + ' ' + data.elementID
|
||||
var templateVariables = sanitizeObject(data)
|
||||
var dismissButton = dotCloseButtonTemplate(templateVariables)
|
||||
title = data.scope.charAt(0).toUpperCase() + templateVariables.scope.slice(1) + ' ' + templateVariables.elementID
|
||||
}
|
||||
return title + dismissButton
|
||||
}
|
||||
|
@ -600,7 +695,7 @@ function getTitleFromMISPElementDOM() {
|
|||
function closeThePopover(closeButton) {
|
||||
var scope = $(closeButton).data('scope')
|
||||
var elementID = $(closeButton).data('elementid')
|
||||
var $MISPElement = $('[data-scope="' + scope + '"][data-elementid="' + elementID + '"]')
|
||||
var $MISPElement = $('[data-scope="' + scope + '"][data-elementid="' + elementID.replaceAll('\"', '\\\"') + '"]')
|
||||
$MISPElement.popover('hide');
|
||||
}
|
||||
|
||||
|
@ -699,9 +794,104 @@ function getPriorityValue(mispObject, objectTemplate) {
|
|||
return false
|
||||
}
|
||||
|
||||
function constructTag(tagName) {
|
||||
var tagData = proxyMISPElements['tag'][tagName]
|
||||
var $info
|
||||
if (tagData.Taxonomy !== undefined) {
|
||||
$info = constructTaxonomyInfo(tagData)
|
||||
} else if(tagData.GalaxyCluster !== undefined) {
|
||||
$info = constructGalaxyInfo(tagData)
|
||||
} else {
|
||||
$info = 'No information about this tag'
|
||||
}
|
||||
return $('<div/>').append($info)
|
||||
}
|
||||
|
||||
function constructTaxonomyInfo(tagData) {
|
||||
var cacheKey = eventid + '-' + tagData.Tag.name
|
||||
var tagHTML = cache_tag[cacheKey]
|
||||
var $tag = $(tagHTML)
|
||||
var $predicate = $('<div/>').append(
|
||||
$('<span/>').append($tag),
|
||||
$('<h3/>').text('Predicate info'),
|
||||
$('<p/>').append(
|
||||
$('<strong/>').text('Expanded tag: '),
|
||||
$('<span/>').text(tagData.TaxonomyPredicate.expanded),
|
||||
),
|
||||
$('<p/>').append(
|
||||
$('<strong/>').text('Description: '),
|
||||
$('<span/>').text(tagData.TaxonomyPredicate.description),
|
||||
)
|
||||
)
|
||||
var $meta = $('<div/>').append(
|
||||
$('<h3/>').text('Taxonomy info'),
|
||||
$('<p/>').append(
|
||||
$('<strong/>').text(tagData.Taxonomy.namespace + ': '),
|
||||
$('<span/>').text(tagData.Taxonomy.description),
|
||||
)
|
||||
)
|
||||
return $('<div/>').append($predicate, $meta)
|
||||
}
|
||||
|
||||
function constructGalaxyInfo(tagData) {
|
||||
var cacheKey = eventid + '-' + tagData.Tag.name
|
||||
var tagHTML = cache_tag[cacheKey]
|
||||
var $tag = $(tagHTML)
|
||||
var $cluster = $('<div/>').append(
|
||||
$('<span/>').append($tag),
|
||||
$('<h3/>').text('Cluster info'),
|
||||
)
|
||||
var fields = ['description', 'source', 'author']
|
||||
fields.forEach(function(field) {
|
||||
$cluster.append(
|
||||
$('<div/>').css({
|
||||
'max-height': '100px',
|
||||
'overflow-y': 'auto',
|
||||
})
|
||||
.append(
|
||||
$('<strong/>').text(field + ': '),
|
||||
$('<span/>').text(tagData.GalaxyCluster[field] === undefined || tagData.GalaxyCluster[field].length == 0 ? '-' : tagData.GalaxyCluster[field]),
|
||||
)
|
||||
)
|
||||
})
|
||||
var $clusterMeta = $('<div/>').css({
|
||||
'height': '100px',
|
||||
'overflow-y': 'auto',
|
||||
'resize': 'vertical',
|
||||
'border': '1px solid #0088cc',
|
||||
'border-radius': '3px',
|
||||
'padding': '5px'
|
||||
})
|
||||
if (tagData.GalaxyCluster.meta !== undefined) {
|
||||
Object.keys(tagData.GalaxyCluster.meta).forEach(function(metaKey) {
|
||||
var metaValue = tagData.GalaxyCluster.meta[metaKey]
|
||||
$clusterMeta.append(
|
||||
$('<div/>').append(
|
||||
$('<strong/>').addClass('blue').text(metaKey + ': '),
|
||||
$('<span/>').text(metaValue),
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
$cluster.append($clusterMeta)
|
||||
var $galaxy = $('<div/>').append(
|
||||
$('<h3/>').text('Galaxy info'),
|
||||
$('<div/>').append(
|
||||
$('<div/>').append(
|
||||
$('<strong/>').text('Name: '),
|
||||
$('<span/>').text(tagData.GalaxyCluster.Galaxy.name),
|
||||
),
|
||||
$('<div/>').append(
|
||||
$('<strong/>').text('Description: '),
|
||||
$('<span/>').text(tagData.GalaxyCluster.Galaxy.description),
|
||||
)
|
||||
)
|
||||
)
|
||||
return $('<div/>').append($cluster, $galaxy)
|
||||
}
|
||||
|
||||
function getContentFromMISPElementDOM() {
|
||||
var data = getElementFromDom(this)
|
||||
|
||||
if (data !== false) {
|
||||
if (data.scope == 'attribute') {
|
||||
var $thead = constructAttributeHeader(data.element)
|
||||
|
@ -716,7 +906,54 @@ function getContentFromMISPElementDOM() {
|
|||
} else if (data.scope == 'object') {
|
||||
var $object = constructObject(data.element)
|
||||
return $object.html()
|
||||
} else if (data.scope == 'tag') {
|
||||
var $tag = constructTag(data.elementID)
|
||||
return $tag.html()
|
||||
}
|
||||
}
|
||||
return invalidMessage
|
||||
}
|
||||
|
||||
function parseTag(str, pos, max) {
|
||||
var level = 0
|
||||
var lines = 0
|
||||
var code
|
||||
var start = pos
|
||||
var result = {
|
||||
ok: false,
|
||||
pos: 0,
|
||||
lines: 0,
|
||||
str: ''
|
||||
};
|
||||
while (pos < max) {
|
||||
code = str.charCodeAt(pos);
|
||||
|
||||
// ascii control characters
|
||||
if (code < 0x20 || code === 0x7F) { break; }
|
||||
|
||||
if (code === 0x5C /* \ */ && pos + 1 < max) {
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code === 0x28 /* ( */) {
|
||||
level++;
|
||||
}
|
||||
|
||||
if (code === 0x29 /* ) */) {
|
||||
if (level === 0) { break; }
|
||||
level--;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (start === pos) { return result; }
|
||||
if (level !== 0) { return result; }
|
||||
|
||||
result.str = md.utils.unescapeAll(str.slice(start, pos));
|
||||
result.lines = lines;
|
||||
result.pos = pos;
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue