Repository: freedomofpress/securedrop

Issue:
API repsonse for a `Reply` has full system path for `filename`
## Description

The filename of a reply to a source should only be the `$(basename $path)` and not just `$path`.

## Steps to Reproduce

Boot the dev env, reply via the API, fetch all replies from the API.

## Expected Behavior

The response does not contain `/var/lib/securedrop/store/$filesystem_id`.

## Actual Behavior

```json
$ http localhost:8081/api/v1/replies
HTTP/1.0 200 OK
Content-Length: 4438
Content-Type: application/json
Date: Fri, 02 Nov 2018 13:58:11 GMT
Server: Werkzeug/0.14.1 Python/2.7.6
Set-Cookie: js=eyJleHBpcmVzIjp7IiBkIjoiRnJpLCAwMiBOb3YgMjAxOCAxNTo1ODoxMSBHTVQifX0.Dr3pcw.p_VpbLQcvnnVzg_jf6MTp9gZ8VI; HttpOnly; Path=/
Vary: Cookie

{
    "replies": [
        ...
        {
            "filename": "/var/lib/securedrop/store/V7ND6HFB3ZDSWS647VA2HPTTTOKD6M433M6ZSZOKTQTQAU7RWTWS5YU62F4WIASHUYTDAYW3HZVNORQAN3QKZVBZRELYCV3NUVARL6Y=/5-fourteen_recitative-reply.gpg", 
            "is_deleted_by_source": false, 
            "journalist_username": "journalist", 
            "journalist_uuid": "3328e4e8-4037-437b-88d9-a0aa270090cb", 
            "reply_url": "/api/v1/sources/c81a50ea-4eeb-4422-ba51-9bc11e598b14/replies/8b97e43d-e84d-412b-b7e2-1fccfa0fda17", 
            "size": 906, 
            "source_url": "/api/v1/sources/c81a50ea-4eeb-4422-ba51-9bc11e598b14", 
            "uuid": "8b97e43d-e84d-412b-b7e2-1fccfa0fda17"
        }, 
        ...
    ]
}
```


Discussion:

Resolving patch:
diff --git a/securedrop/journalist_app/api.py b/securedrop/journalist_app/api.py
--- a/securedrop/journalist_app/api.py
+++ b/securedrop/journalist_app/api.py
@@ -1,9 +1,10 @@
-from datetime import datetime, timedelta
-from functools import wraps
 import json
-from werkzeug.exceptions import default_exceptions  # type: ignore
 
+from datetime import datetime, timedelta
 from flask import abort, Blueprint, current_app, jsonify, request
+from functools import wraps
+from os import path
+from werkzeug.exceptions import default_exceptions  # type: ignore
 
 from db import db
 from journalist_app import utils
@@ -238,9 +239,10 @@ def all_source_replies(source_uuid):
                 return jsonify(
                     {'message': 'You must encrypt replies client side'}), 400
 
-            reply = Reply(user, source,
-                          current_app.storage.path(source.filesystem_id,
-                                                   filename))
+            # issue #3918
+            filename = path.basename(filename)
+
+            reply = Reply(user, source, filename)
             db.session.add(reply)
             db.session.add(source)
             db.session.commit()


Test patch:
diff --git a/securedrop/tests/test_journalist_api.py b/securedrop/tests/test_journalist_api.py
--- a/securedrop/tests/test_journalist_api.py
+++ b/securedrop/tests/test_journalist_api.py
@@ -648,6 +648,9 @@ def test_authorized_user_can_add_reply(journalist_app, journalist_api_token,
         assert reply.journalist_id == test_journo['id']
         assert reply.source_id == source_id
 
+        # regression test for #3918
+        assert '/' not in reply.filename
+
         source = Source.query.get(source_id)
 
         expected_filename = '{}-{}-reply.gpg'.format(