Secure send moves into Tech Tools

This commit is contained in:
2026-08-21 14:18:10 -04:00
parent 4242028270
commit a01f5c1a83
24 changed files with 139 additions and 264 deletions

View File

@@ -27,11 +27,39 @@ def test_meta_matches_manifest():
assert meta.core_version == manifest['core_version']
def test_no_backend_surface():
"""Client-side only: no blueprint to register, no tables to migrate."""
def test_the_only_backend_surface_is_secure_send():
"""Tech Tools used to have NO backend, and mostly still should.
This asserted `get_blueprint() is None` until secure send arrived: a link
that expires and dies on first read cannot be browser-only, because
something has to hold the ciphertext until it is claimed. The guard is kept
rather than deleted, narrowed to the one exception - a future tool that
quietly grows an endpoint or a table has to change this test, and therefore
make the decision on purpose rather than by accident.
"""
from flask import Flask
from plugins.tools.models import SecureSecret
plugin = ToolsPlugin()
assert plugin.get_blueprint() is None
assert plugin.get_models() == []
assert plugin.get_models() == [SecureSecret]
probe = Flask(__name__)
probe.register_blueprint(plugin.get_blueprint(), url_prefix='/api/tools')
served = sorted(
(str(rule), sorted(rule.methods - {'HEAD', 'OPTIONS'}))
for rule in probe.url_map.iter_rules()
if not str(rule).startswith('/static'))
# Methods are asserted, not just paths: REDEEMING MUST BE POST-ONLY. Chat
# and mail clients prefetch GET urls to build previews, and a preview that
# could redeem would consume a one-view secret before its recipient ever
# opened the link. The GET on the same path reports availability only.
assert served == [
('/api/tools/secrets', ['POST']),
('/api/tools/secrets/<token>', ['GET']),
('/api/tools/secrets/<token>', ['POST']),
], served
def test_navigation_item():

View File

@@ -7,8 +7,8 @@ fail in ways nobody notices until somebody needs the password.
import pytest
from plugins.securesend.models import SecureSecret
from plugins.securesend.api.routes import MAX_CIPHERTEXT
from plugins.tools.models import SecureSecret
from plugins.tools.api.routes import MAX_CIPHERTEXT
CIPHER = 'ZmFrZS1jaXBoZXJ0ZXh0LWZvci10ZXN0cw=='
IV = 'MTIzNDU2Nzg5MDEy'
@@ -17,7 +17,7 @@ IV = 'MTIzNDU2Nzg5MDEy'
def _create(client, headers, **overrides):
body = {'ciphertext': CIPHER, 'iv': IV}
body.update(overrides)
return client.post('/api/securesend', json=body, headers=headers)
return client.post('/api/tools/secrets', json=body, headers=headers)
# ------------------------------------------------------------------- creating
@@ -25,7 +25,7 @@ def _create(client, headers, **overrides):
def test_creating_requires_a_login(client):
"""Anonymous creation would be an untraceable way to move data out of a
system that otherwise records who did what."""
resp = client.post('/api/securesend', json={'ciphertext': CIPHER, 'iv': IV})
resp = client.post('/api/tools/secrets', json={'ciphertext': CIPHER, 'iv': IV})
assert resp.status_code in (401, 403, 422)
@@ -74,29 +74,29 @@ def test_anyone_can_read_it_without_logging_in(client, auth_headers):
"""The recipient usually has no account here. The unguessable token is the
authorisation."""
token = _create(client, auth_headers).get_json()['data']['token']
resp = client.post('/api/securesend/%s' % token)
resp = client.post('/api/tools/secrets/%s' % token)
assert resp.status_code == 200
assert resp.get_json()['data']['ciphertext'] == CIPHER
def test_the_second_read_finds_nothing(client, auth_headers):
token = _create(client, auth_headers).get_json()['data']['token']
assert client.post('/api/securesend/%s' % token).status_code == 200
assert client.post('/api/securesend/%s' % token).status_code == 404
assert client.post('/api/tools/secrets/%s' % token).status_code == 200
assert client.post('/api/tools/secrets/%s' % token).status_code == 404
def test_a_spent_secret_is_deleted_not_just_marked(client, db, auth_headers):
"""A secret that has been read has no reason to keep its ciphertext."""
token = _create(client, auth_headers).get_json()['data']['token']
client.post('/api/securesend/%s' % token)
client.post('/api/tools/secrets/%s' % token)
assert SecureSecret.query.filter_by(token=token).first() is None
def test_more_than_one_view_can_be_allowed(client, auth_headers):
token = _create(client, auth_headers, views=2).get_json()['data']['token']
assert client.post('/api/securesend/%s' % token).status_code == 200
assert client.post('/api/securesend/%s' % token).status_code == 200
assert client.post('/api/securesend/%s' % token).status_code == 404
assert client.post('/api/tools/secrets/%s' % token).status_code == 200
assert client.post('/api/tools/secrets/%s' % token).status_code == 200
assert client.post('/api/tools/secrets/%s' % token).status_code == 404
# ------------------------------------------------------- link previews (Teams)
@@ -111,19 +111,19 @@ def test_a_get_does_not_spend_the_secret(client, auth_headers):
"""
token = _create(client, auth_headers).get_json()['data']['token']
peek = client.get('/api/securesend/%s' % token)
peek = client.get('/api/tools/secrets/%s' % token)
assert peek.status_code == 200
assert peek.get_json()['data']['available'] is True
assert 'ciphertext' not in peek.get_json()['data']
# Still there for the person it was meant for.
assert client.post('/api/securesend/%s' % token).status_code == 200
assert client.post('/api/tools/secrets/%s' % token).status_code == 200
def test_peeking_at_a_spent_link_says_so_without_leaking(client, auth_headers):
token = _create(client, auth_headers).get_json()['data']['token']
client.post('/api/securesend/%s' % token)
peek = client.get('/api/securesend/%s' % token)
client.post('/api/tools/secrets/%s' % token)
peek = client.get('/api/tools/secrets/%s' % token)
assert peek.get_json()['data']['available'] is False
@@ -137,7 +137,7 @@ def test_an_expired_secret_is_gone(client, db, auth_headers):
- timedelta(minutes=1))
db.session.commit()
assert client.post('/api/securesend/%s' % token).status_code == 404
assert client.post('/api/tools/secrets/%s' % token).status_code == 404
assert SecureSecret.query.filter_by(token=token).first() is None
@@ -152,10 +152,10 @@ def test_an_unknown_token_answers_the_same_as_a_spent_one(client, auth_headers):
"""Distinguishing them would confirm to anyone holding a guessed token
whether it ever named anything."""
token = _create(client, auth_headers).get_json()['data']['token']
client.post('/api/securesend/%s' % token)
client.post('/api/tools/secrets/%s' % token)
spent = client.post('/api/securesend/%s' % token)
never = client.post('/api/securesend/%s' % ('f' * 32))
spent = client.post('/api/tools/secrets/%s' % token)
never = client.post('/api/tools/secrets/%s' % ('f' * 32))
assert spent.status_code == never.status_code == 404
assert (spent.get_json()['data']['error']['message']
== never.get_json()['data']['error']['message'])