package io.flamenet.e2e import org.json.JSONObject import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue import kotlin.test.assertFalse /** * Documents sent between the two apps. * * The envelope in [IOS_FILE] was produced by the iOS build, not by this * engine. Two implementations of one format that each only test against * themselves agree right up to the day somebody sends a real file, and the * symptom is a message that arrives as "[unsupported attachment]" with no clue * why. This is the test that catches a drift in the field names before then. */ class FileEnvelopeInteropTest { // Standard base64 with padding, which is what Bytes.unb64 decodes. private val key = "ERERERERERERERERERERERERERERERERERERERERERE=" private val digest = "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=" /** As iOS assembles it: `fn`, `cls`, `cmp` as additive top-level keys. */ private val iosFile = """ {"fnc":1,"kind":"file","caption":"📎 Lease agreement.pdf", "att":{"id":"${"a".repeat(64)}","key":"$key","digest":"$digest", "bytes":4096,"mime":"application/pdf","w":0,"h":0}, "fn":"Lease agreement.pdf","cls":2,"cmp":[0,1]} """.trimIndent().replace("\n", "") @Test fun `a file envelope from iOS parses with its name and label`() { val content = Content.parse(iosFile) val ref = assertNotNull(content.attachment, "no attachment parsed") assertEquals(Attachment.FILE, ref.kind) assertEquals("Lease agreement.pdf", ref.filename) assertEquals(2, ref.classification) assertEquals(listOf(0, 1), ref.compartments) assertEquals(4096, ref.bytes) assertEquals("application/pdf", ref.mime) } /** It must be recognised, not merely tolerated. */ @Test fun `a file is a known kind rather than needing a newer client`() { val content = Content.parse(iosFile) assertFalse(content.needsNewerClient, "file is still treated as an unknown kind") assertFalse(content.isControl, "a document is not control traffic") } @Test fun `this engine assembles the same shape it parses`() { val ref = Attachment.Ref( kind = Attachment.FILE, id = "b".repeat(64), key = ByteArray(32) { 0x11 }, digest = ByteArray(32) { 0x22 }, bytes = 2048, mime = "application/pdf", filename = "Settlement draft.docx", classification = 3, compartments = listOf(2), ) val json = Content.attachment(ref) val parsed = JSONObject(json) assertEquals("file", parsed.getString("kind")) assertEquals("Settlement draft.docx", parsed.getString("fn")) assertEquals(3, parsed.getInt("cls")) assertEquals(1, parsed.getJSONArray("cmp").length()) val back = assertNotNull(Content.parse(json).attachment) assertEquals("Settlement draft.docx", back.filename) assertEquals(3, back.classification) assertEquals(listOf(2), back.compartments) } /** * The compatibility rule, on this side too: never captionless, because * a build that predates this kind shows only the caption. */ @Test fun `a file envelope always carries a caption naming the document`() { val ref = Attachment.Ref( kind = Attachment.FILE, id = "c".repeat(64), key = ByteArray(32) { 0x11 }, digest = ByteArray(32) { 0x22 }, bytes = 10, mime = "application/pdf", filename = "notes.txt", ) val json = Content.attachment(ref) assertTrue(json.contains("\"caption\""), "a file envelope went out captionless") assertEquals("📎 notes.txt", Content.parse(json).text) } @Test fun `a caption the sender wrote is not replaced`() { val ref = Attachment.Ref( kind = Attachment.FILE, id = "d".repeat(64), key = ByteArray(32) { 0x11 }, digest = ByteArray(32) { 0x22 }, bytes = 10, mime = "application/pdf", filename = "notes.txt", ) assertEquals("signed copy", Content.parse(Content.attachment(ref, "signed copy")).text) } /** An older peer sends no `fn`/`cls`/`cmp`. It must parse, not throw. */ @Test fun `an envelope without the new fields still parses`() { val older = """ {"fnc":1,"kind":"file","caption":"📎 old.pdf", "att":{"id":"${"e".repeat(64)}","key":"$key","digest":"$digest", "bytes":10,"mime":"application/pdf","w":0,"h":0}} """.trimIndent().replace("\n", "") val ref = assertNotNull(Content.parse(older).attachment) assertEquals("", ref.filename) assertEquals(0, ref.classification) assertEquals(emptyList(), ref.compartments) } /** * The additive property that makes any of this safe: a photo envelope * from this engine still carries none of the file keys, so a peer that has * never heard of them reads it exactly as before. */ @Test fun `a photo envelope carries none of the file keys`() { val photo = Attachment.Ref( kind = Attachment.IMAGE, id = "f".repeat(64), key = ByteArray(32) { 0x11 }, digest = ByteArray(32) { 0x22 }, bytes = 1000, mime = "image/jpeg", width = 100, height = 200, ) val json = Content.attachment(photo, "hi") assertFalse(json.contains("\"fn\""), "a photo envelope carried file fields") assertFalse(json.contains("\"cls\"")) assertFalse(json.contains("\"cmp\"")) } /** * A filename from a peer is display text and a name, never a path. * Carried verbatim, and nothing here should ever join it onto a directory. */ @Test fun `a hostile filename survives as plain text`() { val hostile = """ {"fnc":1,"kind":"file","caption":"x", "att":{"id":"${"a".repeat(64)}","key":"$key","digest":"$digest", "bytes":1,"mime":"application/pdf","w":0,"h":0}, "fn":"../../../etc/passwd","cls":0,"cmp":[]} """.trimIndent().replace("\n", "") assertEquals("../../../etc/passwd", assertNotNull(Content.parse(hostile).attachment).filename) } @Test fun `a file in a group keeps its routing`() { val ref = Attachment.Ref( kind = Attachment.FILE, id = "a".repeat(64), key = ByteArray(32) { 0x11 }, digest = ByteArray(32) { 0x22 }, bytes = 1, mime = "application/pdf", filename = "g.pdf", ) val content = Content.parse(Content.attachment(ref, groupId = "g-123")) assertEquals("g-123", content.groupId) assertEquals(Attachment.FILE, content.attachment?.kind) } }