chg: [internal] Error handling when converting STIX2MISP

pull/9470/head
Jakub Onderka 2024-01-01 02:31:50 +01:00
parent 54b156b378
commit a2fa480568
4 changed files with 104 additions and 63 deletions

View File

@ -127,7 +127,7 @@ class ProcessTool
{
$logMessage = '[' . date("Y-m-d H:i:s") . ' ' . getmypid() . "] $message\n";
if ($stderr) {
$logMessage = $stderr . "\n" . $logMessage;
$logMessage = rtrim($stderr) . "\n" . $logMessage;
}
file_put_contents(self::LOG_FILE, $logMessage, FILE_APPEND | LOCK_EX);
}

View File

@ -5922,61 +5922,24 @@ class Event extends AppModel
/**
* @param array $user
* @param string $file Path
* @param string $stix_version
* @param string $original_file
* @param string $stixVersion
* @param string $originalFile
* @param bool $publish
* @param int $distribution
* @param int $sharingGroupId
* @param bool $galaxiesAsTags
* @param bool $debug
* @return int|string|array
* @throws JsonException
* @throws InvalidArgumentException
* @throws Exception
*/
public function upload_stix(array $user, $file, $stix_version, $original_file, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $debug = false)
public function upload_stix(array $user, $file, $stixVersion, $originalFile, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $debug = false)
{
$scriptDir = APP . 'files' . DS . 'scripts';
if ($stix_version == '2' || $stix_version == '2.0' || $stix_version == '2.1') {
$scriptFile = $scriptDir . DS . 'stix2' . DS . 'stix2misp.py';
$output_path = $file . '.out';
$shell_command = [
ProcessTool::pythonBin(),
$scriptFile,
'-i', $file,
'--distribution', $distribution
];
if ($distribution == 4) {
array_push($shell_command, '--sharing_group_id', $sharingGroupId);
}
if ($galaxiesAsTags) {
$shell_command[] = '--galaxies_as_tags';
}
if ($debug) {
$shell_command[] = '--debug';
}
$stix_version = "STIX 2.1";
} elseif ($stix_version == '1' || $stix_version == '1.1' || $stix_version == '1.2') {
$scriptFile = $scriptDir . DS . 'stix2misp.py';
$output_path = $file . '.json';
$shell_command = [
ProcessTool::pythonBin(),
$scriptFile,
$file,
Configure::read('MISP.default_event_distribution'),
Configure::read('MISP.default_attribute_distribution'),
$this->__getTagNamesFromSynonyms($scriptDir)
];
$stix_version = "STIX 1.1";
} else {
throw new InvalidArgumentException('Invalid STIX version');
}
$decoded = $this->convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $debug);
$result = ProcessTool::execute($shell_command, null, true);
$result = preg_split("/\r\n|\n|\r/", trim($result));
$result = trim(end($result));
$tempFile = file_get_contents($file);
unlink($file);
$decoded = JsonTool::decode($result);
if (!empty($decoded['success'])) {
$data = FileAccessTool::readAndDelete($output_path);
$data = $this->jsonDecode($data);
$data = JsonTool::decodeArray($decoded['converted']);
if (empty($data['Event'])) {
$data = array('Event' => $data);
}
@ -6000,15 +5963,13 @@ class Event extends AppModel
}
}
}
if (!empty($decoded['stix_version'])) {
$stix_version = 'STIX ' . $decoded['stix_version'];
}
$stixVersion = $decoded['stix_version'];
$created_id = false;
$validationIssues = false;
$result = $this->_add($data, true, $user, '', null, false, null, $created_id, $validationIssues);
if ($result === true) {
if ($original_file) {
$this->add_original_file($tempFile, $original_file, $created_id, $stix_version);
if ($originalFile) {
$this->add_original_file($decoded['original'], $originalFile, $created_id, $stixVersion);
}
if ($publish && $user['Role']['perm_publish']) {
$this->publish($created_id);
@ -6031,6 +5992,76 @@ class Event extends AppModel
return $response;
}
/**
* @param string $stixVersion
* @param string $file
* @param int $distribution
* @param int $sharingGroupId
* @param bool $galaxiesAsTags
* @param bool $debug
* @return array
* @throws Exception
*/
private function convertStixToMisp($stixVersion, $file, $distribution, $sharingGroupId, $galaxiesAsTags, $debug)
{
$scriptDir = APP . 'files' . DS . 'scripts';
if ($stixVersion === '2' || $stixVersion === '2.0' || $stixVersion === '2.1') {
$scriptFile = $scriptDir . DS . 'stix2' . DS . 'stix2misp.py';
$outputPath = $file . '.out';
$shellCommand = [
ProcessTool::pythonBin(),
$scriptFile,
'-i', $file,
'--distribution', $distribution,
];
if ($distribution == 4) {
array_push($shellCommand, '--sharing_group_id', $sharingGroupId);
}
if ($galaxiesAsTags) {
$shellCommand[] = '--galaxies_as_tags';
}
if ($debug) {
$shellCommand[] = '--debug';
}
$stixVersion = "STIX 2.1";
} else if ($stixVersion === '1' || $stixVersion === '1.1' || $stixVersion === '1.2') {
$scriptFile = $scriptDir . DS . 'stix2misp.py';
$outputPath = $file . '.json';
$shellCommand = [
ProcessTool::pythonBin(),
$scriptFile,
$file,
Configure::read('MISP.default_event_distribution'),
Configure::read('MISP.default_attribute_distribution'),
$this->__getTagNamesFromSynonyms($scriptDir)
];
$stixVersion = "STIX 1.1";
} else {
throw new InvalidArgumentException('Invalid STIX version');
}
try {
$stdout = ProcessTool::execute($shellCommand, null, true);
} catch (ProcessException $e) {
$stdout = $e->stdout();
}
$stdout = preg_split("/\r\n|\n|\r/", trim($stdout));
$stdout = trim(end($stdout));
$decoded = JsonTool::decode($stdout);
if (empty($decoded['stix_version'])) {
$decoded['stix_version'] = $stixVersion;
}
$decoded['original'] = FileAccessTool::readAndDelete($file);
if (!empty($decoded['success'])) {
$decoded['converted'] = FileAccessTool::readAndDelete($outputPath);
}
return $decoded;
}
private function __handleGalaxiesAndClusters($user, &$data)
{
if (!empty($data['Galaxy'])) {

View File

@ -44,7 +44,7 @@ def _handle_return_message(traceback):
return '\n - '.join(traceback)
def _process_stix_file(args: argparse.ArgumentParser):
def _process_stix_file(args: argparse.Namespace):
try:
with open(args.input, 'rt', encoding='utf-8') as f:
bundle = stix2_parser(
@ -81,8 +81,11 @@ def _process_stix_file(args: argparse.ArgumentParser):
file=sys.stderr
)
except Exception as e:
print(json.dumps({'error': e.__str__()}))
error = type(e).__name__ + ': ' + e.__str__()
print(json.dumps({'error': error}))
traceback.print_tb(e.__traceback__)
print(error, file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
@ -109,8 +112,7 @@ if __name__ == '__main__':
)
try:
args = argparser.parse_args()
_process_stix_file(args)
except SystemExit:
except SystemExit as e:
print(
json.dumps(
{
@ -118,4 +120,6 @@ if __name__ == '__main__':
}
)
)
sys.exit(1)
_process_stix_file(args)

View File

@ -96,7 +96,7 @@ class StixParser():
# Convert the MISP event we create from the STIX document into json format
# and write it in the output file
def saveFile(self):
def save_to_file(self):
for attribute in self.misp_event.attributes:
attribute_uuid = uuid.UUID(attribute.uuid) if isinstance(attribute.uuid, str) else attribute.uuid
if attribute_uuid.version not in _RFC_UUID_VERSIONS:
@ -1547,19 +1547,20 @@ def generate_event(filename, tries=0):
return STIXPackage.from_xml(filename)
except NamespaceNotFoundError:
if tries == 1:
print(json.dump({'error': 'Cannot handle STIX namespace'}))
sys.exit()
print(json.dumps({'error': 'Cannot handle STIX namespace'}))
sys.exit(1)
_update_namespaces()
return generate_event(filename, 1)
except NotImplementedError:
print(json.dumps({'error': 'Missing python library: stix_edh'}))
sys.exit(1)
except Exception as e:
try:
import maec
print(json.dumps({'error': f'Error while loading the STIX file: {e.__str__()}'}))
except ImportError:
print(json.dumps({'error': 'Missing python library: maec'}))
sys.exit(0)
sys.exit(1)
def is_from_misp(event):
@ -1567,7 +1568,7 @@ def is_from_misp(event):
title = event.stix_header.title
except AttributeError:
return False
return ('Export from ' in title and 'MISP' in title)
return 'Export from ' in title and 'MISP' in title
def main(args):
@ -1578,11 +1579,16 @@ def main(args):
stix_parser = StixFromMISPParser() if from_misp else ExternalStixParser()
stix_parser.load_event(args[2:], filename, from_misp, event.version)
stix_parser.build_misp_event(event)
stix_parser.saveFile()
stix_parser.save_to_file()
print(json.dumps({'success': 1}))
sys.exit(0)
except Exception as e:
print(json.dumps({'error': e.__str__()}))
error = type(e).__name__ + ': ' + e.__str__()
print(json.dumps({'error': error}))
traceback.print_tb(e.__traceback__)
print(error, file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main(sys.argv)