From afd3a3be606c43b056c69a9008b6d11a599aed1f Mon Sep 17 00:00:00 2001
From: David Beniamine <david.beniamine@imag.fr>
Date: Thu, 23 Feb 2017 14:39:43 +0100
Subject: [PATCH] Comments + markdown to unify text / html

---
 Readme.md           |  5 +++-
 src/console.py      |  4 +--
 src/index.py        | 31 +++++++------------
 src/test_pass.py    | 72 ++++++++++++++++++++++++++++++++-------------
 src/translations.py |  7 ++---
 5 files changed, 69 insertions(+), 50 deletions(-)

diff --git a/Readme.md b/Readme.md
index 2d33c28..f61f846 100644
--- a/Readme.md
+++ b/Readme.md
@@ -18,7 +18,8 @@
 
 ## Todo
 
-+ [ ] Traduction
++ [X] Commentaires
++ [X] Traduction
 + [ ] Nom
 + [ ] Readme
 + [X] Licence AGPL
@@ -29,6 +30,8 @@
     + [X] Noms
 + [ ] Packages
 + [ ] Installeur
++ [ ] Service systemd
++ [ ] Tests
 
 ## Licences
 
diff --git a/src/console.py b/src/console.py
index b8dc59c..223f74e 100644
--- a/src/console.py
+++ b/src/console.py
@@ -30,6 +30,4 @@ while True:
         break
     user_inputs.append(word)
 
-lines = test_pass(passwd, user_inputs)
-for line in lines:
-    print(line)
+print(test_pass(passwd, user_inputs))
diff --git a/src/index.py b/src/index.py
index 029e8ac..5fda04d 100755
--- a/src/index.py
+++ b/src/index.py
@@ -18,6 +18,7 @@
 
 import cgi
 import re
+from markdown import markdown
 from test_pass import test_pass
 
 form = cgi.FieldStorage()
@@ -44,7 +45,9 @@ Tetras Pass
 if form.getvalue("password") is None:
     # Password not defined => show the form
     passwordPrompt = "Veuillez saisir le mot de passe à tester ci dessous :"
-    inputsPrompt = "Veuillez saisir une liste d'indices : mots ou de dates au format jj/mm/yyyy, un indice par ligne:"
+    inputsPrompt = """Veuillez entrer des indices pour aider à trouver le mot de passe.<br />
+        Les indicides peuvent être des mots ou des dates au format jj/mm/yyyy.<br/>
+        Entrez un indice par ligne:"""
     sendPrompt = "Lancer le test"
 
     html += """
@@ -70,31 +73,14 @@ else:
     else:
         inputs = None
     # Retrieve results
-    lines = test_pass(form.getvalue("password"), inputs)
-    # Process results for the web
-    html += "<h2> {} </h2>".format(lines.pop(0))
-    inlist = False
-    for line in lines:
-        line = re.sub('\*\*(.*)\*\*', '<em>\g<1></em>', line)
-        if line[0] == "\t":
-            if inlist:
-                html += "<li>{}</li>".format(line)
-            else:
-                inlist = True
-                html += "<ul><li>{}</li>".format(line)
-        elif line[0] != "\n":
-            if inlist:
-                html += "</ul>"
-            inlist = False
-            html += "<h3>{}</h3>".format(line)
+    html += markdown(test_pass(form.getvalue("password"), inputs))
 
 html += """
-</div>
-</div>
 </body>
 <foot>
+<div id="foot">
 <p>
-<a href="https://gitlab.tetras-libre.fr/tetras-libre/tetras-pass">Tetra Pass</a> est Logiciel développé par
+<a href="https://gitlab.tetras-libre.fr/tetras-libre/tetras-pass">Tetras Pass</a> est Logiciel développé par
 <a href="http://tetras-libre.fr">Tetras Libre</a>,
 distribué sous Licence <a href="https://www.gnu.org/licenses/agpl.html">AGPL</a> V3.0.
 </p>
@@ -107,7 +93,10 @@ Le testeur de mot de passe est basé sur la bibliothèque
 Les listes de mots et noms français proviennent de <a href="http://www.lexique.org/">lexique.org</a>
 et sont distribués sous licence <a href="https://www.gnu.org/licenses/gpl-3.0.txt">GPL</a>.
 </p>
+</div>
 </foot>
+</div>
+</div>
 </html>
 """
 
diff --git a/src/test_pass.py b/src/test_pass.py
index 2ffdcb9..c951158 100644
--- a/src/test_pass.py
+++ b/src/test_pass.py
@@ -23,6 +23,20 @@ from zxcvbn import zxcvbn
 from zxcvbn.matching import add_frequency_lists
 from translations import tr
 
+comments = {
+    'offline_fast_hashing_1e10_per_second':
+    "L'attaquant.e a accès à une base de donnée d'un site sur lequel vous êtes inscrit et cette base de donnée "
+    "est mal protégée",
+    'offline_slow_hashing_1e4_per_second':
+    "L'attaquant.e a accès à une base de donnée d'un site sur lequel vous êtes inscrit et cette base de donnée est "
+    "bien protégée",
+    'online_no_throttling_10_per_second':
+    "L'attaquant.e essaye de forcer votre mot de passe en ligne, aucune protection anti brute force n'est activée",
+    'online_throttling_100_per_hour':
+    "L'attaquant.e essaye de forcer votre mot de passe en ligne, une protection anti brute force n'est activée",
+}
+
+
 def add_dictionnaries():
     dicts = {}
     directory = os.path.dirname(os.path.realpath(__file__))+'/../data'
@@ -38,7 +52,7 @@ def add_dictionnaries():
 # Process user inputs:
 #   Generate all possibile combinations of 2 parts of dates
 def process_input(inputs):
-    try :
+    try:
         dates = []
         words = []
         for i in inputs:
@@ -55,56 +69,72 @@ def process_input(inputs):
         return None
 
 
+# Print results formated in markdown
 def test_pass(password, inputs):
 
     # Read input
     add_dictionnaries()
     results = zxcvbn(password, process_input(inputs))
-    lines = []
+    lines = ""
 
-    lines.append("Résultats du test\n")
-    lines.append("Nombre de tentatives : {}".format(results["guesses"]))
+    lines += "## Résultats du test\n\n"
+    lines += "\n\n"
+
+    valeur = ["Très faible", "Faible", "Tolérable", "Correct", "Bon"]
+    lines += "\n### Qualité du mot de passe \n\n"
+    lines += "+\tNombre de tentatives pour trouver votre mot de passe : **{}**\n".format(results["guesses"])
+    lines += "+\tScore : **{}/4**\n".format(results["score"])
+    lines += "+\tJugement : **{}**\n".format(valeur[results["score"]])
 
-    lines.append("Temps requis pour craquer votre mot de passe")
-    for key, value in results["crack_times_display"].items():
-        lines.append("\t "+tr(key).capitalize()+" :\t **"+tr(value)+"**")
 
-    lines.append("\n")
-    lines.append("Methode de hack utilisées")
+    lines += "### Temps requis pour craquer votre mot de passe selon le type d'attaque\n"
+    lines += "\n\n"
+    for key, value in sorted(results["crack_times_display"].items()):
+        lines += "+\t"+tr(key).capitalize()+" :\t **"+tr(value)+"**\n\n"
+        lines += "\t*Expliquation :* "+comments[key]+", l'attaquant.e vise votre utilisateur en particulier.\n\n"
+
+    lines += "\n\n"
+    lines += "### Methode de hack utilisées\n"
+    lines += "\n\n"
+    lines += "Voici la liste des techniques utilisées pour trouver votre mot de passe : \n\n"
+    prefix = "Votre mot de pass ou une partie de votre mot de passe a été trouvé"
     # Print hack methods
     for seq in results["sequence"]:
         if seq["pattern"] == "bruteforce":
-            lines.append("\tMot de passe trouvé par force brute")
+            lines += "+\tMot de passe trouvé par force brute\n\n"
+            lines += "\t{} en testant différentes combinaisons\n\n".format(prefix)
         elif seq["pattern"] == "repeat":
             for match in seq["base_matches"]:
-                line = "\tMot de passe trouvé par répétitions depuis"
+                line = "+\tMot de passe trouvé par répétitions depuis"
                 try:
                     line += "le dictionnaire '{}'".format(tr(match["dictionary_name"]))
                 except:
                     line += "le motif '{}'".format(tr(match["token"]))
                 finally:
-                    lines.append(line)
+                    lines += line+"\n"
+            lines += "\t{} en répétant un motif ou mot connu\n\n".format(prefix)
         elif seq["pattern"] == "date":
-            lines.append("\tMot de passe trouvé par essai de dates")
+            lines += "+\tMot de passe trouvé par essai de dates\n\n"
+            lines += "\t{} est une date, récente facile à deviner\n\n".format(prefix)
         elif seq["pattern"] == "sequence":
-            lines.append("\tMot proche trouvé dans la séquence : '{}'".format(tr(seq["sequence_name"])))
+            lines += "+\tMot proche trouvé dans la séquence : '{}'\n\n".format(tr(seq["sequence_name"]))
+            lines += "\t{} est une séquence de caractères facile à deviner\n\n".format(prefix)
         else:
-            lines.append("\tMot proche trouvé dans le dictionnaire : '{}'".format(tr(seq["dictionary_name"])))
+            lines += "+\tMot proche trouvé dans le dictionnaire : '{}'\n\n".format(tr(seq["dictionary_name"]))
+            lines += "\t{} est dans un dictionnaire de mots connus\n\n".format(prefix)
 
-    lines.append("\n")
 
+    lines += "\n### Problemes et conseils\n"
     for key, values in results["feedback"].items():
         if values != [] and values != "":
-            lines.append("{} :".format(tr(key).capitalize()))
+            lines += "\n\n"
+            lines += "#### {}\n\n".format(tr(key).capitalize())
         else:
             continue
         if not isinstance(values, list):
             # Simplify things by always using lists
             values = [values]
         for val in values:
-            lines.append("\t{}".format(tr(val)))
-
-    valeur = ["Très faible", "Faible", "Tolérable", "Correct", "Bon"]
-    lines.append("Score Global {}/4 : {}".format(results["score"], valeur[results["score"]]))
+            lines += "+\t{}\n".format(tr(val))
 
     return lines
diff --git a/src/translations.py b/src/translations.py
index 1dac12b..e9b77f3 100644
--- a/src/translations.py
+++ b/src/translations.py
@@ -27,7 +27,7 @@ translations = {
     'online_throttling_100_per_hour': 'Essai en ligne avec protection anti brute force',
     'passwords': 'mots de passes',
     'surnames': 'nom de famille',
-    'user_input': "indices données par l'attaquant.e",
+    'user_inputs': "indices données par l'attaquant.e",
     'suggestions': 'conseils',
     'warning': 'problèmes',
     'less than a second': "moins d'une seconde",
@@ -77,15 +77,14 @@ substitutions = {
     'All-uppercase is almost as easy to guess as all-lowercase':
         'Les mots en majuscules sont aussi simple à deviner que ceux en minuscules',
     "Reversed words aren't much harder to guess": 'Les mots inversés ne sont pas beaucoup plus dur à deviner',
-
 }
 
 
 def tr(text):
     try:
-        return translations[text]
+        text = translations[text]
     except:
-        # Todo use regex
         for pattern, sub in substitutions.items():
             text = re.sub(pattern, sub, text)
+    finally:
         return text
-- 
GitLab