Repository: freedomofpress/securedrop

Issue:
store.log is broken due to the file extension checks in verify
If a Duplicate ID is ever generated, store.log creates a file named "NOTES" in the store/ directory with the error message. The filename fails the extension checks in verify and is not monitored by OSSEC.  

```
[Tue Dec 03 22:21:49 2013] [error] ERROR:journalist:Exception on / [GET]
[Tue Dec 03 22:21:49 2013] [error] Traceback (most recent call last):
[Tue Dec 03 22:21:49 2013] [error]   File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
[Tue Dec 03 22:21:49 2013] [error]     response = self.full_dispatch_request()
[Tue Dec 03 22:21:49 2013] [error]   File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
[Tue Dec 03 22:21:49 2013] [error]     rv = self.handle_user_exception(e)
[Tue Dec 03 22:21:49 2013] [error]   File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
[Tue Dec 03 22:21:49 2013] [error]     reraise(exc_type, exc_value, tb)
[Tue Dec 03 22:21:49 2013] [error]   File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
[Tue Dec 03 22:21:49 2013] [error]     rv = self.dispatch_request()
[Tue Dec 03 22:21:49 2013] [error]   File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
[Tue Dec 03 22:21:49 2013] [error]     return self.view_functions[rule.endpoint](**req.view_args)
[Tue Dec 03 22:21:49 2013] [error]   File "/var/www/securedrop/journalist.py", line 72, in index
[Tue Dec 03 22:21:49 2013] [error]     date=str(datetime.fromtimestamp(os.stat(store.path(d)).st_mtime)
[Tue Dec 03 22:21:49 2013] [error]   File "/var/www/securedrop/store.py", line 58, in path
[Tue Dec 03 22:21:49 2013] [error]     verify(absolute)
[Tue Dec 03 22:21:49 2013] [error]   File "/var/www/securedrop/store.py", line 49, in verify
[Tue Dec 03 22:21:49 2013] [error]     raise PathException("Invalid file extension %s" % (ext, ))
[Tue Dec 03 22:21:49 2013] [error] PathException: Invalid file extension 
```



Discussion:
@dolanjs FYI, you make text appear as code/pre by surrounding it with "```" ([source](https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks))

Two comments:
1. `verify` is specifically for verifying paths that may contain user input to prevent traversals. It is unnecessary to use it on paths hard-coded into the application, as is the case in `store.log`.
2. We should use a proper logger, like Python's logging module. Appending to some random file is not threadsafe. And we should have a shared, reusable way to log, with a shared place to collect output (which can be monitored by OSSEC).


Resolving patch:
diff --git a/securedrop/source.py b/securedrop/source.py
--- a/securedrop/source.py
+++ b/securedrop/source.py
@@ -4,6 +4,11 @@
 import uuid
 from functools import wraps
 
+import logging
+# This module's logger is explicitly labeled so the correct logger is used,
+# even when this is run from the command line (e.g. during development)
+log = logging.getLogger('source')
+
 from flask import (Flask, request, render_template, session, redirect, url_for,
                    flash, abort, g, send_file)
 from flask_wtf.csrf import CsrfProtect
@@ -116,7 +121,7 @@ def create():
     sid = crypto_util.shash(session['codename'])
     if os.path.exists(store.path(sid)):
         # if this happens, we're not using very secure crypto
-        store.log("Got a duplicate ID '%s'" % sid)
+        log.warning("Got a duplicate ID '%s'" % sid)
     else:
         os.mkdir(store.path(sid))
     session['logged_in'] = True
diff --git a/securedrop/store.py b/securedrop/store.py
--- a/securedrop/store.py
+++ b/securedrop/store.py
@@ -8,6 +8,9 @@
 import tempfile
 from cStringIO import StringIO
 
+import logging
+log = logging.getLogger(__name__)
+
 from werkzeug import secure_filename
 
 VALIDATE_FILENAME = re.compile(
@@ -84,6 +87,3 @@ def save_file_submission(sid, filename, stream):
 def save_message_submission(sid, message):
     msg_loc = path(sid, '%s_msg.gpg' % uuid.uuid4())
     crypto_util.encrypt(config.JOURNALIST_KEY, message, msg_loc)
-
-def log(msg):
-    file(path('NOTES'), 'a').write(msg)


Test patch: