mirror of https://github.com/MISP/MISP
new: [UI] Allow to create object from freetext
parent
119000bf3e
commit
9153234885
|
@ -436,6 +436,7 @@ class ACLComponent extends Component
|
|||
'groupAttributesIntoObject' => array('perm_add'),
|
||||
'revise_object' => array('perm_add'),
|
||||
'view' => array('*'),
|
||||
'createFromFreetext' => ['perm_add'],
|
||||
),
|
||||
'objectReferences' => array(
|
||||
'add' => array('perm_add'),
|
||||
|
|
|
@ -1240,6 +1240,124 @@ class ObjectsController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
public function createFromFreetext($eventId)
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
|
||||
$event = $this->MispObject->Event->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.user_id', 'Event.publish_timestamp'),
|
||||
'conditions' => array('Event.id' => $eventId)
|
||||
));
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event.'));
|
||||
}
|
||||
if (!$this->__canModifyEvent($event)) {
|
||||
throw new ForbiddenException(__('You do not have permission to do that.'));
|
||||
}
|
||||
|
||||
$requestData = $this->request->data['Object'];
|
||||
$selectedTemplateId = $requestData['selectedTemplateId'];
|
||||
$template = $this->MispObject->ObjectTemplate->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'ObjectTemplate.id' => $selectedTemplateId,
|
||||
'ObjectTemplate.active' => true,
|
||||
),
|
||||
'contain' => ['ObjectTemplateElement'],
|
||||
));
|
||||
if (empty($template)) {
|
||||
throw new NotFoundException(__('Invalid template.'));
|
||||
}
|
||||
|
||||
if (isset($requestData['selectedObjectRelationMapping'])) {
|
||||
$distribution = $requestData['distribution'];
|
||||
$sharingGroupId = $requestData['sharing_group_id'] ?? 0;
|
||||
$comment = $requestData['comment'];
|
||||
if ($distribution == 4) {
|
||||
$sg = $this->MispObject->SharingGroup->fetchSG($sharingGroupId, $this->Auth->user());
|
||||
if (empty($sg)) {
|
||||
throw new NotFoundException(__('Invalid sharing group.'));
|
||||
}
|
||||
} else {
|
||||
$sharingGroupId = 0;
|
||||
}
|
||||
|
||||
$attributes = $this->_jsonDecode($requestData['attributes']);
|
||||
$selectedObjectRelationMapping = $this->_jsonDecode($requestData['selectedObjectRelationMapping']);
|
||||
|
||||
// Attach object relation to attributes and fix tag format
|
||||
foreach ($attributes as $k => &$attribute) {
|
||||
$attribute['object_relation'] = $selectedObjectRelationMapping[$k];
|
||||
if (!empty($attribute['tags'])) {
|
||||
$attribute['Tag'] = [];
|
||||
foreach (explode(",", $attribute['tags']) as $tagName) {
|
||||
$attribute['Tag'][] = [
|
||||
'name' => trim($tagName),
|
||||
];
|
||||
}
|
||||
unset($attribute['tags']);
|
||||
}
|
||||
}
|
||||
|
||||
$object = [
|
||||
'Object' => [
|
||||
'event_id' => $eventId,
|
||||
'distribution' => $distribution,
|
||||
'sharing_group_id' => $sharingGroupId,
|
||||
'comment' => $comment,
|
||||
'Attribute' => $attributes,
|
||||
],
|
||||
];
|
||||
|
||||
$object = $this->MispObject->fillObjectDataFromTemplate($object, $template);
|
||||
$result = $this->MispObject->captureObject($object, $eventId, $this->Auth->user(), true, false, $event);
|
||||
if ($result === true) {
|
||||
return $this->RestResponse->saveSuccessResponse('Objects', 'Created from Attributes', $result, 'json');
|
||||
} else {
|
||||
$error = __('Failed to create an Object from Attributes. Error: ') . PHP_EOL . h($result);
|
||||
return $this->RestResponse->saveFailResponse('Objects', 'Created from Attributes', false, $error, 'json');
|
||||
}
|
||||
} else {
|
||||
$attributes = $this->_jsonDecode($requestData['attributes']);
|
||||
|
||||
$processedAttributes = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute['type'] === 'ip-src/ip-dst') {
|
||||
$types = array('ip-src', 'ip-dst');
|
||||
} elseif ($attribute['type'] === 'ip-src|port/ip-dst|port') {
|
||||
$types = array('ip-src|port', 'ip-dst|port');
|
||||
} else {
|
||||
$types = [$attribute['type']];
|
||||
}
|
||||
foreach ($types as $type) {
|
||||
$attribute['type'] = $type;
|
||||
$processedAttributes[] = $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$attributeTypes = array_column($processedAttributes, 'type');
|
||||
$conformityResult = $this->MispObject->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $attributeTypes);
|
||||
|
||||
if ($conformityResult['valid'] !== true || !empty($conformityResult['invalidTypes'])) {
|
||||
throw new NotFoundException(__('Invalid template.'));
|
||||
}
|
||||
|
||||
$objectRelations = [];
|
||||
foreach ($template['ObjectTemplateElement'] as $templateElement) {
|
||||
$objectRelations[$templateElement['type']][] = $templateElement;
|
||||
}
|
||||
|
||||
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
|
||||
$this->set('event', $event);
|
||||
$this->set('distributionData', $distributionData);
|
||||
$this->set('distributionLevels', $this->MispObject->Attribute->distributionLevels);
|
||||
$this->set('template', $template);
|
||||
$this->set('objectRelations', $objectRelations);
|
||||
$this->set('attributes', $processedAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
private function __objectIdToConditions($id)
|
||||
{
|
||||
if (is_numeric($id)) {
|
||||
|
|
|
@ -452,7 +452,37 @@ class MispObject extends AppModel
|
|||
return false;
|
||||
}
|
||||
|
||||
public function saveObject(array $object, $eventId, $template = false, $user, $errorBehaviour = 'drop', $breakOnDuplicate = false)
|
||||
/**
|
||||
* @param array $object
|
||||
* @param array $template
|
||||
* @return array
|
||||
*/
|
||||
public function fillObjectDataFromTemplate(array $object, array $template)
|
||||
{
|
||||
$templateFields = array(
|
||||
'name' => 'name',
|
||||
'meta-category' => 'meta-category',
|
||||
'description' => 'description',
|
||||
'template_version' => 'version',
|
||||
'template_uuid' => 'uuid'
|
||||
);
|
||||
foreach ($templateFields as $objectField => $templateField) {
|
||||
$object['Object'][$objectField] = $template['ObjectTemplate'][$templateField];
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $object
|
||||
* @param int $eventId
|
||||
* @param array $template
|
||||
* @param array $user
|
||||
* @param string $errorBehaviour
|
||||
* @param bool $breakOnDuplicate
|
||||
* @return array|array[]|bool|int|mixed|string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function saveObject(array $object, $eventId, $template = false, array $user, $errorBehaviour = 'drop', $breakOnDuplicate = false)
|
||||
{
|
||||
$templateFields = array(
|
||||
'name' => 'name',
|
||||
|
|
|
@ -204,12 +204,18 @@
|
|||
</table>
|
||||
<span>
|
||||
<div class="btn-toolbar" style="float:left;">
|
||||
<button class="btn btn-primary" onclick="freetextImportResultsSubmit(<?= $event['Event']['id']; ?>, <?= count($resultArray) ?>);"><?= __('Submit %s', $scope);?></button>
|
||||
<button class="btn btn-primary" onclick="freetextImportResultsSubmit(<?= $event['Event']['id']; ?>);"><?= __('Submit %s', $scope);?></button>
|
||||
<div class="btn-group createObject" style="display: none">
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<?= __('Create object') ?>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<?php
|
||||
echo $this->Form->create('Object', ['url' => $baseurl . '/objects/createFromFreetext/' . $event['Event']['id'], 'class' => 'hidden']);
|
||||
echo $this->Form->input('selectedTemplateId', ['label' => false]);
|
||||
echo $this->Form->input('attributes', ['label' => false]);
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<?= __('Create object') ?>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -239,7 +245,7 @@
|
|||
endforeach;
|
||||
?>
|
||||
</select>
|
||||
<span role="button" tabindex="0" aria-label="<?php echo __('Apply changes to all applicable resolved attributes');?>" title="<?php echo __('Apply changes to all applicable resolved attributes');?>" class="btn btn-inverse" onClick="changeFreetextImportExecute();"><?php echo __('Change all');?></span><br />
|
||||
<span role="button" tabindex="0" aria-label="<?php echo __('Apply changes to all applicable resolved attributes');?>" title="<?php echo __('Apply changes to all applicable resolved attributes');?>" class="btn btn-inverse" onClick="changeFreetextImportExecute();"><?php echo __('Change all');?></span><br>
|
||||
<?php endif; ?>
|
||||
<input type="text" id="changeComments" style="margin-left:50px;margin-top:10px;width:446px;" placeholder="<?php echo __('Update all comment fields');?>">
|
||||
<span role="button" tabindex="0" aria-label="<?php echo __('Change all');?>" title="<?php echo __('Change all');?>" class="btn btn-inverse" onClick="changeFreetextImportCommentExecute();"><?php echo __('Change all');?></span>
|
||||
|
@ -250,7 +256,7 @@
|
|||
var options = <?php echo json_encode($optionsRearranged);?>;
|
||||
var typeCategoryMapping = <?php echo json_encode($typeCategoryMapping); ?>;
|
||||
$(function() {
|
||||
possibleObjectTemplates();
|
||||
freetextPossibleObjectTemplates();
|
||||
popoverStartup();
|
||||
$('.typeToggle').on('change', function() {
|
||||
var currentId = $(this).attr('id');
|
||||
|
@ -258,7 +264,7 @@
|
|||
currentId = currentId.replace('Type', 'Category');
|
||||
var currentOptions = typeCategoryMapping[selected];
|
||||
|
||||
possibleObjectTemplates();
|
||||
freetextPossibleObjectTemplates();
|
||||
|
||||
/*
|
||||
// Coming soon - restrict further if a list of categories is passed by the modules / freetext import tool
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<div class="index">
|
||||
<h3><?php echo __('Create object');?></h3>
|
||||
<?= $this->Form->create('Object', array('url' => $baseurl . '/objects/createFromFreetext/' . $event['Event']['id'])); ?>
|
||||
<dl style="margin-top: 10px;">
|
||||
<dt><?php echo __('Object Template');?></dt>
|
||||
<dd><a href="<?php echo $baseurl . '/ObjectTemplates/view/' . h($template['ObjectTemplate']['id']) ?>"><?php echo Inflector::humanize(h($template['ObjectTemplate']['name'])) . ' v' . h($template['ObjectTemplate']['version']); ?></a></dd>
|
||||
<dt><?php echo __('Description');?></dt>
|
||||
<dd><?php echo h($template['ObjectTemplate']['description']); ?></dd>
|
||||
<dt><?php echo __('Meta category');?></dt>
|
||||
<dd><?php echo h($template['ObjectTemplate']['meta-category']); ?></dd>
|
||||
<dt><?php echo __('Distribution');?></dt>
|
||||
<dd>
|
||||
<?php echo $this->Form->input('Object.distribution', array(
|
||||
'class' => 'Object_distribution_select',
|
||||
'options' => $distributionData['levels'],
|
||||
'default' => $distributionData['initial'],
|
||||
'label' => false,
|
||||
'style' => 'margin-bottom:5px;',
|
||||
'div' => false
|
||||
)); ?>
|
||||
<?php echo $this->Form->input('Object.sharing_group_id', array(
|
||||
'class' => 'Object_sharing_group_id_select',
|
||||
'options' => $distributionData['sgs'],
|
||||
'label' => false,
|
||||
'div' => false,
|
||||
'style' => 'display:none;margin-bottom:5px;',
|
||||
'value' => 0
|
||||
)); ?>
|
||||
<dt><?php echo __('Comment');?></dt>
|
||||
<dd>
|
||||
<?php echo $this->Form->input('Object.comment', array(
|
||||
'type' => 'textarea',
|
||||
'style' => 'height:20px;width:400px;',
|
||||
'required' => false,
|
||||
'allowEmpty' => true,
|
||||
'label' => false,
|
||||
'div' => false
|
||||
)); ?>
|
||||
<div class="hidden">
|
||||
<?php
|
||||
echo $this->Form->input('selectedTemplateId', array('type' => 'hidden', 'value' => $template['ObjectTemplate']['id']));
|
||||
echo $this->Form->input('attributes', array('type' => 'hidden', 'value' => JsonTool::encode($attributes)));
|
||||
echo $this->Form->input('selectedObjectRelationMapping', ['value' => '', 'label' => false]);
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div style="border: 2px solid #3465a4; border-radius: 3px; overflow: hidden;">
|
||||
<table class="table table-striped table-condensed" style="margin-bottom: 0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php echo __('Name :: Type'); ?></th>
|
||||
<th><?php echo __('Category'); ?></th>
|
||||
<th><?php echo __('Value'); ?></th>
|
||||
<th><?php echo __('To IDS'); ?></th>
|
||||
<th><?php echo __('Disable Correlation'); ?></th>
|
||||
<th><?php echo __('Comment'); ?></th>
|
||||
<th><?php echo __('Distribution'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="attributeMappingBody">
|
||||
<?php foreach ($attributes as $attribute): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span style="display: block;">
|
||||
<select class="attributeMapping" style="margin-bottom: 5px;">
|
||||
<?php foreach ($objectRelations[$attribute['type']] as $object_relation): ?>
|
||||
<option title="<?= h($object_relation['description']); ?>"><?= h($object_relation['object_relation']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
:: <?= h($attribute['type']); ?>
|
||||
</span>
|
||||
<i class="objectRelationDescription apply_css_arrow"><?= h($objectRelations[$attribute['type']][0]['description']); ?></i>
|
||||
</td>
|
||||
<td><?= h($attribute['category']); ?></td>
|
||||
<td style="white-space: nowrap;"><?= h($attribute['value']); ?></td>
|
||||
<td><?= $attribute['to_ids'] ? __('Yes') : __('No') ?></td>
|
||||
<td><?= $attribute['disable_correlation'] ? __('Yes') : __('No') ?></td>
|
||||
<td><?= h($attribute['comment']) ?></td>
|
||||
<td><?= h($distributionLevels[$attribute['distribution']]); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px; text-align: center;">
|
||||
<button class="btn btn-primary" onclick="submitCreateFromFreetext();"><?= __('Create Object'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(".Object_distribution_select").change(function() {
|
||||
checkAndEnable($(this).parent().find('.Object_sharing_group_id_select'), $(this).val() == 4);
|
||||
});
|
||||
|
||||
$(".attributeMapping").change(function() {
|
||||
var $select = $(this);
|
||||
var text = $select.find(":selected").attr('title');
|
||||
$select.parent().parent().find('.objectRelationDescription').text(text);
|
||||
});
|
||||
|
||||
function submitCreateFromFreetext() {
|
||||
var $form = $('#ObjectCreateFromFreetextForm');
|
||||
var attribute_mapping = [];
|
||||
$('#attributeMappingBody').find('tr').each(function() {
|
||||
attribute_mapping.push($(this).find('.attributeMapping').val());
|
||||
});
|
||||
$('#ObjectSelectedObjectRelationMapping').val(JSON.stringify(attribute_mapping));
|
||||
|
||||
xhr({
|
||||
type: "post",
|
||||
data: $form.serialize(),
|
||||
url: $form.attr('action'),
|
||||
success: function(data) {
|
||||
if (data.saved) {
|
||||
window.location = baseurl + '/events/view/' + <?= $event['Event']['id'] ?>;
|
||||
} else {
|
||||
showMessage('fail', data.errors);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'event', 'menuItem' => 'freetextResults']);
|
|
@ -2772,9 +2772,10 @@ function importChoiceSelect(url, elementId, ajax) {
|
|||
}
|
||||
}
|
||||
|
||||
function freetextImportResultsSubmit(event_id, count) {
|
||||
function freetextSerializeAttributes() {
|
||||
var attributeArray = [];
|
||||
for (var i = 0; i < count; i++) {
|
||||
$('.freetext_row').each(function() {
|
||||
var i = $(this).data('row');
|
||||
if ($('#Attribute' + i + 'Save').val() == 1) {
|
||||
attributeArray.push({
|
||||
value:$('#Attribute' + i + 'Value').val(),
|
||||
|
@ -2790,7 +2791,12 @@ function freetextImportResultsSubmit(event_id, count) {
|
|||
tags:$('#Attribute' + i + 'Tags').val()
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
return attributeArray;
|
||||
}
|
||||
|
||||
function freetextImportResultsSubmit(event_id, count) {
|
||||
var attributeArray = freetextSerializeAttributes();
|
||||
$("#AttributeJsonObject").val(JSON.stringify(attributeArray));
|
||||
var formData = $(".mainForm").serialize();
|
||||
xhr({
|
||||
|
@ -2808,20 +2814,37 @@ function freetextRemoveRow(id, event_id) {
|
|||
$('#Attribute' + id + 'Save').attr("value", "0");
|
||||
if ($(".freetext_row:visible").length == 0) {
|
||||
window.location = baseurl + "/events/" + event_id;
|
||||
} else {
|
||||
freetextPossibleObjectTemplates();
|
||||
}
|
||||
}
|
||||
|
||||
function possibleObjectTemplates() {
|
||||
function freetextCreateObject(objectId) {
|
||||
var attributeArray = freetextSerializeAttributes();
|
||||
$('#ObjectSelectedTemplateId').val(objectId);
|
||||
$('#ObjectAttributes').val(JSON.stringify(attributeArray));
|
||||
$('#ObjectFreeTextImportForm').submit();
|
||||
}
|
||||
|
||||
function freetextPossibleObjectTemplates() {
|
||||
var allTypes = [];
|
||||
$('.freetext_row').each(function () {
|
||||
var rowId = $(this).data('row');
|
||||
if ($('#Attribute' + rowId + 'Save').val() === "1") {
|
||||
allTypes.push($(this).find('.typeToggle').val());
|
||||
var type = $(this).find('.typeToggle').val();
|
||||
if (type === 'ip-src/ip-dst') {
|
||||
allTypes.push('ip-src', 'ip-dst');
|
||||
} else if (type === 'ip-src|port/ip-dst|port') {
|
||||
allTypes.push('ip-src|port', 'ip-dst|port');
|
||||
} else {
|
||||
allTypes.push(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (allTypes.length < 2) {
|
||||
$('.createObject').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
|
@ -2830,24 +2853,28 @@ function possibleObjectTemplates() {
|
|||
success: function (data) {
|
||||
if (data.length === 0) {
|
||||
$('.createObject').hide();
|
||||
} else {
|
||||
var $menu = $('.createObject ul');
|
||||
$menu.find('li').remove();
|
||||
|
||||
$.each(data, function (i, template) {
|
||||
var a = document.createElement('a');
|
||||
a.href = '#';
|
||||
a.textContent = template.name;
|
||||
a.title = template.description;
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.appendChild(a);
|
||||
|
||||
$menu.append(li);
|
||||
});
|
||||
|
||||
$('.createObject').show();
|
||||
return;
|
||||
}
|
||||
|
||||
var $menu = $('.createObject ul');
|
||||
$menu.find('li').remove();
|
||||
|
||||
$.each(data, function (i, template) {
|
||||
var a = document.createElement('a');
|
||||
a.href = '#';
|
||||
a.onclick = function () {
|
||||
freetextCreateObject(template['id']);
|
||||
};
|
||||
a.textContent = template.name;
|
||||
a.title = template.description;
|
||||
|
||||
var li = document.createElement('li');
|
||||
li.appendChild(a);
|
||||
|
||||
$menu.append(li);
|
||||
});
|
||||
|
||||
$('.createObject').show();
|
||||
},
|
||||
type: "post",
|
||||
url: baseurl + "/objectTemplates/possibleObjectTemplates",
|
||||
|
|
Loading…
Reference in New Issue