Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.7.0] Backport "Fix source_app.utils.normalize_timestamps" #5727

Merged
merged 1 commit into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions securedrop/source_app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ def async_genkey(crypto_util_: CryptoUtil,

def normalize_timestamps(filesystem_id: str) -> None:
"""
Update the timestamps on all of the source's submissions to match that of
the latest submission. This minimizes metadata that could be useful to
investigators. See #301.
Update the timestamps on all of the source's submissions. This
minimizes metadata that could be useful to investigators. See
#301.
"""
sub_paths = [current_app.storage.path(filesystem_id, submission.filename)
for submission in g.source.submissions]
if len(sub_paths) > 1:
args = ["touch"]
args.extend(sub_paths[:-1])
args = ["touch", "--no-create"]
args.extend(sub_paths)
rc = subprocess.call(args)
if rc != 0:
current_app.logger.warning(
Expand Down
69 changes: 68 additions & 1 deletion securedrop/tests/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import shutil

from io import BytesIO, StringIO
from pathlib import Path

from flask import session, escape, url_for, g, request
from mock import patch, ANY

Expand All @@ -21,7 +23,7 @@
from models import InstanceConfig, Source, Reply
from source_app import main as source_app_main
from source_app import api as source_app_api
from .utils.db_helper import new_codename
from .utils.db_helper import new_codename, submit
from .utils.instrument import InstrumentedApp
from sdconfig import config

Expand Down Expand Up @@ -652,6 +654,71 @@ def test_login_with_overly_long_codename(source_app):
"Called hash_codename for codename w/ invalid length"


def test_normalize_timestamps(source_app):
"""
Check function of source_app.utils.normalize_timestamps.

All submissions for a source should have the same timestamp. Any
existing submissions' files that did not exist at the time of a
new submission should not be created by normalize_timestamps.
"""
with source_app.test_client() as app:
# create a source
source, codename = utils.db_helper.init_source()

# create one submission
first_submission = submit(source, 1)[0]

# delete the submission's file from the store
first_submission_path = Path(
source_app.storage.path(source.filesystem_id, first_submission.filename)
)
first_submission_path.unlink()
assert not first_submission_path.exists()

# log in as the source
resp = app.post(url_for('main.login'),
data=dict(codename=codename),
follow_redirects=True)
assert resp.status_code == 200
text = resp.data.decode('utf-8')
assert "Submit Files" in text
assert session['logged_in'] is True

# submit another message
resp = _dummy_submission(app)
assert resp.status_code == 200
text = resp.data.decode('utf-8')
assert "Thanks! We received your message" in text

# sleep to ensure timestamps would differ
time.sleep(1)

# submit another message
resp = _dummy_submission(app)
assert resp.status_code == 200
text = resp.data.decode('utf-8')
assert "Thanks! We received your message" in text

# only two of the source's three submissions should have files in the store
assert 3 == len(source.submissions)
submission_paths = [
Path(source_app.storage.path(source.filesystem_id, s.filename))
for s in source.submissions
]
extant_paths = [p for p in submission_paths if p.exists()]
assert 2 == len(extant_paths)

# verify that the deleted file has not been recreated
assert not first_submission_path.exists()
assert first_submission_path not in extant_paths

# and the timestamps of all existing files should match exactly
assert extant_paths[0].stat().st_atime_ns == extant_paths[1].stat().st_atime_ns
assert extant_paths[0].stat().st_ctime_ns == extant_paths[1].stat().st_ctime_ns
assert extant_paths[0].stat().st_mtime_ns == extant_paths[1].stat().st_mtime_ns


def test_failed_normalize_timestamps_logs_warning(source_app):
"""If a normalize timestamps event fails, the subprocess that calls
touch will fail and exit 1. When this happens, the submission should
Expand Down