Merge branch 'develop' into 2.4

pull/8901/head v2.4.168
iglocska 2023-02-01 14:37:06 +01:00
commit a7905b40ce
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
25 changed files with 176 additions and 138 deletions

2
PyMISP

@ -1 +1 @@
Subproject commit cca5287b2b11a1951789680c75e10636dde98add
Subproject commit 9a7adb2e0d60d2edee9f541db808652875bae20e

View File

@ -108,16 +108,16 @@ License
This software is licensed under [GNU Affero General Public License version 3](http://www.gnu.org/licenses/agpl-3.0.html)
* Copyright (C) 2012-2022 Christophe Vandeplas
* Copyright (C) 2012-2023 Christophe Vandeplas
* Copyright (C) 2012 Belgian Defence
* Copyright (C) 2012 NATO / NCIRC
* Copyright (C) 2013-2022 Andras Iklody
* Copyright (C) 2015-2022 CIRCL - Computer Incident Response Center Luxembourg
* Copyright (C) 2013-2023 Andras Iklody
* Copyright (C) 2015-2023 CIRCL - Computer Incident Response Center Luxembourg
* Copyright (C) 2016 Andreas Ziegler
* Copyright (C) 2018-2022 Sami Mokaddem
* Copyright (C) 2018-2022 Christian Studer
* Copyright (C) 2015-2022 Alexandre Dulaunoy
* Copyright (C) 2018-2023 Sami Mokaddem
* Copyright (C) 2018-2023 Christian Studer
* Copyright (C) 2015-2023 Alexandre Dulaunoy
* Copyright (C) 2018-2022 Steve Clement
* Copyright (C) 2020-2022 Jakub Onderka
* Copyright (C) 2020-2023 Jakub Onderka
For more information, [the list of authors and contributors](AUTHORS) is available.

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":167}
{"major":2, "minor":4, "hotfix":168}

View File

@ -34,7 +34,7 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '147';
public $pyMispVersion = '2.4.167';
public $pyMispVersion = '2.4.168';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';
@ -152,20 +152,25 @@ class AppController extends Controller
}
$this->User = ClassRegistry::init('User');
if ($this->Auth->user()) {
if ($this->User->checkForSessionDestruction($this->Auth->user('id'))) {
$this->Auth->logout();
$this->Session->destroy();
$message = __('User deauthenticated on administrator request. Please reauthenticate.');
if ($this->_isRest()) {
throw new ForbiddenException($message);
} else {
$this->Flash->warning($message);
$this->_redirectToLogin();
}
}
if (!empty($this->request->params['named']['disable_background_processing'])) {
Configure::write('MISP.background_jobs', 0);
}
Configure::write('CurrentController', $controller);
Configure::write('CurrentAction', $action);
$versionArray = $this->User->checkMISPVersion();
$this->mispVersion = implode('.', $versionArray);
$this->Security->blackHoleCallback = 'blackHole';
// send users away that are using ancient versions of IE
// Make sure to update this if IE 20 comes out :)
if (isset($_SERVER['HTTP_USER_AGENT'])) {
if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT']) && !strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
throw new MethodNotAllowedException('You are using an unsecure and outdated version of IE, please download Google Chrome, Mozilla Firefox or update to a newer version of IE. If you are running IE9 or newer and still receive this error message, please make sure that you are not running your browser in compatibility mode. If you still have issues accessing the site, get in touch with your administration team at ' . Configure::read('MISP.contact'));
}
}
// For fresh installation (salt empty) generate a new salt
if (!Configure::read('Security.salt')) {
$this->User->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
@ -176,6 +181,10 @@ class AppController extends Controller
$this->User->Server->serverSettingsSaveValue('MISP.uuid', CakeText::uuid());
}
/**
* Authentication related activities
*/
// Check if Apache provides kerberos authentication data
$authUserFields = $this->User->describeAuthFields();
$envvar = Configure::read('ApacheSecureAuth.apacheEnv');
@ -191,22 +200,7 @@ class AppController extends Controller
} else {
$this->Auth->authenticate[AuthComponent::ALL]['userFields'] = $authUserFields;
}
if (!empty($this->request->params['named']['disable_background_processing'])) {
Configure::write('MISP.background_jobs', 0);
}
Configure::write('CurrentController', $controller);
Configure::write('CurrentAction', $action);
$versionArray = $this->User->checkMISPVersion();
$this->mispVersion = implode('.', $versionArray);
$this->Security->blackHoleCallback = 'blackHole';
// send users away that are using ancient versions of IE
// Make sure to update this if IE 20 comes out :)
if (isset($_SERVER['HTTP_USER_AGENT'])) {
if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT']) && !strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) {
throw new MethodNotAllowedException('You are using an unsecure and outdated version of IE, please download Google Chrome, Mozilla Firefox or update to a newer version of IE. If you are running IE9 or newer and still receive this error message, please make sure that you are not running your browser in compatibility mode. If you still have issues accessing the site, get in touch with your administration team at ' . Configure::read('MISP.contact'));
}
}
$userLoggedIn = false;
if (Configure::read('Plugin.CustomAuth_enable')) {
$userLoggedIn = $this->__customAuthentication($_SERVER);
@ -528,12 +522,24 @@ class AppController extends Controller
}
$this->Flash->info($message);
$this->Auth->logout();
throw new MethodNotAllowedException($message);//todo this should pb be removed?
$this->_redirectToLogin();
return false;
} else {
$this->Flash->error(__('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live). An update might also be in progress, you can see the progress in ') , array('params' => array('url' => $this->baseurl . '/servers/updateProgress/', 'urlName' => __('Update Progress')), 'clear' => 1));
}
}
// kill existing sessions for a user if the admin/instance decides so
// exclude API authentication as it doesn't make sense
if (!$this->isApiAuthed && $this->User->checkForSessionDestruction($user['id'])) {
$this->Auth->logout();
$this->Session->destroy();
$message = __('User deauthenticated on administrator request. Please reauthenticate.');
$this->Flash->warning($message);
$this->_redirectToLogin();
return false;
}
// Force logout doesn't make sense for API key authentication
if (!$this->isApiAuthed && $user['force_logout']) {
$this->User->id = $user['id'];

View File

@ -192,10 +192,13 @@ class AuditLogsController extends AppController
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
}
$this->set('list', $list);
$this->set('data', $list);
$this->set('event', $event);
$this->set('mayModify', $this->__canModifyEvent($event));
$this->set('title_for_layout', __('Audit logs for event #%s', $event['Event']['id']));
$this->set('menuData', [
'menuList' => 'event',
'menuItem' => 'eventLog'
]);
}
public function fullChange($id)

View File

@ -125,7 +125,7 @@ class ACLComponent extends Component
'decayingModel' => array(
"update" => array(),
"export" => array('*'),
"import" => array('*'),
"import" => array('OR' => array('perm_admin', 'perm_decaying')),
"view" => array('*'),
"index" => array('*'),
"add" => array( 'OR' => array('perm_admin', 'perm_decaying')),

View File

@ -114,7 +114,7 @@ class CRUDComponent extends Component
$this->Controller->Flash->success($message);
if (!empty($params['displayOnSuccess'])) {
$this->Controller->set('entity', $data);
$this->Controller->set('referer', $this->Controller->referer());
$this->Controller->set('referer', $this->Controller->referer(['action' => 'view', $model->id], true));
$this->Controller->render($params['displayOnSuccess']);
return;
}

View File

@ -1726,6 +1726,7 @@ class ServersController extends AppController
if (!$server) {
throw new NotFoundException(__('Invalid server'));
}
@session_write_close(); // close session to allow concurrent requests
$result = $this->Server->runConnectionTest($server);
if ($result['status'] == 1) {
if (isset($result['info']['version']) && preg_match('/^[0-9]+\.+[0-9]+\.[0-9]+$/', $result['info']['version'])) {

View File

@ -62,8 +62,10 @@ class ShadowAttributesController extends AppController
// If the old_id is set to anything but 0 then we're dealing with a proposed edit to an existing attribute
if ($shadow['old_id'] != 0) {
// Find the live attribute by the shadow attribute's uuid, so we can begin editing it
$this->Attribute->contain = 'Event';
$activeAttribute = $this->Attribute->findByUuid($shadow['uuid']);
$activeAttribute = $this->Attribute->find('first', [
'conditions' => ['Attribute.uuid' => $shadow['uuid']],
'contain' => ['Event'],
]);
// Send those away that shouldn't be able to edit this
if (!$this->__canModifyEvent($activeAttribute)) {

View File

@ -1230,6 +1230,11 @@ class UsersController extends AppController
$this->Bruteforce->insert($this->request->data['User']['email']);
}
}
//
// Actions needed for the first access, when the database is not populated yet.
//
// populate the DB with the first role (site admin) if it's empty
if (!$this->User->Role->hasAny()) {
$siteAdmin = array('Role' => array(
@ -1279,7 +1284,6 @@ class UsersController extends AppController
}
$org_id = $this->User->Organisation->id;
}
// populate the DB with the first user if it's empty
if (!$this->User->hasAny()) {
if (!isset($org_id)) {
@ -1291,7 +1295,6 @@ class UsersController extends AppController
$org_id = $firstOrg['Organisation']['id'];
}
}
$this->User->runUpdates();
$this->User->createInitialUser($org_id);
}
@ -1300,25 +1303,25 @@ class UsersController extends AppController
private function _postlogin()
{
$this->User->extralog($this->Auth->user(), "login");
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
$this->User->id = $this->Auth->user('id');
$user = $this->User->find('first', array(
'conditions' => array(
'User.id' => $this->Auth->user('id')
),
'recursive' => -1
));
unset($user['User']['password']);
$this->User->updateLoginTimes($user['User']);
$lastUserLogin = $user['User']['last_login'];
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
if ($lastUserLogin) {
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
$this->Flash->info(__('Welcome! Last login was on %s', $readableDatetime));
}
// no state changes are ever done via GET requests, so it is safe to return to the original page:
$this->redirect($this->Auth->redirectUrl());
$this->User->extralog($this->Auth->user(), "login");
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
$this->User->id = $this->Auth->user('id');
$user = $this->User->find('first', array(
'conditions' => array(
'User.id' => $this->Auth->user('id')
),
'recursive' => -1
));
unset($user['User']['password']);
$this->User->updateLoginTimes($user['User']);
$lastUserLogin = $user['User']['last_login'];
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
if ($lastUserLogin) {
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
$this->Flash->info(__('Welcome! Last login was on %s', $readableDatetime));
}
// no state changes are ever done via GET requests, so it is safe to return to the original page:
$this->redirect($this->Auth->redirectUrl());
}
public function routeafterlogin()

View File

@ -1,48 +1,59 @@
<div class="logs index">
<h2><?= __('Audit logs for event #%s', intval($event['Event']['id'])) ?></h2>
<div class="pagination">
<ul>
<?php
$paginator = $this->LightPaginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
$paginator .= $this->LightPaginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $paginator;
?>
<li><a href="<?= $baseurl . '/logs/event_index/' . intval($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?= $this->LightPaginator->sort('created') ?></th>
<th><?= $this->LightPaginator->sort('user_id', __('User')) ?></th>
<th><?= $this->LightPaginator->sort('org_id', __('Org')) ?></th>
<th><?= $this->LightPaginator->sort('action') ?></th>
<th><?= __('Model') ?></th>
<th><?= __('Title') ?></th>
<th><?= __('Change') ?></th>
</tr>
<?php foreach ($list as $item): ?>
<tr>
<td class="short"><?= $this->Time->time($item['AuditLog']['created']); ?></td>
<td class="short"><?php
if (isset($item['AuditLog']['user_id']) && $item['AuditLog']['user_id'] == 0) {
echo __('SYSTEM');
} else if (isset($item['User']['email'])) {
echo h($item['User']['email']);
} ?></td>
<td class="short"><?= isset($item['Organisation']) ? $this->OrgImg->getOrgLogo($item, 24) : '' ?></td>
<td class="short"><?= h($item['AuditLog']['action_human']) ?></td>
<td class="short"><?= h($item['AuditLog']['model']) . ' #' . intval($item['AuditLog']['model_id']) ?></td>
<td class="limitedWidth"><?= h($item['AuditLog']['title']) ?></td>
<td><?= $this->element('AuditLog/change', ['item' => $item]) ?></td>
</tr>
<?php endforeach; ?>
</table>
<div class="pagination">
<ul>
<?= $paginator ?>
<li><a href="<?= $baseurl . '/logs/event_index/' . intval($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
</ul>
</div>
</div>
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'event', 'menuItem' => 'eventLog']);
<?php
echo sprintf('<div%s>', empty($ajax) ? ' class="index"' : '');
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'light_paginator' => 1,
'data' => $data,
'fields' => [
[
'name' => __('Created'),
'data_path' => 'AuditLog.created',
'sort' => 'AuditLog.created',
'class' => 'short',
'element' => 'time'
],
[
'name' => __('User'),
'data_path' => 'User.email',
'sort' => 'User.email',
'class' => 'short',
'empty' => 'SYSTEM'
],
[
'name' => __('Organisation'),
'data_path' => 'Organisation',
'sort' => 'Organisation.name',
'element' => 'org',
'class' => 'short'
],
[
'name' => __('Action'),
'data_path' => 'AuditLog.action_human',
'sort' => 'AuditLog.action_human',
'class' => 'short'
],
[
'name' => __('Model'),
'data_path' => 'AuditLog',
'element' => 'model',
'class' => 'short'
],
[
'name' => __('Title'),
'data_path' => 'AuditLog.title',
'class' => 'limitedWidth'
],
[
'name' => __('Change'),
'data_path' => 'AuditLog',
'element' => 'custom_element',
'element_path' => 'AuditLog/change'
]
],
'title' => __('Audit logs for event #%s', intval($event['Event']['id']))
]
]);
echo '</div>';
if (empty($ajax)) {
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
}

View File

@ -12,7 +12,7 @@
<pre class="quickSelect"><?= h($entity['AuthKey']['authkey_raw']) ?></pre>
</div>
<div class="modal-footer">
<a href="<?= $referer ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
<a href="<?= h($referer) ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
</div>
</div>
<?php
@ -22,7 +22,7 @@
<p><?= __('Please make sure that you note down the auth key below, this is the only time the auth key is shown in plain text, so make sure you save it. If you lose the key, simply remove the entry and generate a new one.'); ?></p>
<p><?=__('MISP will use the first and the last 4 characters for identification purposes.')?></p>
<pre class="quickSelect"><?= h($entity['AuthKey']['authkey_raw']) ?></pre>
<a href="<?= $referer ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
<a href="<?= h($referer) ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
<?php
}
?>

View File

@ -0,0 +1 @@
<?= $this->element($field['element_path'], ['item' => $row]) ?>

View File

@ -1,5 +1,8 @@
<?php
$data = Hash::extract($row, $field['data_path']);
if (!empty($field['empty']) && empty($data)) {
$data = $field['empty'];
}
if (is_array($data)) {
if (count($data) > 1) {
$implodeGlue = isset($field['array_implode_glue']) ? $field['array_implode_glue'] : ', ';

View File

@ -0,0 +1,3 @@
<?php
$data = Hash::extract($row, $field['data_path']);
echo h($data['model']) . ' #' . intval($data['model_id']);

View File

@ -0,0 +1,3 @@
<?php
$time = Hash::extract($row, $field['data_path'])[0];
echo $this->Time->time($time);

View File

@ -73,7 +73,7 @@ echo $this->element('genericElements/Form/genericForm', [
'field' => 'orgc_id',
'label' => __('Creator organisation'),
'options' => $dropdownData['orgs'],
'value' => $this->request->params['action'] === 'add' ? $me['org_id'] : '',
'value' => $this->request->params['action'] === 'add' ? $me['org_id'] : null,
'type' => 'dropdown',
'div' => ['id' => 'OrgcDiv', 'style' => 'display:none', 'class' => 'optionalField'],
'class' => 'form-control span6'

View File

@ -1,22 +1,24 @@
<div class="logs index">
<h2><?php echo __('Logs');?></h2>
<p><?= __('Showing log entries for data currently stored in the database. Entries about hard-deleted data have been omitted.') ?></p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
$this->LightPaginator->options(array('url' => $this->passedArgs));
echo $this->LightPaginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->LightPaginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->LightPaginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('org');?></th>
<th><?php echo $this->Paginator->sort('email');?></th>
<th><?php echo $this->Paginator->sort('action');?></th>
<th><?php echo $this->Paginator->sort('model');?></th>
<th><?php echo $this->Paginator->sort('title');?></th>
<th><?php echo $this->Paginator->sort('created');?></th>
<th><?php echo $this->LightPaginator->sort('org');?></th>
<th><?php echo $this->LightPaginator->sort('email');?></th>
<th><?php echo $this->LightPaginator->sort('action');?></th>
<th><?php echo $this->LightPaginator->sort('model');?></th>
<th><?php echo $this->LightPaginator->sort('title');?></th>
<th><?php echo $this->LightPaginator->sort('created');?></th>
</tr>
<?php foreach ($list as $item): ?>
<tr>
@ -39,7 +41,7 @@
</table>
<p>
<?php
echo $this->Paginator->counter(array(
echo $this->LightPaginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
@ -47,9 +49,9 @@
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->LightPaginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->LightPaginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->LightPaginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>

@ -1 +1 @@
Subproject commit ac1242a40e3a165ea664f3a8640dc346a5877c3a
Subproject commit f605f041d99057b767231831d7f5970be6c1fcb9

@ -1 +1 @@
Subproject commit 4e19aa30ba94940e38bf50164248a572139ca520
Subproject commit fd603be3283953b68ed48ede7afd2e19f43577ac

@ -1 +1 @@
Subproject commit c60a6e76ccd735d460b382effee9af6aae7fa4ab
Subproject commit 93da9ae3a468f1beacc7edbfe241e6df89b94250

@ -1 +1 @@
Subproject commit e4d0c58076c2c1af20491b1d9be56967a16ac515
Subproject commit 14f1349fad189091960311018a4cabcb1018adb9

@ -1 +1 @@
Subproject commit ab2938e008eb5ec085d9e9c8e7c5c4d6fd466a39
Subproject commit a51a9adc6c693bd5d2bf83f49920df7247ced298

View File

@ -162,7 +162,7 @@ class ActionTable {
tr.id = "tr_" + this.__uuidv4();
for (var col of row) {
var td = document.createElement('td');
td.innerHTML = col;
td.textContent = col;
tr.appendChild(td);
}
this.__add_action_button(tr);

View File

@ -636,7 +636,7 @@ class EventGraph {
btn_plot.data('network-preview', preview);
btn_plot.popover({
container: 'body',
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $(this).data('network-preview') + '" />'; },
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $('<div>').text($(this).data('network-preview')).html() + '" />'; },
placement: 'right',
trigger: 'hover',
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content" style="width: 500px; height: 150px;"></div></div>',
@ -2002,7 +2002,7 @@ function reset_graph_history() {
btn_plot.data('network-preview', preview);
btn_plot.popover({
container: 'body',
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $(this).data('network-preview') + '" />'; },
content: function() { return '<img style="width: 500px; height: 150px;" src="' + $('<div>').text($(this).data('network-preview')).html() + '" />'; },
placement: 'right',
trigger: 'hover',
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content" style="width: 500px; height: 150px;"></div></div>',