diff options
author | Michael Weiser <michael.weiser@gmx.de> | 2019-02-14 21:59:53 +0000 |
---|---|---|
committer | Michael Weiser <michael.weiser@gmx.de> | 2019-02-18 17:58:58 +0000 |
commit | d6dbf8f9e42692fb409c0ce683b8bde93c43fbe9 (patch) | |
tree | 7dac6ed942194abedbb63e70b3ab6cf0c60cda44 /peekaboo | |
parent | 603abb51499c61f02bbcfc3bfc4112b62907da30 (diff) |
Localise client communication
Since we're communicating with a client that potentially wants to
present our findings to an administrator or even end user to inform them
about what's going on with their email or data, we had German messages
going to the client. Obviously, this is very inconvenient and inflexible
for non-German speakers. Also, it doesn't look very good in the code
base.
Replace static German strings in client communication with English
message ids for internationalisation by gettext. Add an appropriate
German message file. Use the pure-python standard-library gettext module
of python to employ message catalog files. Add the catalog files to
MANIFEST.in for automatic installation into the package by setup.py. Add
Babel as a development dependency to extract messages and update and
compile the catalogs. Add documentation for developers and translators,
how to work with these translations.
Diffstat (limited to 'peekaboo')
-rw-r--r-- | peekaboo/config.py | 2 | ||||
-rw-r--r-- | peekaboo/daemon.py | 19 | ||||
-rw-r--r-- | peekaboo/locale/de/LC_MESSAGES/peekaboo.mo | bin | 0 -> 3872 bytes | |||
-rw-r--r-- | peekaboo/locale/de/LC_MESSAGES/peekaboo.po | 191 | ||||
-rw-r--r-- | peekaboo/locale/peekaboo.pot | 186 | ||||
-rw-r--r-- | peekaboo/ruleset/__init__.py | 12 | ||||
-rw-r--r-- | peekaboo/ruleset/engine.py | 9 | ||||
-rw-r--r-- | peekaboo/ruleset/rules.py | 80 | ||||
-rw-r--r-- | peekaboo/sample.py | 6 | ||||
-rw-r--r-- | peekaboo/server.py | 41 |
10 files changed, 478 insertions, 68 deletions
diff --git a/peekaboo/config.py b/peekaboo/config.py index 9867b12..e98b929 100644 --- a/peekaboo/config.py +++ b/peekaboo/config.py @@ -83,6 +83,7 @@ class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes self.use_debug_module = False self.keep_mail_data = False self.processing_info_dir = '/var/lib/peekaboo/malware_reports' + self.report_locale = None self.db_url = 'sqlite:////var/lib/peekaboo/peekaboo.db' self.config_file = '/opt/peekaboo/etc/peekaboo.conf' self.ruleset_config = '/opt/peekaboo/etc/ruleset.conf' @@ -114,6 +115,7 @@ class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes 'use_debug_module': ['global', 'use_debug_module'], 'keep_mail_data': ['global', 'keep_mail_data'], 'processing_info_dir': ['global', 'processing_info_dir'], + 'report_locale': ['global', 'report_locale'], 'db_url': ['db', 'url'], 'ruleset_config': ['ruleset', 'config'], 'cuckoo_mode': ['cuckoo', 'mode'], diff --git a/peekaboo/daemon.py b/peekaboo/daemon.py index 326ea03..f80d485 100644 --- a/peekaboo/daemon.py +++ b/peekaboo/daemon.py @@ -26,6 +26,7 @@ components. """ import errno +import gettext import os import sys import grp @@ -253,6 +254,24 @@ def run(): logging.critical(error) sys.exit(1) + # find localisation in our package directory + locale_domain = 'peekaboo' + locale_dir = os.path.join(os.path.dirname(__file__), 'locale') + languages = None + if config.report_locale: + logger.debug('Looking for translations for preconfigured locale "%s"', + config.report_locale) + languages = [config.report_locale] + if not gettext.find(locale_domain, locale_dir, languages): + logger.warning('Translation file not found - falling back to ' + 'system configuration.') + languages = None + + logger.debug('Installing report message translations') + translation = gettext.translation(locale_domain, locale_dir, languages, + fallback=True) + translation.install() + # establish a connection to the database try: db_con = PeekabooDatabase( diff --git a/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo b/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo Binary files differnew file mode 100644 index 0000000..236347d --- /dev/null +++ b/peekaboo/locale/de/LC_MESSAGES/peekaboo.mo diff --git a/peekaboo/locale/de/LC_MESSAGES/peekaboo.po b/peekaboo/locale/de/LC_MESSAGES/peekaboo.po new file mode 100644 index 0000000..ec26b6f --- /dev/null +++ b/peekaboo/locale/de/LC_MESSAGES/peekaboo.po @@ -0,0 +1,191 @@ +# Peekaboo translations of client-visible strings +# Copyright (C) 2019 science + computing ag +# Michael Weiser <michael.weiser@gmx.de> +# +msgid "" +msgstr "" +"Project-Id-Version: PeekabooAV 1.6.2\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2019-02-15 12:33+0000\n" +"PO-Revision-Date: 2019-02-14 22:02+0000\n" +"Last-Translator: Michael Weiser <michael.weiser@gmx.de>\n" +"Language: de\n" +"Language-Team: Michael Weiser <michael.weiser@gmx.de>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.4.0\n" + +#: peekaboo/sample.py:171 +#, python-format +msgid "File \"%s\" %s is being analyzed" +msgstr "Datei \"%s\" %s wird analysiert" + +#: peekaboo/sample.py:222 +#, python-format +msgid "File \"%s\" is considered \"%s\"" +msgstr "Die Datei \"%s\" wird als \"%s\" betrachtet" + +#: peekaboo/sample.py:291 +#, python-format +msgid "File \"%s\": %s" +msgstr "Datei \"%s\": %s" + +#: peekaboo/server.py:143 +msgid "Hello, this is Peekaboo." +msgstr "Hallo das ist Peekaboo" + +#: peekaboo/server.py:176 +#, fuzzy +msgid "Error: Invalid JSON in request." +msgstr "FEHLER: Ungueltiges JSON." + +#: peekaboo/server.py:181 +msgid "ERROR: Invalid data structure." +msgstr "FEHLER: Ungueltiges Datenformat." + +#: peekaboo/server.py:188 +msgid "ERROR: Incomplete data structure." +msgstr "FEHLER: Unvollstaendige Datenstruktur." + +#: peekaboo/server.py:195 +msgid "ERROR: Path does not exist or no permission to access it." +msgstr "FEHLER: Pfad existiert nicht oder Zugriff verweigert." + +#: peekaboo/server.py:202 +msgid "ERRROR: Input is not a file" +msgstr "FEHLER: Eingabe ist keine Datei." + +#: peekaboo/server.py:237 +msgid "Files are being analyzed..." +msgstr "Dateien werden analysiert..." + +#: peekaboo/server.py:260 +msgid "Peekaboo is shutting down." +msgstr "Peekaboo wird beendet." + +#: peekaboo/server.py:314 +#, python-format +msgid "The file collection has been categorized \"%s\"" +msgstr "Die Datensammlung wurde als \"%s\" eingestuft" + +#: peekaboo/ruleset/__init__.py:66 +msgid "Rule without result" +msgstr "Regel ohne Ergebnis" + +#: peekaboo/ruleset/__init__.py:70 +#, python-format +msgid "Result \"%s\" of rule %s - %s, analysis continues: %s." +msgstr "Ergebnis \"%s\" der Regel %s - %s, Analyse wird fortgesetzt: %s." + +#: peekaboo/ruleset/__init__.py:72 +msgid "Yes" +msgstr "Ja" + +#: peekaboo/ruleset/__init__.py:72 +msgid "No" +msgstr "Nein" + +#: peekaboo/ruleset/engine.py:88 +#, python-format +msgid "Rule '%s' is disabled." +msgstr "Regel '%s' ist deaktiviert." + +#: peekaboo/ruleset/engine.py:102 +msgid "Rule aborted with error" +msgstr "Regel mit Fehler abgebrochen" + +#: peekaboo/ruleset/rules.py:84 +msgid "File is not yet known to the system" +msgstr "Datei ist dem System noch nicht bekannt" + +#: peekaboo/ruleset/rules.py:101 +#, python-format +msgid "File has more than %d bytes" +msgstr "Datei hat mehr als %d bytes" + +#: peekaboo/ruleset/rules.py:106 +#, python-format +msgid "File is more than %d bytes long" +msgstr "Datei ist nur %d bytes lang" + +#: peekaboo/ruleset/rules.py:125 +msgid "File type is on whitelist" +msgstr "Dateityp ist auf Whitelist" + +#: peekaboo/ruleset/rules.py:129 +msgid "File type is not on whitelist" +msgstr "Dateityp ist nicht auf Whitelist" + +#: peekaboo/ruleset/rules.py:148 +msgid "File type is on the list of types to analyze" +msgstr "Dateityp ist auf der Liste der zu analysiserenden Typen" + +#: peekaboo/ruleset/rules.py:153 +#, python-format +msgid "File type is not on the list of types to analyse (%s)" +msgstr "Dateityp ist nicht auf der Liste der zu analysierenden Typen (%s)" + +#: peekaboo/ruleset/rules.py:166 +msgid "The file contains an Office macro" +msgstr "Die Datei beinhaltet ein Office-Makro" + +#: peekaboo/ruleset/rules.py:170 +msgid "The file does not contain a recognizable Office macro" +msgstr "Die Datei beinhaltet kein erkennbares Office-Makro" + +#: peekaboo/ruleset/rules.py:197 peekaboo/ruleset/rules.py:327 +msgid "" +"Behavioral analysis by Cuckoo has produced an error and did not finish " +"successfully" +msgstr "" +"Die Verhaltensanalyse durch Cuckoo hat einen Fehler produziert und konnte" +" nicht erfolgreich abgeschlossen werden" + +#: peekaboo/ruleset/rules.py:231 +msgid "Empty list of malicious signatures" +msgstr "Leere Liste schaedlicher Signaturen" + +#: peekaboo/ruleset/rules.py:249 +msgid "No signature suggesting malware detected" +msgstr "Keine Signatur erkannt die auf Schadcode hindeutet" + +#: peekaboo/ruleset/rules.py:254 +#, python-format +msgid "The following signatures have been recognized: %s" +msgstr "Folgende Signaturen wurden erkannt: %s" + +#: peekaboo/ruleset/rules.py:271 +#, python-format +msgid "Cuckoo score >= %s: %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:276 +#, python-format +msgid "Cuckoo score < %s: %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:292 +msgid "Empty domain list" +msgstr "Leere Domainliste" + +#: peekaboo/ruleset/rules.py:297 +#, python-format +msgid "The file attempts to contact at least one domain on the blacklist (%s)" +msgstr "" +"Die Datei versucht mindestens eine Domain aus der Blacklist zu " +"kontaktieren (%s)" + +#: peekaboo/ruleset/rules.py:303 +msgid "File does not seem to attempt contact with domains on the blacklist" +msgstr "Datei scheint keine Domains aus der Blacklist kontaktieren zu wollen" + +#: peekaboo/ruleset/rules.py:322 +msgid "Behavioral analysis by Cuckoo completed successfully" +msgstr "Die Verhaltensanalyse durch Cuckoo wurde erfolgreich abgeschlossen" + +#: peekaboo/ruleset/rules.py:340 +msgid "File does not seem to exhibit recognizable malicious behaviour" +msgstr "Datei scheint keine erkennbaren Schadroutinen zu starten" + diff --git a/peekaboo/locale/peekaboo.pot b/peekaboo/locale/peekaboo.pot new file mode 100644 index 0000000..99321e7 --- /dev/null +++ b/peekaboo/locale/peekaboo.pot @@ -0,0 +1,186 @@ +# Translations template for PROJECT. +# Copyright (C) 2019 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2019. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2019-02-15 12:33+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.4.0\n" + +#: peekaboo/sample.py:171 +#, python-format +msgid "File \"%s\" %s is being analyzed" +msgstr "" + +#: peekaboo/sample.py:222 +#, python-format +msgid "File \"%s\" is considered \"%s\"" +msgstr "" + +#: peekaboo/sample.py:291 +#, python-format +msgid "File \"%s\": %s" +msgstr "" + +#: peekaboo/server.py:143 +msgid "Hello, this is Peekaboo." +msgstr "" + +#: peekaboo/server.py:176 +msgid "Error: Invalid JSON in request." +msgstr "" + +#: peekaboo/server.py:181 +msgid "ERROR: Invalid data structure." +msgstr "" + +#: peekaboo/server.py:188 +msgid "ERROR: Incomplete data structure." +msgstr "" + +#: peekaboo/server.py:195 +msgid "ERROR: Path does not exist or no permission to access it." +msgstr "" + +#: peekaboo/server.py:202 +msgid "ERRROR: Input is not a file" +msgstr "" + +#: peekaboo/server.py:237 +msgid "Files are being analyzed..." +msgstr "" + +#: peekaboo/server.py:260 +msgid "Peekaboo is shutting down." +msgstr "" + +#: peekaboo/server.py:314 +#, python-format +msgid "The file collection has been categorized \"%s\"" +msgstr "" + +#: peekaboo/ruleset/__init__.py:66 +msgid "Rule without result" +msgstr "" + +#: peekaboo/ruleset/__init__.py:70 +#, python-format +msgid "Result \"%s\" of rule %s - %s, analysis continues: %s." +msgstr "" + +#: peekaboo/ruleset/__init__.py:72 +msgid "Yes" +msgstr "" + +#: peekaboo/ruleset/__init__.py:72 +msgid "No" +msgstr "" + +#: peekaboo/ruleset/engine.py:88 +#, python-format +msgid "Rule '%s' is disabled." +msgstr "" + +#: peekaboo/ruleset/engine.py:102 +msgid "Rule aborted with error" +msgstr "" + +#: peekaboo/ruleset/rules.py:84 +msgid "File is not yet known to the system" +msgstr "" + +#: peekaboo/ruleset/rules.py:101 +#, python-format +msgid "File has more than %d bytes" +msgstr "" + +#: peekaboo/ruleset/rules.py:106 +#, python-format +msgid "File is more than %d bytes long" +msgstr "" + +#: peekaboo/ruleset/rules.py:125 +msgid "File type is on whitelist" +msgstr "" + +#: peekaboo/ruleset/rules.py:129 +msgid "File type is not on whitelist" +msgstr "" + +#: peekaboo/ruleset/rules.py:148 +msgid "File type is on the list of types to analyze" +msgstr "" + +#: peekaboo/ruleset/rules.py:153 +#, python-format +msgid "File type is not on the list of types to analyse (%s)" +msgstr "" + +#: peekaboo/ruleset/rules.py:166 +msgid "The file contains an Office macro" +msgstr "" + +#: peekaboo/ruleset/rules.py:170 +msgid "The file does not contain a recognizable Office macro" +msgstr "" + +#: peekaboo/ruleset/rules.py:197 peekaboo/ruleset/rules.py:327 +msgid "" +"Behavioral analysis by Cuckoo has produced an error and did not finish " +"successfully" +msgstr "" + +#: peekaboo/ruleset/rules.py:231 +msgid "Empty list of malicious signatures" +msgstr "" + +#: peekaboo/ruleset/rules.py:249 +msgid "No signature suggesting malware detected" +msgstr "" + +#: peekaboo/ruleset/rules.py:254 +#, python-format +msgid "The following signatures have been recognized: %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:271 +#, python-format +msgid "Cuckoo score >= %s: %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:276 +#, python-format +msgid "Cuckoo score < %s: %s" +msgstr "" + +#: peekaboo/ruleset/rules.py:292 +msgid "Empty domain list" +msgstr "" + +#: peekaboo/ruleset/rules.py:297 +#, python-format +msgid "The file attempts to contact at least one domain on the blacklist (%s)" +msgstr "" + +#: peekaboo/ruleset/rules.py:303 +msgid "File does not seem to attempt contact with domains on the blacklist" +msgstr "" + +#: peekaboo/ruleset/rules.py:322 +msgid "Behavioral analysis by Cuckoo completed successfully" +msgstr "" + +#: peekaboo/ruleset/rules.py:340 +msgid "File does not seem to exhibit recognizable malicious behaviour" +msgstr "" + diff --git a/peekaboo/ruleset/__init__.py b/peekaboo/ruleset/__init__.py index 60e9e87..87f4ebb 100644 --- a/peekaboo/ruleset/__init__.py +++ b/peekaboo/ruleset/__init__.py @@ -56,19 +56,19 @@ class RuleResult: """ def __init__(self, rule, result=Result.unknown, - reason='regel ohne Ergebnis', + reason=None, further_analysis=True): self.result = Result.unchecked self.rule = rule self.result = result self.reason = reason + if self.reason is None: + self.reason = _("Rule without result") self.further_analysis = further_analysis def __str__(self): - return ("Ergebnis \"%s\" der Regel %s - %s, Analyse wird fortgesetzt: %s."\ - % (self.result.name, - self.rule, - self.reason, - 'Ja' if self.further_analysis else 'Nein')) + return (_("Result \"%s\" of rule %s - %s, analysis continues: %s.") + % (self.result.name, self.rule, self.reason, + _('Yes') if self.further_analysis else _('No'))) __repr__ = __str__ diff --git a/peekaboo/ruleset/engine.py b/peekaboo/ruleset/engine.py index 5c71296..a277931 100644 --- a/peekaboo/ruleset/engine.py +++ b/peekaboo/ruleset/engine.py @@ -83,9 +83,10 @@ class RulesetEngine(object): result = rule.evaluate(sample) else: logger.debug("Rule '%s' is disabled." % rule_name) - result = RuleResult(rule_name, result=Result.unchecked, - reason="Regel '%s' ist deaktiviert." % rule_name, - further_analysis=True) + result = RuleResult( + rule_name, result=Result.unchecked, + reason=_("Rule '%s' is disabled.") % rule_name, + further_analysis=True) sample.add_rule_result(result) except CuckooReportPendingException as e: @@ -98,7 +99,7 @@ class RulesetEngine(object): logger.exception(e) # create "fake" RuleResult result = RuleResult("RulesetEngine", result=Result.unknown, - reason="Regel mit Fehler abgebrochen", + reason=_("Rule aborted with error"), further_analysis=True) sample.add_rule_result(result) diff --git a/peekaboo/ruleset/rules.py b/peekaboo/ruleset/rules.py index 52efacd..b5ae850 100644 --- a/peekaboo/ruleset/rules.py +++ b/peekaboo/ruleset/rules.py @@ -81,7 +81,7 @@ class KnownRule(Rule): return self.result(sample_info.result, sample_info.reason, False) return self.result(Result.unknown, - "Datei ist dem System noch nicht bekannt", + _("File is not yet known to the system"), True) @@ -98,12 +98,13 @@ class FileLargerThanRule(Rule): if sample.file_size > size: return self.result(Result.unknown, - "Datei hat mehr als %d bytes" % size, + _("File has more than %d bytes") % size, True) - return self.result(Result.ignored, - "Datei ist nur %d bytes lang" % sample.file_size, - False) + return self.result( + Result.ignored, + _("File is more than %d bytes long") % sample.file_size, + False) class FileTypeOnWhitelistRule(Rule): @@ -116,16 +117,16 @@ class FileTypeOnWhitelistRule(Rule): whitelist and we could determine at least one. """ whitelist = self.config.get('whitelist', ()) if not whitelist: - logger.warn("Empty whitelist, check ruleset config.") + logger.warning("Empty whitelist, check ruleset config.") return self.result(Result.unknown, "Whitelist ist leer", True) if sample.mimetypes and sample.mimetypes.issubset(set(whitelist)): return self.result(Result.ignored, - "Dateityp ist auf Whitelist", + _("File type is on whitelist"), False) return self.result(Result.unknown, - "Dateityp ist nicht auf Whitelist", + _("File type is not on whitelist"), True) @@ -139,19 +140,18 @@ class FileTypeOnGreylistRule(Rule): greylist or in case we don't have one. """ greylist = self.config.get('greylist', ()) if not greylist: - logger.warn("Empty greylist, check ruleset config.") + logger.warning("Empty greylist, check ruleset config.") return self.result(Result.unknown, "Greylist is leer", False) if not sample.mimetypes or sample.mimetypes.intersection(set(greylist)): return self.result(Result.unknown, - "Dateityp ist auf der Liste der zu " - "analysiserenden Typen", + _("File type is on the list of types to " + "analyze"), True) return self.result(Result.unknown, - "Dateityp ist nicht auf der Liste der zu " - "analysierenden Typen (%s)" % - (str(sample.mimetypes)), + _("File type is not on the list of types to " + "analyse (%s)") % (str(sample.mimetypes)), False) @@ -163,12 +163,12 @@ class OfficeMacroRule(Rule): """ Report the sample as bad if it contains a macro. """ if sample.office_macros: return self.result(Result.bad, - "Die Datei beinhaltet ein Office-Makro", + _("The file contains an Office macro"), False) return self.result(Result.unknown, - "Die Datei beinhaltet kein erkennbares " - "Office-Makro", + _("The file does not contain a recognizable " + "Office macro"), True) @@ -194,9 +194,8 @@ class CuckooRule(Rule): except CuckooAnalysisFailedException: return self.result( Result.failed, - "Die Verhaltensanalyse durch Cuckoo hat einen " - "Fehler produziert und konnte nicht erfolgreich " - "abgeschlossen werden", + _("Behavioral analysis by Cuckoo has produced an error " + "and did not finish successfully"), False) logger.info('Sample submitted to Cuckoo. Job ID: %s. ' @@ -227,9 +226,9 @@ class CuckooEvilSigRule(CuckooRule): # grep -o "description.*" -R . ~/cuckoo2.0/modules/signatures/ bad_sigs = self.config.get('signature', ()) if not bad_sigs: - logger.warn("Empty bad signature list, check ruleset config.") + logger.warning("Empty bad signature list, check ruleset config.") return self.result(Result.unknown, - "Leere Liste schaedlicher Signaturen", + _("Empty list of malicious signatures"), True) # look through matched signatures @@ -247,13 +246,13 @@ class CuckooEvilSigRule(CuckooRule): if not matched_bad_sigs: return self.result(Result.unknown, - "Keine Signatur erkannt die auf Schadcode " - "hindeutet", + _("No signature suggesting malware detected"), True) matched = ''.ljust(8).join(["%s\n" % s for s in matched_bad_sigs]) return self.result(Result.bad, - "Folgende Signaturen wurden erkannt: %s" % matched, + _("The following signatures have been recognized: " + "%s") % matched, False) @@ -269,12 +268,12 @@ class CuckooScoreRule(CuckooRule): if report.score >= threshold: return self.result(Result.bad, - "Cuckoo score >= %s: %s" % + _("Cuckoo score >= %s: %s") % (threshold, report.score), False) return self.result(Result.unknown, - "Cuckoo score < %s: %s" % + _("Cuckoo score < %s: %s") % (threshold, report.score), True) @@ -289,20 +288,20 @@ class RequestsEvilDomainRule(CuckooRule): list of evil domains. """ evil_domains = self.config.get('domain', ()) if not evil_domains: - logger.warn("Empty evil domain list, check ruleset config.") - return self.result(Result.unknown, "Leere Domainliste", True) + logger.warning("Empty evil domain list, check ruleset config.") + return self.result(Result.unknown, _("Empty domain list"), True) for domain in report.requested_domains: if domain in evil_domains: return self.result(Result.bad, - "Die Datei versucht mindestens eine Domain " - "aus der Blacklist zu kontaktieren " - "(%s)" % domain, + _("The file attempts to contact at least " + "one domain on the blacklist (%s)") + % domain, False) return self.result(Result.unknown, - "Datei scheint keine Domains aus der Blacklist " - "kontaktieren zu wollen", + _("File does not seem to attempt contact with " + "domains on the blacklist"), True) @@ -320,14 +319,13 @@ class CuckooAnalysisFailedRule(CuckooRule): for entry in report.cuckoo_server_messages: if 'analysis completed successfully' in entry: return self.result(Result.unknown, - "Die Verhaltensanalyse durch Cuckoo wurde " - "erfolgreich abgeschlossen", + _("Behavioral analysis by Cuckoo " + "completed successfully"), True) return self.result(Result.failed, - "Die Verhaltensanalyse durch Cuckoo hat einen " - "Fehler produziert und konnte nicht erfolgreich " - "abgeschlossen werden", + _("Behavioral analysis by Cuckoo has produced " + "an error and did not finish successfully"), False) @@ -339,6 +337,6 @@ class FinalRule(Rule): """ Report an unknown analysis result indicating that nothing much can be said about the sample. """ return self.result(Result.unknown, - "Datei scheint keine erkennbaren Schadroutinen " - "zu starten", + _("File does not seem to exhibit recognizable " + "malicious behaviour"), True) diff --git a/peekaboo/sample.py b/peekaboo/sample.py index b065415..8ed0c2b 100644 --- a/peekaboo/sample.py +++ b/peekaboo/sample.py @@ -168,7 +168,7 @@ class Sample(object): self.initialized = True - self.__report.append("Datei \"%s\" %s wird analysiert" + self.__report.append(_("File \"%s\" %s is being analyzed") % (self.__filename, self.sha256sum)) # log some additional info to report to aid debugging @@ -219,7 +219,7 @@ class Sample(object): # Changed intentionally to not trigger configured god/bad matching # patterns in clients (e.g. AMaViS) any more since we switched to # reporting an overall analysis batch result. - return self.__report + ["Die Datei \"%s\" wird als \"%s\" betrachtet\n" + return self.__report + [_("File \"%s\" is considered \"%s\"") % (self.__filename, self.__result.name)] @property @@ -288,7 +288,7 @@ class Sample(object): self.__reason = res.reason # also append a report message right away - self.__report.append("Datei \"%s\": %s" % (self.__filename, str(res))) + self.__report.append(_("File \"%s\": %s") % (self.__filename, str(res))) def dump_processing_info(self): """ diff --git a/peekaboo/server.py b/peekaboo/server.py index 3b9486e..a57a83e 100644 --- a/peekaboo/server.py +++ b/peekaboo/server.py @@ -140,7 +140,7 @@ class PeekabooStreamRequestHandler(socketserver.StreamRequestHandler): """ Handles an analysis request. """ # catch wavering clients early on logger.debug('New connection incoming.') - if not self.talk_back('Hallo das ist Peekaboo\n'): + if not self.talk_back([_('Hello, this is Peekaboo.'), '']): return submitted = self.parse() @@ -173,33 +173,33 @@ class PeekabooStreamRequestHandler(socketserver.StreamRequestHandler): try: parts = json.loads(request) except ValueError as error: - self.talk_back('FEHLER: Ungueltiges JSON.') + self.talk_back(_('Error: Invalid JSON in request.')) logger.error('Invalid JSON in request: %s', error) return None if not isinstance(parts, (list, tuple)): - self.talk_back('FEHLER: Ungueltiges Datenformat.') + self.talk_back(_('ERROR: Invalid data structure.')) logger.error('Invalid data structure.') return None submitted = [] for part in parts: if not part.has_key('full_name'): - self.talk_back('FEHLER: Unvollstaendige Datenstruktur.') + self.talk_back(_('ERROR: Incomplete data structure.')) logger.error('Incomplete data structure.') return None path = part['full_name'] logger.info("Got run_analysis request for %s", path) if not os.path.exists(path): - self.talk_back('FEHLER: Pfad existiert nicht oder ' - 'Zugriff verweigert.') + self.talk_back(_('ERROR: Path does not exist or no ' + 'permission to access it.')) logger.error('Path does not exist or no permission ' 'to access it.') return None if not os.path.isfile(path): - self.talk_back('FEHLER: Eingabe ist keine Datei.') + self.talk_back(_('ERRROR: Input is not a file')) logger.error('Input is not a file') return None @@ -234,7 +234,7 @@ class PeekabooStreamRequestHandler(socketserver.StreamRequestHandler): if not self.status_change.wait(self.status_change_timeout): # keep our client engaged # TODO: Impose maximum processing time of our own? - if not self.talk_back('Dateien werden analysiert...'): + if not self.talk_back(_('Files are being analyzed...')): # Abort handling this request since no-one's interested # any more. We could dequeue the samples here to avoid # unnecessary work. Instead we'll have them run their @@ -257,7 +257,7 @@ class PeekabooStreamRequestHandler(socketserver.StreamRequestHandler): # see if our server is shutting down and follow it if so if self.server.shutting_down: - self.talk_back('Peekaboo wird beendet.') + self.talk_back(_('Peekaboo is shutting down.')) logger.debug('Request shutting down with server.') self.server.deregister_request(self) return False @@ -302,14 +302,27 @@ class PeekabooStreamRequestHandler(socketserver.StreamRequestHandler): if sample_result >= result: result = sample_result - # and unconditionally send out its report to the client - if not self.talk_back(sample.peekaboo_report): + # and unconditionally send out its report to the client (plus an + # empty line) + if not self.talk_back(sample.peekaboo_report + ['']): return - # report overall result + # report overall result. logger.debug('Reporting batch as "%s" to client', result.name) - if not self.talk_back('Die Datensammlung wurde als "%s" eingestuft\n' - % result.name): + loc_verdict = _('The file collection has been categorized "%s"') + overall = [loc_verdict % result.name] + + # Add untranslated verdict (if the above actually got translated) for + # potential pattern matching of the client to reliably latch on to. + # Need to duplicate strings here for pygettext and pybabel extract to + # find the translatable one in the above _(). + verdict = 'The file collection has been categorized "%s"' + if verdict != loc_verdict: + overall.append(verdict % result.name) + + # append newline and send + overall.append('') + if not self.talk_back(overall): return # shut down connection |