Release cookie-free Persimmons vault link 0.6.0

This commit is contained in:
matcha committed 2026-10-04 08:05:18 +00:00
1 parent 53c4bb50f4
commit 1aa3cffaf2
32 files changed
+1535 -624

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import AsyncMock, patch
import httpx
from starlette.applications import Starlette
from backend.shared import human_vault as vault
class CapabilityLinkTests(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self):
self.temp = tempfile.TemporaryDirectory()
self.path = Path(self.temp.name) / 'vault.sqlite'
self.env = patch.dict(os.environ, PERSIMMONS_HUMAN_VAULT_DB=str(self.path))
self.env.start()
self.actor = AsyncMock(return_value='fixture')
app = Starlette(routes=vault.routes(self.actor))
self.client = httpx.AsyncClient(
transport=httpx.ASGITransport(app=app), base_url='https://teahub.xyz',
headers={'Cookie': 'session=fixture'},
)
async def asyncTearDown(self):
await self.client.aclose()
self.env.stop()
self.temp.cleanup()
async def test_signed_in_page_mints_owner_capability_used_without_cookie(self):
response = await self.client.post(
'/chat/persimmons/human-vault/link',
headers={'Origin': 'https://persimmons.app', 'X-Requested-With': 'Persimmons'},
)
self.assertEqual(response.status_code, 200)
linked = response.json()
self.assertEqual(linked['owner'], 'fixture')
self.assertRegex(linked['capability'], vault.CAPABILITY)
self.assertNotIn(linked['capability'].encode(), self.path.read_bytes())
self.actor.reset_mock()
result = await self.client.post(
vault.PREFIX + '/read',
headers={
'Origin': vault.EXTENSION_ORIGIN,
'Cookie': '',
'X-Vault-Owner': 'fixture',
'X-Vault-Capability': linked['capability'],
},
)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), {'owner': 'fixture', 'items': []})
self.actor.assert_not_awaited()
async def test_link_is_exact_origin_cookie_and_request_header_gated(self):
endpoint = '/chat/persimmons/human-vault/link'
for headers in (
{'Origin': 'https://evil.example', 'X-Requested-With': 'Persimmons'},
{'Origin': 'https://persimmons.app'},
{'Origin': 'https://persimmons.app', 'X-Requested-With': 'Persimmons', 'Cookie': ''},
):
self.assertIn((await self.client.post(endpoint, headers=headers)).status_code, (401, 403))
if __name__ == '__main__':
unittest.main()
+3 -3
View File
@@ -7,7 +7,7 @@ import unittest
import zipfile
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('updater', ROOT / 'persimmons/web/update-extension.py')
spec = importlib.util.spec_from_file_location('updater', ROOT / 'backend/web/update-extension.py')
updater = importlib.util.module_from_spec(spec)
spec.loader.exec_module(updater)
@@ -16,10 +16,10 @@ class UpdateTests(unittest.TestCase):
def test_same_folder_preserves_identity_and_unrelated_files(self):
with tempfile.TemporaryDirectory() as name:
folder = Path(name)
manifest = (ROOT / 'persimmons-extension/manifest.json').read_bytes()
manifest = (ROOT / 'extension/manifest.json').read_bytes()
(folder / 'manifest.json').write_bytes(manifest)
(folder / 'keep.txt').write_text('unrelated')
raw = (ROOT / 'persimmons/web/extension.zip').read_bytes()
raw = (ROOT / 'assets/extension.zip').read_bytes()
self.assertEqual(updater.update(folder, raw), json.loads(manifest)['version'])
self.assertEqual((folder / 'keep.txt').read_text(), 'unrelated')
self.assertEqual((folder / 'manifest.json').read_bytes(), manifest)
+8 -6
View File
@@ -12,14 +12,14 @@ ROOT = Path(__file__).resolve().parents[1]
class UxTests(unittest.TestCase):
def test_packaged_icons_and_pinned_identity(self):
import base64
with zipfile.ZipFile(ROOT / 'persimmons/web/extension.zip') as archive:
with zipfile.ZipFile(ROOT / 'assets/extension.zip') as archive:
manifest = json.loads(archive.read('persimmons-extension/manifest.json'))
digest = hashlib.sha256(base64.b64decode(manifest['key'])).hexdigest()[:32]
self.assertEqual(''.join(chr(ord('a') + int(c, 16)) for c in digest),
'kiolgjoohhgdohcafgelhikpgkbmckbg')
self.assertEqual(
manifest['version'],
json.loads((ROOT / 'persimmons-extension/manifest.json').read_text())['version'],
json.loads((ROOT / 'extension/manifest.json').read_text())['version'],
)
for size in (16, 32, 48, 128):
name = manifest['icons'][str(size)]
@@ -27,10 +27,12 @@ class UxTests(unittest.TestCase):
raw = archive.read('persimmons-extension/' + name)
self.assertEqual(raw[:8], b'\x89PNG\r\n\x1a\n')
self.assertEqual(struct.unpack('>II', raw[16:24]), (size, size))
self.assertEqual(raw, (ROOT / 'persimmons-extension' / name).read_bytes())
self.assertEqual(raw, (ROOT / 'extension' / name).read_bytes())
self.assertFalse(any('/tests/' in name for name in archive.namelist()))
self.assertEqual(manifest['externally_connectable'], {'matches': ['https://persimmons.app/*']})
self.assertNotIn('content_scripts', manifest)
self.assertNotIn('optional_host_permissions', manifest)
self.assertEqual(manifest['host_permissions'], ['https://teahub.xyz/*'])
self.assertEqual(manifest['permissions'], ['activeTab', 'scripting', 'storage'])
popup = archive.read('persimmons-extension/popup.html').decode()
self.assertIn('Save autofilled login', popup)
@@ -42,14 +44,14 @@ class UxTests(unittest.TestCase):
self.assertNotIn('juice-panel', popup)
def test_footer_css_and_hero_copy(self):
for name in ('tea/web/🍊/index.html', 'persimmons/web/index.html'):
for name in ('index.html',):
html = (ROOT / name).read_text()
self.assertIn('🍊 → 💧 → 🤖', html)
self.assertIn('>put plainly</span>', html)
self.assertNotIn('Persimmons are', html)
self.assertNotIn('without paperwork', html)
self.assertNotIn('<div class="footerNote">', html)
hosted = (ROOT / 'persimmons/web/index.html').read_text()
hosted = (ROOT / 'index.html').read_text()
self.assertIn('Master vault', hosted)
self.assertIn('id="namedVaults"', hosted)
self.assertIn('+ New vault', hosted)
@@ -70,7 +72,7 @@ class UxTests(unittest.TestCase):
if 'id' in attrs:
self.nodes[attrs['id']] = attrs
page = Parser()
page.feed((ROOT / 'persimmons/web/signin.html').read_text())
page.feed((ROOT / 'backend/web/signin.html').read_text())
for name, value in [('signin', 'on'), ('name', 'username'), ('password', 'current-password')]:
self.assertEqual(page.nodes[name]['autocomplete'], value)
self.assertEqual(page.nodes['password']['type'], 'password')