new: Added new feature to block attributes from IDS sensitive exports based on proposals

- Enabled via a new server setting (MISP.proposals_block_attributes)
- Attributes are skipped from exports that require the to_ids flag if:
  - they have an active proposal that proposes to remove the to_ids flag
  - they have an active proposal that proposes to delete the attribute

- Currently affected exports:
  - OpenIOC
  - All HIDS exports
  - All NIDS exports
  - All text exports
  - RPZ Zone file export
pull/1637/head
Iglocska 2016-10-26 18:05:15 +02:00
parent 97512bd092
commit ebdaa11312
4 changed files with 71 additions and 23 deletions

View File

@ -1868,7 +1868,9 @@ class AttributesController extends AppController {
$this->header('Content-Disposition: download; filename="misp.rpz.' . $file . 'txt"');
$this->layout = 'text/default';
$this->loadModel('Whitelist');
$values = $this->Whitelist->removeWhitelistedValuesFromArray($values);
foreach ($values as $key => $value) {
$values[$key] = $this->Whitelist->removeWhitelistedValuesFromArray($value);
}
$this->set('values', $values);
$this->set('rpzSettings', $rpzSettings);
$this->render('/Attributes/rpz');

View File

@ -1317,7 +1317,7 @@ class Attribute extends AppModel {
public function text($user, $type, $tags = false, $eventId = false, $allowNonIDS = false, $from = false, $to = false, $last = false) {
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array();
if ($allowNonIDS === false) $conditions['AND'] = array('Attribute.to_ids =' => 1, 'Event.published =' => 1);
if ($allowNonIDS === false) $conditions['AND'] = array('Attribute.to_ids' => 1, 'Event.published' => 1);
if ($type !== 'all') $conditions['AND']['Attribute.type'] = $type;
if ($from) $conditions['AND']['Event.date >='] = $from;
if ($to) $conditions['AND']['Event.date <='] = $to;
@ -1378,13 +1378,12 @@ class Attribute extends AppModel {
}
$values = array();
foreach ($typesToFetch as $k => $v) {
$tempConditions = $conditions;
$tempConditions['type'] = $v;
$temp = $this->fetchAttributes(
$user,
array(
'conditions' => array(
$conditions,
array('type' => $v),
),
'conditions' => $tempConditions,
'fields' => array('Attribute.value'), // array of field names
)
);
@ -1413,7 +1412,7 @@ class Attribute extends AppModel {
function bro($user, $type, $tags = false, $eventId = false, $from = false, $to = false, $last = false) {
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array('Attribute.to_ids =' => 1, 'Event.published =' => 1);
$conditions['AND'] = array('Attribute.to_ids' => 1, 'Event.published' => 1);
if ($from) $conditions['AND']['Event.date >='] = $from;
if ($to) $conditions['AND']['Event.date <='] = $to;
if ($last) $conditions['AND']['Event.publish_timestamp >='] = $last;
@ -1799,6 +1798,24 @@ class Attribute extends AppModel {
);
if (isset($options['contain'])) $params['contain'] = array_merge_recursive($params['contain'], $options['contain']);
else $option['contain']['Event']['fields'] = array('id', 'info', 'org_id', 'orgc_id');
if (Configure::read('MISP.proposals_block_attributes') && isset($options['conditions']['AND']['Attribute.to_ids']) && $options['conditions']['AND']['Attribute.to_ids'] == 1) {
$this->bindModel(array('hasMany' => array('ShadowAttribute' => array('foreignKey' => 'old_id'))));
$proposalRestriction = array(
'ShadowAttribute' => array(
'conditions' => array(
'AND' => array(
'ShadowAttribute.deleted' => 0,
'OR' => array(
'ShadowAttribute.proposal_to_delete' => 1,
'ShadowAttribute.to_ids' => 0
)
)
),
'fields' => array('ShadowAttribute.id')
)
);
$params['contain'] = array_merge($params['contain'], $proposalRestriction);
}
if (isset($options['fields'])) $params['fields'] = $options['fields'];
if (isset($options['conditions'])) $params['conditions']['AND'][] = $options['conditions'];
if (isset($options['order'])) $params['order'] = $options['order'];
@ -1807,6 +1824,16 @@ class Attribute extends AppModel {
if (isset($options['group'])) $params['group'] = array_merge(array('Attribute.id'), $options['group']);
if (Configure::read('MISP.unpublishedprivate')) $params['conditions']['AND'][] = array('OR' => array('Event.published' => 1, 'Event.orgc_id' => $user['org_id']));
$results = $this->find('all', $params);
if (Configure::read('MISP.proposals_block_attributes')) {
foreach ($results as $key => $value) {
if (!empty($value['ShadowAttribute'])) {
unset($results[$key]);
} else {
unset($results[$key]['ShadowAttribute']);
}
}
}
$results = array_values($results);
if (isset($options['withAttachments']) && $options['withAttachments']) {
foreach ($results as &$attribute) {
if ($this->typeIsAttachment($attribute['Attribute']['type'])) {

View File

@ -1325,19 +1325,19 @@ class Event extends AppModel {
$results[$eventKey]['RelatedAttribute'] = $this->getRelatedAttributes($user, $event['Event']['id'], $sgsids);
$results[$eventKey]['RelatedShadowAttribute'] = $this->getRelatedAttributes($user, $event['Event']['id'], $sgsids, true);
if (isset($event['ShadowAttribute']) && !empty($event['ShadowAttribute']) && isset($options['includeAttachments']) && $options['includeAttachments']) {
foreach ($event['ShadowAttribute'] as &$sa) {
foreach ($event['ShadowAttribute'] as $k => $sa) {
if ($this->ShadowAttribute->typeIsAttachment($sa['type'])) {
$encodedFile = $this->ShadowAttribute->base64EncodeAttachment($sa);
$sa['data'] = $encodedFile;
$event['ShadowAttribute'][$k]['data'] = $encodedFile;
}
}
}
if (isset($event['Attribute'])) {
foreach ($event['Attribute'] as $key => &$attribute) {
foreach ($event['Attribute'] as $key => $attribute) {
if (isset($options['includeAttachments']) && $options['includeAttachments']) {
if ($this->Attribute->typeIsAttachment($attribute['type'])) {
$encodedFile = $this->Attribute->base64EncodeAttachment($attribute);
$attribute['data'] = $encodedFile;
$event['Attribute'][$key]['data'] = $encodedFile;
}
}
if (isset($attribute['SharingGroup']['SharingGroupServer'])) {
@ -1347,18 +1347,28 @@ class Event extends AppModel {
}
}
}
$attribute['ShadowAttribute'] = array();
$event['Attribute'][$key]['ShadowAttribute'] = array();
// If a shadowattribute can be linked to an attribute, link it to it then remove it from the event
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
foreach ($event['ShadowAttribute'] as $k => &$sa) {
foreach ($event['ShadowAttribute'] as $k => $sa) {
if (!empty($sa['old_id'])) {
if ($sa['old_id'] == $attribute['id']) {
if ($event['ShadowAttribute'][$k]['old_id'] == $attribute['id']) {
$results[$eventKey]['Attribute'][$key]['ShadowAttribute'][] = $sa;
unset($results[$eventKey]['ShadowAttribute'][$k]);
}
}
}
if (Configure::read('MISP.proposals_block_attributes') && isset($options['to_ids']) && $options['to_ids']) {
foreach ($results[$eventKey]['Attribute'][$key]['ShadowAttribute'] as $sa) {
if ($sa['proposal_to_delete'] || $sa['to_ids'] == 0) {
unset($results[$eventKey]['Attribute'][$key]);
continue;
}
}
}
}
$event['Attribute'] = array_values($event['Attribute']);
}
if (Configure::read('Plugin.Sightings_enable')) {
$event['Sighting'] = $this->Sighting->attachToEvent($event, $user);
@ -1366,10 +1376,10 @@ class Event extends AppModel {
// remove proposals to attributes that we cannot see
// if the shadow attribute wasn't moved within an attribute before, this is the case
if (isset($event['ShadowAttribute'])) {
$event['ShadowAttribute'] = array_values($event['ShadowAttribute']);
foreach ($event['ShadowAttribute'] as $k => &$sa) {
foreach ($event['ShadowAttribute'] as $k => $sa) {
if (!empty($sa['old_id'])) unset($event['ShadowAttribute'][$k]);
}
$event['ShadowAttribute'] = array_values($event['ShadowAttribute']);
}
}
return $results;
@ -1380,11 +1390,11 @@ class Event extends AppModel {
$conditions = array();
// If we are not in the search result csv download function then we need to check what can be downloaded. CSV downloads are already filtered by the search function.
if ($eventid !== 'search') {
if ($from) $conditions['AND'][] = array('Event.date >=' => $from);
if ($to) $conditions['AND'][] = array('Event.date <=' => $to);
if ($last) $conditions['AND'][] = array('Event.publish_timestamp >=' => $last);
if ($from) $conditions['AND']['Event.date >='] = $from;
if ($to) $conditions['AND']['Event.date <='] = $to;
if ($last) $conditions['AND']['Event.publish_timestamp >='] = $last;
// This is for both single event downloads and for full downloads. Org has to be the same as the user's or distribution not org only - if the user is no siteadmin
if ($ignore == false) $conditions['AND'][] = array('Event.published' => 1);
if ($ignore == false) $conditions['AND']['Event.published'] = 1;
// If we sent any tags along, load the associated tag names for each attribute
if ($tags) {
@ -1406,9 +1416,9 @@ class Event extends AppModel {
if ($eventid) $conditions['AND'][] = array('Event.id' => $eventid);
//restricting to non-private or same org if the user is not a site-admin.
if (!$ignore) $conditions['AND'][] = array('Attribute.to_ids' => 1);
if ($type) $conditions['AND'][] = array('Attribute.type' => $type);
if ($category) $conditions['AND'][] = array('Attribute.category' => $category);
if (!$ignore) $conditions['AND']['Attribute.to_ids'] = 1;
if ($type) $conditions['AND']['Attribute.type'] = $type;
if ($category) $conditions['AND']['Attribute.category'] = $category;
}
if ($eventid === 'search') {

View File

@ -654,6 +654,15 @@ class Server extends AppModel {
'type' => 'string',
'null' => true,
),
'proposals_block_attributes' => array(
'level' => 0,
'description' => 'Enable this setting to allow blocking attributes from to_ids sensitive exports if a proposal has been made to it to remove the IDS flag or to remove the attribute altogether. This is a powerful tool to deal with false-positives efficiently.',
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => false,
)
),
'GnuPG' => array(
'branch' => 1,