mirror of https://github.com/MISP/MISP
commit
c08ea70562
|
@ -187,8 +187,9 @@ class EventShell extends AppShell
|
|||
$this->Job->id = $id;
|
||||
$export_type = $this->args[2];
|
||||
file_put_contents('/tmp/test', $export_type);
|
||||
$typeData = $this->Event->export_types[$export_type];
|
||||
if (!in_array($export_type, array_keys($this->Event->export_types))) {
|
||||
$exportTypes = $this->Event->exportTypes();
|
||||
$typeData = $exportTypes[$export_type];
|
||||
if (!in_array($export_type, array_keys($exportTypes))) {
|
||||
$this->Job->saveField('progress', 100);
|
||||
$timeDelta = (time()-$timeStart);
|
||||
$this->Job->saveField('message', 'Job Failed due to invalid export format. (in '.$timeDelta.'s)');
|
||||
|
@ -385,7 +386,7 @@ class EventShell extends AppShell
|
|||
// the special cache files containing all events
|
||||
$i = 0;
|
||||
foreach ($users as $user) {
|
||||
foreach ($this->Event->export_types as $k => $type) {
|
||||
foreach ($this->Event->exportTypes() as $k => $type) {
|
||||
if ($k == 'stix') continue;
|
||||
$this->Job->cache($k, $user['User']);
|
||||
$i++;
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class ApiController extends AppController
|
||||
{
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
$this->Security->unlockedActions[] = 'getApiInfo';
|
||||
}
|
||||
|
||||
public function openapi()
|
||||
{
|
||||
$this->set('title_for_layout', __('OpenAPI'));
|
||||
}
|
||||
|
||||
public function viewDeprecatedFunctionUse()
|
||||
{
|
||||
$server = ClassRegistry::init('Server');
|
||||
$data = $this->Deprecation->getDeprecatedAccessList($server);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllApis()
|
||||
{
|
||||
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
|
||||
$allValidApisFieldsConstraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
|
||||
$output = [
|
||||
'allValidApis' => $allValidApis,
|
||||
'fieldsConstraint' => $allValidApisFieldsConstraint,
|
||||
];
|
||||
return $this->RestResponse->viewData($output, 'json');
|
||||
}
|
||||
|
||||
public function getApiInfo()
|
||||
{
|
||||
$relative_path = $this->request->data['url'];
|
||||
$result = $this->RestResponse->getApiInfo($relative_path);
|
||||
if ($this->_isRest()) {
|
||||
if (!empty($result)) {
|
||||
$result['api_info'] = $result;
|
||||
}
|
||||
return $this->RestResponse->viewData($result, $this->response->type());
|
||||
} else {
|
||||
if (empty($result)) {
|
||||
return $this->RestResponse->viewData(' ', $this->response->type());
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->autoRender = false;
|
||||
$this->set('api_info', $result);
|
||||
$this->render('ajax/get_api_info');
|
||||
}
|
||||
}
|
||||
|
||||
public function rest()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$request = $this->request->data;
|
||||
if (!empty($request['Server'])) {
|
||||
$request = $this->request->data['Server'];
|
||||
}
|
||||
$curl = '';
|
||||
$python = '';
|
||||
try {
|
||||
$result = $this->__doRestQuery($request, $curl, $python);
|
||||
$this->set('curl', $curl);
|
||||
$this->set('python', $python);
|
||||
if (!$result) {
|
||||
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
|
||||
} else {
|
||||
$this->set('data', $result);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
$header = sprintf(
|
||||
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
|
||||
__('YOUR_API_KEY')
|
||||
);
|
||||
$this->set('header', $header);
|
||||
|
||||
$allAccessibleApis = $this->RestResponse->getAccessibleApis($this->Auth->user());
|
||||
$this->set('allAccessibleApis', $allAccessibleApis);
|
||||
$this->set('title_for_layout', __('REST client'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @param string $curl
|
||||
* @param string $python
|
||||
* @return array|false
|
||||
*/
|
||||
private function __doRestQuery(array $request, &$curl = false, &$python = false)
|
||||
{
|
||||
$params = array();
|
||||
|
||||
$logHeaders = $request['header'];
|
||||
if (!empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$logHeaders = explode("\n", $request['header']);
|
||||
foreach ($logHeaders as $k => $header) {
|
||||
if (strpos($header, 'Authorization') !== false) {
|
||||
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
|
||||
}
|
||||
}
|
||||
$logHeaders = implode("\n", $logHeaders);
|
||||
}
|
||||
|
||||
if (empty($request['body'])) {
|
||||
$historyBody = '';
|
||||
} else if (strlen($request['body']) > 65535) {
|
||||
$historyBody = ''; // body is too long to save into history table
|
||||
} else {
|
||||
$historyBody = $request['body'];
|
||||
}
|
||||
|
||||
$rest_history_item = array(
|
||||
'org_id' => $this->Auth->user('org_id'),
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'headers' => $logHeaders,
|
||||
'body' => $historyBody,
|
||||
'url' => $request['url'],
|
||||
'http_method' => $request['method'],
|
||||
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
|
||||
'show_result' => $request['show_result'],
|
||||
'skip_ssl' => $request['skip_ssl_validation'],
|
||||
'bookmark' => $request['bookmark'],
|
||||
'bookmark_name' => $request['name'],
|
||||
'timestamp' => time(),
|
||||
);
|
||||
if (!empty($request['url'])) {
|
||||
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
|
||||
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
|
||||
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
|
||||
unset($request['url']);
|
||||
} else {
|
||||
$url = $request['url'];
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException('URL not set.');
|
||||
}
|
||||
if (!empty($request['skip_ssl_validation'])) {
|
||||
$params['ssl_verify_peer'] = false;
|
||||
$params['ssl_verify_host'] = false;
|
||||
$params['ssl_verify_peer_name'] = false;
|
||||
$params['ssl_allow_self_signed'] = true;
|
||||
}
|
||||
$params['timeout'] = 300;
|
||||
App::uses('HttpSocketExtended', 'Tools');
|
||||
$HttpSocket = new HttpSocketExtended($params);
|
||||
|
||||
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
|
||||
$request['header'] = array(
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'MISP REST Client',
|
||||
);
|
||||
foreach ($temp_headers as $header) {
|
||||
$header = explode(':', $header);
|
||||
$header[0] = trim($header[0]);
|
||||
$header[1] = trim($header[1]);
|
||||
$request['header'][$header[0]] = $header[1];
|
||||
}
|
||||
$start = microtime(true);
|
||||
if (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'GET'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('get', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'POST' &&
|
||||
!empty($request['body'])
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('post', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'DELETE'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('delete', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$viewData = [
|
||||
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
|
||||
'url' => $url,
|
||||
'code' => $response->code,
|
||||
'headers' => $response->headers,
|
||||
];
|
||||
|
||||
if (!empty($request['show_result'])) {
|
||||
$viewData['data'] = $response->body;
|
||||
} else {
|
||||
if ($response->isOk()) {
|
||||
$viewData['data'] = 'Success.';
|
||||
} else {
|
||||
$viewData['data'] = 'Something went wrong.';
|
||||
}
|
||||
}
|
||||
$rest_history_item['outcome'] = $response->code;
|
||||
|
||||
$this->loadModel('RestClientHistory');
|
||||
$this->RestClientHistory->create();
|
||||
$this->RestClientHistory->save($rest_history_item);
|
||||
$this->RestClientHistory->cleanup($this->Auth->user('id'));
|
||||
|
||||
return $viewData;
|
||||
}
|
||||
|
||||
private function __generatePythonScript(array $request, $url)
|
||||
{
|
||||
$slashCounter = 0;
|
||||
$baseurl = '';
|
||||
$relative = '';
|
||||
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
|
||||
for ($i = 0; $i < strlen($url); $i++) {
|
||||
//foreach ($url as $url[$i]) {
|
||||
if ($url[$i] === '/') {
|
||||
$slashCounter += 1;
|
||||
if ($slashCounter == 3) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($slashCounter < 3) {
|
||||
$baseurl .= $url[$i];
|
||||
} else {
|
||||
$relative .= $url[$i];
|
||||
}
|
||||
}
|
||||
$python_script =
|
||||
sprintf(
|
||||
'misp_url = \'%s\'
|
||||
misp_key = \'%s\'
|
||||
misp_verifycert = %s
|
||||
relative_path = \'%s\'
|
||||
body = %s
|
||||
|
||||
from pymisp import ExpandedPyMISP
|
||||
|
||||
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
|
||||
misp.direct_call(relative_path, body)
|
||||
',
|
||||
$baseurl,
|
||||
$request['header']['Authorization'],
|
||||
$verifyCert,
|
||||
$relative,
|
||||
(empty($request['body']) ? 'None' : $request['body'])
|
||||
);
|
||||
return $python_script;
|
||||
}
|
||||
|
||||
private function __generateCurlQuery($type, array $request, $url)
|
||||
{
|
||||
if ($type === 'get') {
|
||||
$curl = sprintf(
|
||||
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
} else {
|
||||
$curl = sprintf(
|
||||
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
|
||||
PHP_EOL,
|
||||
json_encode(json_decode($request['body'])),
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
}
|
||||
return $curl;
|
||||
}
|
||||
}
|
|
@ -202,20 +202,7 @@ class AppController extends Controller
|
|||
if (empty($dataToDecode)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$decoded = json_decode($dataToDecode, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
|
||||
}
|
||||
return $this->_jsonDecode($dataToDecode);
|
||||
};
|
||||
// Throw exception if JSON in request is invalid. Default CakePHP behaviour would just ignore that error.
|
||||
$this->RequestHandler->addInputType('json', [$jsonDecode]);
|
||||
|
@ -1450,4 +1437,27 @@ class AppController extends Controller
|
|||
}
|
||||
return parent::_getViewObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode JSON with proper error handling.
|
||||
* @param string $dataToDecode
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _jsonDecode($dataToDecode)
|
||||
{
|
||||
try {
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$decoded = json_decode($dataToDecode, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ class ACLComponent extends Component
|
|||
'queryACL' => array(),
|
||||
'restSearch' => array('*'),
|
||||
),
|
||||
'api' => [
|
||||
'rest' => ['perm_auth'],
|
||||
'viewDeprecatedFunctionUse' => [],
|
||||
'openapi' => ['*'],
|
||||
'getApiInfo' => ['*'],
|
||||
'getAllApis' => ['*'],
|
||||
],
|
||||
'attributes' => array(
|
||||
'add' => array('perm_add'),
|
||||
'add_attachment' => array('perm_add'),
|
||||
|
@ -505,7 +512,6 @@ class ACLComponent extends Component
|
|||
'eventBlockRule' => array(),
|
||||
'fetchServersForSG' => array('perm_sharing_group'),
|
||||
'filterEventIndex' => array(),
|
||||
'getApiInfo' => array('*'),
|
||||
'getAvailableSyncFilteringRules' => array('*'),
|
||||
'getInstanceUUID' => array('perm_sync'),
|
||||
'getPyMISPVersion' => array('*'),
|
||||
|
@ -531,8 +537,6 @@ class ACLComponent extends Component
|
|||
'releaseUpdateLock' => array(),
|
||||
'resetRemoteAuthKey' => array(),
|
||||
'removeOrphanedCorrelations' => array(),
|
||||
'rest' => array('perm_auth'),
|
||||
'openapi' => array('*'),
|
||||
'restartDeadWorkers' => array(),
|
||||
'restartWorkers' => array(),
|
||||
'serverSettings' => array(),
|
||||
|
@ -549,7 +553,6 @@ class ACLComponent extends Component
|
|||
'updateProgress' => array(),
|
||||
'updateSubmodule' => array(),
|
||||
'uploadFile' => array(),
|
||||
'viewDeprecatedFunctionUse' => array(),
|
||||
'killAllWorkers' => [],
|
||||
'cspReport' => ['*'],
|
||||
'pruneDuplicateUUIDs' => array(),
|
||||
|
@ -557,6 +560,7 @@ class ACLComponent extends Component
|
|||
'upgrade2324' => array(),
|
||||
'cleanModelCaches' => array(),
|
||||
'updateDatabase' => array(),
|
||||
'rest' => ['perm_auth'],
|
||||
),
|
||||
'shadowAttributes' => array(
|
||||
'accept' => array('perm_add'),
|
||||
|
|
|
@ -367,12 +367,7 @@ class RestResponseComponent extends Component
|
|||
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
|
||||
foreach ($actions as $action => $data) {
|
||||
if ($this->ACL->canUserAccess($user, $controller, $action)) {
|
||||
$admin_routing = '';
|
||||
if (substr($action, 0, 6) === 'admin_') {
|
||||
$action = substr($action, 6);
|
||||
$admin_routing = 'admin/';
|
||||
}
|
||||
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
|
||||
$url = $this->generateUrl($controller, $action);
|
||||
$result[$url] = $data;
|
||||
}
|
||||
}
|
||||
|
@ -380,6 +375,25 @@ class RestResponseComponent extends Component
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @return array
|
||||
*/
|
||||
public function getAccessibleApis(array $user)
|
||||
{
|
||||
$output = [];
|
||||
foreach ($this->__descriptions as $controller => $actions) {
|
||||
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
|
||||
foreach ($actions as $action => $data) {
|
||||
if ($this->ACL->canUserAccess($user, $controller, $action)) {
|
||||
$url = $this->generateUrl($controller, $action);
|
||||
$output[$controller][$action] = $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function getAllApis($user)
|
||||
{
|
||||
$this->__setup();
|
||||
|
@ -389,15 +403,10 @@ class RestResponseComponent extends Component
|
|||
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
|
||||
foreach ($actions as $action => $data) {
|
||||
if ($this->ACL->canUserAccess($user, $controller, $action)) {
|
||||
$admin_routing = '';
|
||||
if (substr($action, 0, 6) === 'admin_') {
|
||||
$action = substr($action, 6);
|
||||
$admin_routing = 'admin/';
|
||||
}
|
||||
$data['api_name'] = '[' . $controller . '] ' . $action;
|
||||
$data['controller'] = $controller;
|
||||
$data['action'] = $action;
|
||||
$data['body'] = array();
|
||||
$body = [];
|
||||
$filter_types = array('mandatory', 'optional');
|
||||
foreach ($filter_types as $filter_type) {
|
||||
if (!empty($data[$filter_type])) {
|
||||
|
@ -407,16 +416,16 @@ class RestResponseComponent extends Component
|
|||
}
|
||||
foreach ($filter_items as $filter) {
|
||||
if ($filter === lcfirst($filter)) {
|
||||
$data['body'][$filter] = $filter_type;
|
||||
$body[$filter] = $filter_type;
|
||||
} else {
|
||||
$data['body'][$filter] = array($filter_type);
|
||||
$body[$filter] = array($filter_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['body'] = json_encode($data['body'], JSON_PRETTY_PRINT);
|
||||
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
|
||||
$data['body'] = $body;
|
||||
$url = $this->generateUrl($controller, $action);;
|
||||
$data['url'] = $url;
|
||||
if (!empty($data['params'])) {
|
||||
foreach ($data['params'] as $param) {
|
||||
|
@ -731,13 +740,8 @@ class RestResponseComponent extends Component
|
|||
$scopes = array('Event', 'Attribute', 'Sighting');
|
||||
foreach ($scopes as $scope) {
|
||||
$this->{$scope} = ClassRegistry::init($scope);
|
||||
$this->__descriptions[$scope]['restSearch'] = array(
|
||||
'description' => $this->__descriptions[$scope]['restSearch']['description'],
|
||||
'returnFormat' => array_keys($this->{$scope}->validFormats),
|
||||
'mandatory' => $this->__descriptions[$scope]['restSearch']['mandatory'],
|
||||
'optional' => $this->__descriptions[$scope]['restSearch']['optional'],
|
||||
'params' => $this->__descriptions[$scope]['restSearch']['params']
|
||||
);
|
||||
$returnFormat = array_keys($this->{$scope}->validFormats);
|
||||
$this->__descriptions[$scope]['restSearch']['returnFormat'] = $returnFormat;
|
||||
}
|
||||
$this->__configureFieldConstraints();
|
||||
$this->__setupFieldsConstraint();
|
||||
|
@ -1938,7 +1942,8 @@ class RestResponseComponent extends Component
|
|||
if ($values === null) {
|
||||
$tagModel = ClassRegistry::init("Tag");
|
||||
$tags = $tagModel->find('column', array(
|
||||
'fields' => array('Tag.name')
|
||||
'fields' => array('Tag.name'),
|
||||
'callbacks' => false,
|
||||
));
|
||||
$values = [];
|
||||
foreach ($tags as $tag) {
|
||||
|
@ -1963,19 +1968,40 @@ class RestResponseComponent extends Component
|
|||
private function __overwriteAction($scope, $action, &$field) {
|
||||
$field['values'] = array_keys(ClassRegistry::init("Log")->actionDefinitions);
|
||||
}
|
||||
private function __overwriteRoleId($scope, $action, &$field) {
|
||||
$this->{$scope} = ClassRegistry::init("Role");
|
||||
$roles = $this->{$scope}->find('list', array(
|
||||
'fields' => array('id', 'name')
|
||||
));
|
||||
$field['values'] = [];
|
||||
foreach ($roles as $id => $name) {
|
||||
$field['values'][] = ['label' => $name, 'value' => $id];
|
||||
|
||||
private function __overwriteRoleId($scope, $action, &$field)
|
||||
{
|
||||
static $values;
|
||||
if ($values === null) {
|
||||
$roleModel = ClassRegistry::init("Role");
|
||||
$roles = $roleModel->find('list', array(
|
||||
'fields' => array('id', 'name')
|
||||
));
|
||||
$values = [];
|
||||
foreach ($roles as $id => $name) {
|
||||
$values[] = ['label' => $name, 'value' => $id];
|
||||
}
|
||||
}
|
||||
$field['values'] = $values;
|
||||
}
|
||||
private function __overwriteSeen($scope, $action, &$field) {
|
||||
if ($action == 'restSearch') {
|
||||
$field['help'] = __('Seen within the last x amount of time, where x can be defined in days, hours, minutes (for example 5d or 12h or 30m)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @return string
|
||||
*/
|
||||
private function generateUrl($controller, $action)
|
||||
{
|
||||
$admin_routing = '';
|
||||
if (substr($action, 0, 6) === 'admin_') {
|
||||
$action = substr($action, 6);
|
||||
$admin_routing = 'admin/';
|
||||
}
|
||||
return '/' . $admin_routing . $controller . '/' . $action;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3213,7 +3213,8 @@ class EventsController extends AppController
|
|||
'order' => 'Event.timestamp DESC',
|
||||
));
|
||||
$this->loadModel('Job');
|
||||
foreach ($this->Event->export_types as $k => $type) {
|
||||
$exportTypes = $this->Event->exportTypes();
|
||||
foreach ($exportTypes as $k => $type) {
|
||||
if ($type['requiresPublished']) {
|
||||
$tempNewestEvent = $newestEventPublished;
|
||||
} else {
|
||||
|
@ -3237,10 +3238,10 @@ class EventsController extends AppController
|
|||
if (!$file->readable()) {
|
||||
if (empty($tempNewestEvent)) {
|
||||
$lastModified = 'No valid events';
|
||||
$this->Event->export_types[$k]['recommendation'] = 0;
|
||||
$exportTypes[$k]['recommendation'] = 0;
|
||||
} else {
|
||||
$lastModified = 'N/A';
|
||||
$this->Event->export_types[$k]['recommendation'] = 1;
|
||||
$exportTypes[$k]['recommendation'] = 1;
|
||||
}
|
||||
} else {
|
||||
$filesize = $file->size();
|
||||
|
@ -3249,32 +3250,31 @@ class EventsController extends AppController
|
|||
$filesize_unit_index++;
|
||||
$filesize = $filesize / 1024;
|
||||
}
|
||||
$this->Event->export_types[$k]['filesize'] = round($filesize, 1) . $filesize_units[$filesize_unit_index];
|
||||
$exportTypes[$k]['filesize'] = round($filesize, 1) . $filesize_units[$filesize_unit_index];
|
||||
$fileChange = $file->lastChange();
|
||||
$lastModified = $this->__timeDifference($now, $fileChange);
|
||||
if (empty($tempNewestEvent) || $fileChange > $tempNewestEvent['Event']['timestamp']) {
|
||||
if (empty($tempNewestEvent)) {
|
||||
$lastModified = 'No valid events';
|
||||
}
|
||||
$this->Event->export_types[$k]['recommendation'] = 0;
|
||||
$exportTypes[$k]['recommendation'] = 0;
|
||||
} else {
|
||||
$this->Event->export_types[$k]['recommendation'] = 1;
|
||||
$exportTypes[$k]['recommendation'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->Event->export_types[$k]['lastModified'] = $lastModified;
|
||||
$exportTypes[$k]['lastModified'] = $lastModified;
|
||||
if (!empty($job)) {
|
||||
$this->Event->export_types[$k]['job_id'] = $job['Job']['id'];
|
||||
$this->Event->export_types[$k]['progress'] = $job['Job']['progress'];
|
||||
$exportTypes[$k]['job_id'] = $job['Job']['id'];
|
||||
$exportTypes[$k]['progress'] = $job['Job']['progress'];
|
||||
} else {
|
||||
$this->Event->export_types[$k]['job_id'] = -1;
|
||||
$this->Event->export_types[$k]['progress'] = 0;
|
||||
$exportTypes[$k]['job_id'] = -1;
|
||||
$exportTypes[$k]['progress'] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadModel('Attribute');
|
||||
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
|
||||
$this->set('export_types', $this->Event->export_types);
|
||||
$this->set('sigTypes', array_keys($this->Event->Attribute->typeDefinitions));
|
||||
$this->set('export_types', $exportTypes);
|
||||
}
|
||||
|
||||
public function downloadExport($type, $extra = null)
|
||||
|
@ -3291,8 +3291,9 @@ class EventsController extends AppController
|
|||
if ($extra != null) {
|
||||
$extra = '_' . $extra;
|
||||
}
|
||||
$this->response->type($this->Event->export_types[$type]['extension']);
|
||||
$path = 'tmp/cached_exports/' . $type . DS . 'misp.' . strtolower($this->Event->export_types[$type]['type']) . $extra . '.' . $org . $this->Event->export_types[$type]['extension'];
|
||||
$exportType = $this->Event->exportTypes()[$type];
|
||||
$this->response->type($exportType['extension']);
|
||||
$path = 'tmp/cached_exports/' . $type . DS . 'misp.' . strtolower($exportType['type']) . $extra . '.' . $org . $exportType['extension'];
|
||||
$this->response->file($path, array('download' => true));
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ class ServersController extends AppController
|
|||
$this->Auth->allow(['cspReport']); // cspReport must work without authentication
|
||||
|
||||
parent::beforeFilter();
|
||||
$this->Security->unlockedActions[] = 'getApiInfo';
|
||||
$this->Security->unlockedActions[] = 'cspReport';
|
||||
// permit reuse of CSRF tokens on some pages.
|
||||
switch ($this->request->params['action']) {
|
||||
|
@ -1972,280 +1971,6 @@ class ServersController extends AppController
|
|||
return $this->RestResponse->viewData(array('uuid' => Configure::read('MISP.uuid')), $this->response->type());
|
||||
}
|
||||
|
||||
public function rest()
|
||||
{
|
||||
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
|
||||
$allValidApisFieldsContraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
|
||||
if ($this->request->is('post')) {
|
||||
$request = $this->request->data;
|
||||
if (!empty($request['Server'])) {
|
||||
$request = $this->request->data['Server'];
|
||||
}
|
||||
$curl = '';
|
||||
$python = '';
|
||||
try {
|
||||
$result = $this->__doRestQuery($request, $curl, $python);
|
||||
$this->set('curl', $curl);
|
||||
$this->set('python', $python);
|
||||
if (!$result) {
|
||||
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
|
||||
} else {
|
||||
$this->set('data', $result);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
$header = sprintf(
|
||||
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
|
||||
__('YOUR_API_KEY')
|
||||
);
|
||||
$this->set('header', $header);
|
||||
$this->set('allValidApis', $allValidApis);
|
||||
// formating for optgroup
|
||||
$allValidApisFormated = array();
|
||||
foreach ($allValidApis as $endpoint_url => $endpoint_data) {
|
||||
$allValidApisFormated[$endpoint_data['controller']][] = array('url' => $endpoint_url, 'action' => $endpoint_data['action']);
|
||||
}
|
||||
$this->set('allValidApisFormated', $allValidApisFormated);
|
||||
$this->set('allValidApisFieldsContraint', $allValidApisFieldsContraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @param string $curl
|
||||
* @param string $python
|
||||
* @return array|false
|
||||
*/
|
||||
private function __doRestQuery(array $request, &$curl = false, &$python = false)
|
||||
{
|
||||
App::uses('SyncTool', 'Tools');
|
||||
$params = array();
|
||||
|
||||
$logHeaders = $request['header'];
|
||||
if (!empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$logHeaders = explode("\n", $request['header']);
|
||||
foreach ($logHeaders as $k => $header) {
|
||||
if (strpos($header, 'Authorization') !== false) {
|
||||
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
|
||||
}
|
||||
}
|
||||
$logHeaders = implode("\n", $logHeaders);
|
||||
}
|
||||
|
||||
if (empty($request['body'])) {
|
||||
$historyBody = '';
|
||||
} else if (strlen($request['body']) > 65535) {
|
||||
$historyBody = ''; // body is too long to save into history table
|
||||
} else {
|
||||
$historyBody = $request['body'];
|
||||
}
|
||||
|
||||
$rest_history_item = array(
|
||||
'org_id' => $this->Auth->user('org_id'),
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'headers' => $logHeaders,
|
||||
'body' => $historyBody,
|
||||
'url' => $request['url'],
|
||||
'http_method' => $request['method'],
|
||||
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
|
||||
'show_result' => $request['show_result'],
|
||||
'skip_ssl' => $request['skip_ssl_validation'],
|
||||
'bookmark' => $request['bookmark'],
|
||||
'bookmark_name' => $request['name'],
|
||||
'timestamp' => time(),
|
||||
);
|
||||
if (!empty($request['url'])) {
|
||||
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
|
||||
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
|
||||
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
|
||||
unset($request['url']);
|
||||
} else {
|
||||
$url = $request['url'];
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException('URL not set.');
|
||||
}
|
||||
if (!empty($request['skip_ssl_validation'])) {
|
||||
$params['ssl_verify_peer'] = false;
|
||||
$params['ssl_verify_host'] = false;
|
||||
$params['ssl_verify_peer_name'] = false;
|
||||
$params['ssl_allow_self_signed'] = true;
|
||||
}
|
||||
$params['timeout'] = 300;
|
||||
App::uses('HttpSocket', 'Network/Http');
|
||||
$HttpSocket = new HttpSocket($params);
|
||||
|
||||
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
|
||||
$request['header'] = array(
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'MISP REST Client',
|
||||
);
|
||||
foreach ($temp_headers as $header) {
|
||||
$header = explode(':', $header);
|
||||
$header[0] = trim($header[0]);
|
||||
$header[1] = trim($header[1]);
|
||||
$request['header'][$header[0]] = $header[1];
|
||||
}
|
||||
$start = microtime(true);
|
||||
if (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'GET'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('get', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'POST' &&
|
||||
!empty($request['body'])
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('post', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'DELETE'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('delete', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$viewData = [
|
||||
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
|
||||
'url' => $url,
|
||||
'code' => $response->code,
|
||||
'headers' => $response->headers,
|
||||
];
|
||||
|
||||
if (!empty($request['show_result'])) {
|
||||
$viewData['data'] = $response->body;
|
||||
} else {
|
||||
if ($response->isOk()) {
|
||||
$viewData['data'] = 'Success.';
|
||||
} else {
|
||||
$viewData['data'] = 'Something went wrong.';
|
||||
}
|
||||
}
|
||||
$rest_history_item['outcome'] = $response->code;
|
||||
|
||||
$this->loadModel('RestClientHistory');
|
||||
$this->RestClientHistory->create();
|
||||
$this->RestClientHistory->save($rest_history_item);
|
||||
$this->RestClientHistory->cleanup($this->Auth->user('id'));
|
||||
|
||||
return $viewData;
|
||||
}
|
||||
|
||||
private function __generatePythonScript($request, $url)
|
||||
{
|
||||
$slashCounter = 0;
|
||||
$baseurl = '';
|
||||
$relative = '';
|
||||
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
|
||||
for ($i = 0; $i < strlen($url); $i++) {
|
||||
//foreach ($url as $url[$i]) {
|
||||
if ($url[$i] === '/') {
|
||||
$slashCounter += 1;
|
||||
if ($slashCounter == 3) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($slashCounter < 3) {
|
||||
$baseurl .= $url[$i];
|
||||
} else {
|
||||
$relative .= $url[$i];
|
||||
}
|
||||
}
|
||||
$python_script =
|
||||
sprintf(
|
||||
'misp_url = \'%s\'
|
||||
misp_key = \'%s\'
|
||||
misp_verifycert = %s
|
||||
relative_path = \'%s\'
|
||||
body = %s
|
||||
|
||||
from pymisp import ExpandedPyMISP
|
||||
|
||||
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
|
||||
misp.direct_call(relative_path, body)
|
||||
',
|
||||
$baseurl,
|
||||
$request['header']['Authorization'],
|
||||
$verifyCert,
|
||||
$relative,
|
||||
(empty($request['body']) ? 'None' : $request['body'])
|
||||
);
|
||||
return $python_script;
|
||||
}
|
||||
|
||||
private function __generateCurlQuery($type, $request, $url)
|
||||
{
|
||||
if ($type === 'get') {
|
||||
$curl = sprintf(
|
||||
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
} else {
|
||||
$curl = sprintf(
|
||||
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
|
||||
PHP_EOL,
|
||||
json_encode(json_decode($request['body'])),
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
}
|
||||
return $curl;
|
||||
}
|
||||
|
||||
public function getApiInfo()
|
||||
{
|
||||
$relative_path = $this->request->data['url'];
|
||||
$result = $this->RestResponse->getApiInfo($relative_path);
|
||||
if ($this->_isRest()) {
|
||||
if (!empty($result)) {
|
||||
$result['api_info'] = $result;
|
||||
}
|
||||
return $this->RestResponse->viewData($result, $this->response->type());
|
||||
} else {
|
||||
if (empty($result)) {
|
||||
return $this->RestResponse->viewData(' ', $this->response->type());
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->autoRender = false;
|
||||
$this->set('api_info', $result);
|
||||
$this->render('ajax/get_api_info');
|
||||
}
|
||||
}
|
||||
|
||||
public function cache($id = 'all')
|
||||
{
|
||||
if (Configure::read('MISP.background_jobs')) {
|
||||
|
@ -2501,17 +2226,6 @@ misp.direct_call(relative_path, body)
|
|||
return new CakeResponse(['status' => 204]);
|
||||
}
|
||||
|
||||
public function viewDeprecatedFunctionUse()
|
||||
{
|
||||
$data = $this->Deprecation->getDeprecatedAccessList($this->Server);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all tags for the rule picker.
|
||||
*
|
||||
|
@ -2566,9 +2280,6 @@ misp.direct_call(relative_path, body)
|
|||
return $this->RestResponse->viewData($syncFilteringRules);
|
||||
}
|
||||
|
||||
public function openapi() {
|
||||
}
|
||||
|
||||
public function pruneDuplicateUUIDs()
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
|
@ -2738,4 +2449,13 @@ misp.direct_call(relative_path, body)
|
|||
}
|
||||
return $this->RestResponse->viewData($users, $this->response->type());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return void
|
||||
*/
|
||||
public function rest()
|
||||
{
|
||||
$this->redirect(['controller' => 'api', 'action' => 'rest']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,15 @@ class JsonTool
|
|||
*/
|
||||
public static function decode($value)
|
||||
{
|
||||
$flags = defined('JSON_THROW_ON_ERROR') ? JSON_THROW_ON_ERROR : 0; // Throw exception on error if supported
|
||||
return json_decode($value, true, 512, $flags);
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
return json_decode($value, true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
$decoded = json_decode($value, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3197,16 +3197,7 @@ class AppModel extends Model
|
|||
*/
|
||||
public function jsonDecode($json)
|
||||
{
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
$decoded = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$decoded = json_decode($json, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
}
|
||||
|
||||
$decoded = JsonTool::decode($json);
|
||||
if (!is_array($decoded)) {
|
||||
throw new UnexpectedValueException('JSON must be array type, get ' . gettype($decoded));
|
||||
}
|
||||
|
|
|
@ -94,8 +94,6 @@ class Event extends AppModel
|
|||
|
||||
public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group');
|
||||
|
||||
public $export_types = [];
|
||||
|
||||
public $validFormats = array(
|
||||
'attack' => array('html', 'AttackExport', 'html'),
|
||||
'attack-sightings' => array('json', 'AttackSightingsExport', 'json'),
|
||||
|
@ -301,118 +299,6 @@ class Event extends AppModel
|
|||
]
|
||||
);
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
|
||||
$this->export_types = array(
|
||||
'json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'JSON',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'),
|
||||
'description' => __('Click this to download all events and attributes that you have access to in MISP JSON format.'),
|
||||
),
|
||||
'xml' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'XML',
|
||||
'scope' => 'Event',
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'),
|
||||
'requiresPublished' => 0,
|
||||
'description' => __('Click this to download all events and attributes that you have access to in MISP XML format.'),
|
||||
),
|
||||
'csv_sig' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_Sig',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => __('Click this to download all attributes that are indicators and that you have access to (except file attachments) in CSV format.'),
|
||||
),
|
||||
'csv_all' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_All',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('ignore' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => __('Click this to download all attributes that you have access to (except file attachments) in CSV format.'),
|
||||
),
|
||||
'suricata' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Suricata',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'suricata'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'snort' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Snort',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'snort'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'bro' => array(
|
||||
'extension' => '.intel',
|
||||
'type' => 'Bro',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'bro'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'stix' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'STIX',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
|
||||
'description' => __('Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.')
|
||||
),
|
||||
'stix2' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'STIX2',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1),
|
||||
'description' => __('Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.')
|
||||
),
|
||||
'rpz' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'RPZ',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'rpz'),
|
||||
'description' => __('Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.')
|
||||
),
|
||||
'text' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'TEXT',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'text', 'includeAttachments' => 1),
|
||||
'description' => __('Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.')
|
||||
),
|
||||
'yara' => array(
|
||||
'extension' => '.yara',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara'),
|
||||
'description' => __('Click this to download Yara rules generated from all relevant attributes.')
|
||||
),
|
||||
'yara-json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara-json'),
|
||||
'description' => __('Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private $assetCache = [];
|
||||
|
||||
public function beforeDelete($cascade = true)
|
||||
|
@ -6772,7 +6658,7 @@ class Event extends AppModel
|
|||
return $attribute_save;
|
||||
}
|
||||
|
||||
public function processFreeTextDataRouter($user, $attributes, $id, $default_comment = '', $proposals = false, $adhereToWarninglists = false, $returnRawResults = false)
|
||||
public function processFreeTextDataRouter(array $user, array $attributes, $id, $default_comment = '', $proposals = false, $adhereToWarninglists = false, $returnRawResults = false)
|
||||
{
|
||||
if (Configure::read('MISP.background_jobs') && count($attributes) > 5) { // on background process just big attributes batch
|
||||
/** @var Job $job */
|
||||
|
@ -7588,4 +7474,118 @@ class Event extends AppModel
|
|||
$banStatus['message'] = __('Emailing republishing ban setting is not enabled');
|
||||
return $banStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @deprecated
|
||||
*/
|
||||
public function exportTypes()
|
||||
{
|
||||
return array(
|
||||
'json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'JSON',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'),
|
||||
'description' => __('Click this to download all events and attributes that you have access to in MISP JSON format.'),
|
||||
),
|
||||
'xml' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'XML',
|
||||
'scope' => 'Event',
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'),
|
||||
'requiresPublished' => 0,
|
||||
'description' => __('Click this to download all events and attributes that you have access to in MISP XML format.'),
|
||||
),
|
||||
'csv_sig' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_Sig',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => __('Click this to download all attributes that are indicators and that you have access to (except file attachments) in CSV format.'),
|
||||
),
|
||||
'csv_all' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_All',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('ignore' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => __('Click this to download all attributes that you have access to (except file attachments) in CSV format.'),
|
||||
),
|
||||
'suricata' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Suricata',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'suricata'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'snort' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Snort',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'snort'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'bro' => array(
|
||||
'extension' => '.intel',
|
||||
'type' => 'Bro',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'bro'),
|
||||
'description' => __('Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
|
||||
),
|
||||
'stix' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'STIX',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
|
||||
'description' => __('Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.')
|
||||
),
|
||||
'stix2' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'STIX2',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1),
|
||||
'description' => __('Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.')
|
||||
),
|
||||
'rpz' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'RPZ',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'rpz'),
|
||||
'description' => __('Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.')
|
||||
),
|
||||
'text' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'TEXT',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'text', 'includeAttachments' => 1),
|
||||
'description' => __('Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.')
|
||||
),
|
||||
'yara' => array(
|
||||
'extension' => '.yara',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara'),
|
||||
'description' => __('Click this to download Yara rules generated from all relevant attributes.')
|
||||
),
|
||||
'yara-json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara-json'),
|
||||
'description' => __('Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.')
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class Job extends AppModel
|
|||
|
||||
$this->Event = ClassRegistry::init('Event');
|
||||
|
||||
if (in_array($type, array_keys($this->Event->export_types)) && $type !== 'bro') {
|
||||
if (in_array($type, array_keys($this->Event->exportTypes())) && $type !== 'bro') {
|
||||
|
||||
$this->getBackgroundJobsTool()->enqueue(
|
||||
BackgroundJobsTool::CACHE_QUEUE,
|
||||
|
|
|
@ -1980,7 +1980,7 @@ class Server extends AppModel
|
|||
App::uses('Folder', 'Utility');
|
||||
App::uses('File', 'Utility');
|
||||
// delete all cache files
|
||||
foreach ($this->Event->export_types as $type => $settings) {
|
||||
foreach ($this->Event->exportTypes() as $type => $settings) {
|
||||
$dir = new Folder(APP . 'tmp/cached_exports/' . $type);
|
||||
// No caches created for this type of export, move on
|
||||
if ($dir == null) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="dashboard_element">
|
||||
<h4 class="blue bold">API info</h4>
|
||||
<?php
|
||||
echo '<h4 class="blue bold">API info</h4>';
|
||||
foreach ($api_info as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
if (is_array($value)) {
|
||||
|
@ -30,11 +30,11 @@
|
|||
}
|
||||
$temp[] = $fieldName . $infoHtml;
|
||||
}
|
||||
$value = implode('<br />', $temp);
|
||||
$value = implode('<br>', $temp);
|
||||
} else {
|
||||
$value = h($value);
|
||||
}
|
||||
echo sprintf('<span class=blue>%s</span>:<br /><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
|
||||
echo sprintf('<span class=blue>%s</span>:<br><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -4,12 +4,12 @@
|
|||
<span id="showQB" class="btn btn-primary useCursorPointer" style="margin: 5px;"><span class="fa fa-wrench"> Query Builder</span></span>
|
||||
<?php
|
||||
$options = '<option value="">None</option>';
|
||||
foreach($allValidApisFormated as $scope => $actions) {
|
||||
$options .= sprintf('<optgroup label="%s">', $scope);
|
||||
foreach($actions as $action) {
|
||||
$options .= sprintf('<option value="%s">%s</option>', $action['url'], $action['action']);
|
||||
}
|
||||
foreach ($allAccessibleApis as $scope => $actions) {
|
||||
$options .= sprintf('<optgroup label="%s">', $scope);
|
||||
foreach ($actions as $action => $url) {
|
||||
$options .= sprintf('<option value="%s">%s</option>', $url, $action);
|
||||
}
|
||||
}
|
||||
echo sprintf('<select id="TemplateSelect">%s</select>', $options);
|
||||
?>
|
||||
<div id="apiInfo" style="margin-top: 15px;"></div>
|
||||
|
@ -63,7 +63,7 @@
|
|||
'class' => 'input-xxlarge'
|
||||
));
|
||||
?>
|
||||
<div class="input clear" style="width:100%;" />
|
||||
<div class="input clear" style="width:100%;"></div>
|
||||
<?php
|
||||
if (!empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
|
||||
echo $this->Form->input('use_full_path', array(
|
||||
|
@ -77,7 +77,7 @@
|
|||
'onChange' => 'toggleRestClientBookmark();'
|
||||
));
|
||||
?>
|
||||
<div class="input clear" style="width:100%;" />
|
||||
<div class="input clear" style="width:100%;"></div>
|
||||
<div id="bookmark-name" style="display:none;">
|
||||
<?php
|
||||
echo $this->Form->input('name', array(
|
||||
|
@ -86,7 +86,7 @@
|
|||
));
|
||||
?>
|
||||
</div>
|
||||
<div class="input clear" style="width:100%;" />
|
||||
<div class="input clear" style="width:100%;"></div>
|
||||
<?php
|
||||
echo $this->Form->input('show_result', array(
|
||||
'label' => __('Show result'),
|
||||
|
@ -220,18 +220,6 @@
|
|||
)
|
||||
));
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
var allValidApis = <?= json_encode($allValidApis); ?>;
|
||||
var fieldsConstraint = <?= json_encode($allValidApisFieldsContraint); ?>;
|
||||
$(function() {
|
||||
populate_rest_history('history');
|
||||
populate_rest_history('bookmark');
|
||||
toggleRestClientBookmark();
|
||||
setupCodeMirror();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.CodeMirror-wrap {
|
||||
border: 1px solid #cccccc;
|
|
@ -51,4 +51,3 @@
|
|||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -340,10 +340,10 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
),
|
||||
'text' => __('Import from…')
|
||||
));
|
||||
if ($canAccess('servers', 'rest')) {
|
||||
if ($canAccess('api', 'rest')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'rest',
|
||||
'url' => $baseurl . '/servers/rest',
|
||||
'url' => $baseurl . '/api/rest',
|
||||
'text' => __('REST client')
|
||||
));
|
||||
}
|
||||
|
@ -1609,17 +1609,15 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
case 'api':
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'openapi',
|
||||
'url' => $baseurl . '/servers/openapi',
|
||||
'url' => $baseurl . '/api/openapi',
|
||||
'text' => __('OpenAPI')
|
||||
));
|
||||
if ($isAclAdd) {
|
||||
if ($canAccess('servers', 'rest')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'rest',
|
||||
'url' => $baseurl . '/servers/rest',
|
||||
'text' => __('REST client')
|
||||
));
|
||||
}
|
||||
if ($canAccess('api', 'rest')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'rest',
|
||||
'url' => $baseurl . '/api/rest',
|
||||
'text' => __('REST client')
|
||||
));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -488,12 +488,12 @@
|
|||
'children' => array(
|
||||
array(
|
||||
'text' => __('OpenAPI'),
|
||||
'url' => $baseurl . '/servers/openapi'
|
||||
'url' => $baseurl . '/api/openapi'
|
||||
),
|
||||
array(
|
||||
'text' => __('REST client'),
|
||||
'url' => $baseurl . '/servers/rest',
|
||||
'requirement' => $canAccess('servers', 'rest')
|
||||
'url' => $baseurl . '/api/rest',
|
||||
'requirement' => $canAccess('api', 'rest')
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<p class="bold"><?php echo __('Check out the OpenAPI spec of the MISP Automation API <a href="%s">here</a>.', $baseurl . '/servers/openapi');?></p>
|
||||
<p><?php echo __('Automation functionality is designed to automatically feed other tools and systems with the data in your MISP repository.
|
||||
To to make this functionality available for automated tools an authentication key is used.');?>
|
||||
<br /><?php echo __('You can use the <a href="' . $baseurl . '/servers/rest">REST client</a> to test your API queries against your MISP and export the resulting tuned queries as curl or python scripts.');?>
|
||||
<br /><?php echo __('You can use the <a href="' . $baseurl . '/api/rest">REST client</a> to test your API queries against your MISP and export the resulting tuned queries as curl or python scripts.');?>
|
||||
<strong><?php echo __('Make sure you keep your API key secret as it gives access to the all of the data that you normally have access to in MISP.');?></strong>
|
||||
<?php echo __('To view the old MISP automation page, click <a href="' . $baseurl . '/automation/1">here</a>.');?>
|
||||
</p>
|
||||
|
|
|
@ -5057,12 +5057,12 @@ function checkRoleEnforceRateLimit() {
|
|||
|
||||
function queryDeprecatedEndpointUsage() {
|
||||
$.ajax({
|
||||
url: baseurl + '/servers/viewDeprecatedFunctionUse',
|
||||
url: baseurl + '/api/viewDeprecatedFunctionUse',
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
$('#deprecationResults').html(data);
|
||||
},
|
||||
error: function(data) {
|
||||
error: function() {
|
||||
handleGenericAjaxResponse({'saved':false, 'errors':['Could not query the deprecation statistics.']});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -19,11 +19,11 @@ function setApiInfoBox(isTyping) {
|
|||
function() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: baseurl + '/servers/getApiInfo',
|
||||
url: baseurl + '/api/getApiInfo',
|
||||
data: payload,
|
||||
success:function (data, textStatus) {
|
||||
success: function (data) {
|
||||
$('#apiInfo').html(data);
|
||||
addHoverInfo($('#ServerUrl').data('urlWithoutParam'));
|
||||
addHoverInfo($('#ServerUrl').data('urlWithoutParam'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -81,7 +81,7 @@ function toggleRestClientBookmark() {
|
|||
function removeRestClientHistoryItem(id) {
|
||||
$.ajax({
|
||||
data: '[]',
|
||||
success:function (data, textStatus) {
|
||||
success: function () {
|
||||
populate_rest_history('bookmark');
|
||||
populate_rest_history('history');
|
||||
},
|
||||
|
@ -94,32 +94,39 @@ function removeRestClientHistoryItem(id) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var allValidApis;
|
||||
var fieldsConstraint;
|
||||
var querybuilderTool;
|
||||
var debounceTimerUpdate;
|
||||
var allValidApis;
|
||||
var fieldsConstraint;
|
||||
var querybuilderTool;
|
||||
var debounceTimerUpdate;
|
||||
|
||||
$('form').submit(function(e) {
|
||||
$('#querybuilder').remove();
|
||||
return true;
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
$(function () {
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: baseurl + '/api/getAllApis',
|
||||
success: function (data) {
|
||||
allValidApis = data['allValidApis'];
|
||||
fieldsConstraint = data['fieldsConstraint'];
|
||||
}
|
||||
});
|
||||
|
||||
insertRawRestResponse();
|
||||
$('.format-toggle-button').bind('click', function() {
|
||||
if ($(this).data('toggle-type') == 'Raw') {
|
||||
var type = $(this).data('toggle-type');
|
||||
if (type === 'Raw') {
|
||||
$('#rest-response-container').empty();
|
||||
insertRawRestResponse();
|
||||
} else if ($(this).data('toggle-type') == 'HTML') {
|
||||
} else if (type === 'HTML') {
|
||||
$('#rest-response-container').empty();
|
||||
insertHTMLRestResponse();
|
||||
} else if ($(this).data('toggle-type') == 'JSON') {
|
||||
} else if (type === 'JSON') {
|
||||
$('#rest-response-container').empty();
|
||||
insertJSONRestResponse();
|
||||
} else if ($(this).data('toggle-type') == 'Download') {
|
||||
} else if (type === 'Download') {
|
||||
var download_content = $('#rest-response-hidden-container').text();
|
||||
var extension = 'json';
|
||||
var export_type = 'json';
|
||||
|
@ -154,7 +161,7 @@ function removeRestClientHistoryItem(id) {
|
|||
}
|
||||
});
|
||||
|
||||
$('#TemplateSelect').change(function(e) {
|
||||
$('#TemplateSelect').change(function() {
|
||||
var selected_template = $('#TemplateSelect').val();
|
||||
var previously_selected_template = $('#ServerUrl').data('urlWithoutParam')
|
||||
if (selected_template !== '' && allValidApis[selected_template] !== undefined) {
|
||||
|
@ -167,8 +174,9 @@ function removeRestClientHistoryItem(id) {
|
|||
var body_changed = allValidApis[previously_selected_template] !== undefined ? allValidApis[previously_selected_template].body != body_value : true;
|
||||
var refreshBody = (body_value === '' || (server_url_changed && !body_changed))
|
||||
if (refreshBody) {
|
||||
$('#ServerBody').val(allValidApis[selected_template].body);
|
||||
cm.setValue(allValidApis[selected_template].body)
|
||||
var body = JSON.stringify(allValidApis[selected_template].body, null, 4);
|
||||
$('#ServerBody').val(body);
|
||||
cm.setValue(body)
|
||||
}
|
||||
setApiInfoBox(false);
|
||||
updateQueryTool(selected_template, refreshBody);
|
||||
|
@ -232,6 +240,11 @@ function removeRestClientHistoryItem(id) {
|
|||
|
||||
/* Apply jquery chosen where applicable */
|
||||
$("#TemplateSelect").chosen();
|
||||
|
||||
populate_rest_history('history');
|
||||
populate_rest_history('bookmark');
|
||||
toggleRestClientBookmark();
|
||||
setupCodeMirror();
|
||||
});
|
||||
|
||||
|
||||
|
@ -499,11 +512,10 @@ function getCompletions(token, isJSONKey) {
|
|||
}
|
||||
if (isJSONKey) {
|
||||
var apiJson = allValidApis[url];
|
||||
var filtersJson = fieldsConstraint[url];
|
||||
allHints = (apiJson.mandatory !== undefined ? apiJson.mandatory : []).concat((apiJson.optional !== undefined ? apiJson.optional : []))
|
||||
var allHints = (apiJson.mandatory !== undefined ? apiJson.mandatory : []).concat((apiJson.optional !== undefined ? apiJson.optional : []))
|
||||
hints = findMatchingHints(token.string, allHints)
|
||||
} else {
|
||||
jsonKey = findPropertyFromValue(token)
|
||||
var jsonKey = findPropertyFromValue(token)
|
||||
var filtersJson = fieldsConstraint[url];
|
||||
if (filtersJson[jsonKey] !== undefined) {
|
||||
var values = filtersJson[jsonKey].values
|
||||
|
|
|
@ -736,6 +736,11 @@ class TestComprehensive(unittest.TestCase):
|
|||
self.assertIn('x-pgp-signature', response.headers)
|
||||
self.assertTrue(len(response.headers['x-pgp-signature']) > 0, response.headers['x-pgp-signature'])
|
||||
|
||||
def test_get_all_apis(self):
|
||||
response = self.admin_misp_connector._prepare_request('GET', 'api/getAllApis.json')
|
||||
self.assertEqual(200, response.status_code, response)
|
||||
response.json()
|
||||
|
||||
def _search(self, query: dict):
|
||||
response = self.admin_misp_connector._prepare_request('POST', 'events/restSearch', data=query)
|
||||
response = self.admin_misp_connector._check_response(response)
|
||||
|
|
Loading…
Reference in New Issue