CI / Python lint (flake8) (push) Has been cancelled
CI / Python syntax check (push) Has been cancelled
CI / Alembic migration chain (push) Has been cancelled
CI / JavaScript syntax check (push) Has been cancelled
CI / Pytest (push) Has been cancelled
CI / Build extension zip (push) Has been cancelled
88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
"""
|
|
Regression tests for share expiry failing open (review finding #10).
|
|
|
|
create_share parsed expires_days inside `try: ... except: pass`, so any value it
|
|
could not parse silently became "never expires" — the opposite of what the user
|
|
asked for, with no error to notice.
|
|
"""
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from tests.conftest import add_item, auth_headers, make_user
|
|
|
|
|
|
def _share(client, token, item_id, recipient='friend@example.com', **extra):
|
|
body = {
|
|
'item_id': item_id,
|
|
'recipient_email': recipient,
|
|
'enc_data': 'ECDH-CT',
|
|
'iv': 'ECDH-IV',
|
|
'item_name': 'password',
|
|
'item_type': 'password',
|
|
}
|
|
body.update(extra)
|
|
return client.post('/api/sharing', headers=auth_headers(token), json=body)
|
|
|
|
|
|
def test_valid_expiry_is_applied(client, app):
|
|
token, _ = make_user(client)
|
|
item_id = add_item(client, token)
|
|
|
|
res = _share(client, token, item_id, expires_days=7)
|
|
assert res.status_code == 201, res.get_json()
|
|
|
|
expires_at = res.get_json()['expires_at']
|
|
assert expires_at is not None
|
|
parsed = datetime.fromisoformat(expires_at)
|
|
expected = datetime.now(timezone.utc).replace(tzinfo=None) + timedelta(days=7)
|
|
assert abs((parsed - expected).total_seconds()) < 60
|
|
|
|
|
|
def test_null_expiry_means_never(client, app):
|
|
token, _ = make_user(client)
|
|
item_id = add_item(client, token)
|
|
|
|
res = _share(client, token, item_id, expires_days=None)
|
|
assert res.status_code == 201
|
|
assert res.get_json()['expires_at'] is None
|
|
|
|
|
|
def test_omitted_expiry_means_never(client, app):
|
|
token, _ = make_user(client)
|
|
item_id = add_item(client, token)
|
|
|
|
res = _share(client, token, item_id)
|
|
assert res.status_code == 201
|
|
assert res.get_json()['expires_at'] is None
|
|
|
|
|
|
def test_unparseable_expiry_is_rejected_not_silently_dropped(client, app):
|
|
"""The bug: 'seven' used to yield a share that never expires."""
|
|
token, _ = make_user(client)
|
|
|
|
for bad in ('seven', '7 days', {}, [], 'NaN', ''):
|
|
item_id = add_item(client, token)
|
|
res = _share(client, token, item_id, expires_days=bad)
|
|
assert res.status_code == 400, (
|
|
f'expires_days={bad!r} accepted; share would never expire'
|
|
)
|
|
assert 'expires_days' in res.get_json()['error']
|
|
|
|
|
|
def test_out_of_range_expiry_is_rejected(client, app):
|
|
token, _ = make_user(client)
|
|
|
|
for bad in (-1, -30, 4000):
|
|
item_id = add_item(client, token)
|
|
res = _share(client, token, item_id, expires_days=bad)
|
|
assert res.status_code == 400, f'expires_days={bad!r} accepted'
|
|
|
|
|
|
def test_zero_expiry_means_never(client, app):
|
|
"""0 is 'no expiry', consistent with null — not 'expires immediately'."""
|
|
token, _ = make_user(client)
|
|
item_id = add_item(client, token)
|
|
|
|
res = _share(client, token, item_id, expires_days=0)
|
|
assert res.status_code == 201
|
|
assert res.get_json()['expires_at'] is None
|