UNPKG

@fedify/fedify

Version:

An ActivityPub server framework

179 lines (178 loc) • 6.45 kB
import "@js-temporal/polyfill"; import "urlpattern-polyfill"; globalThis.addEventListener = () => {}; import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs"; import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs"; import { t as normalizePublicAudience } from "../public-audience-DYFHzm_c.mjs"; import { test } from "@fedify/fixture"; import { Create, Note, PUBLIC_COLLECTION } from "@fedify/vocab"; import { getDocumentLoader, preloadedContexts } from "@fedify/vocab-runtime"; //#region src/compat/public-audience.test.ts const PUBLIC_URI = PUBLIC_COLLECTION.href; const AS_CONTEXT = "https://www.w3.org/ns/activitystreams"; test("normalizePublicAudience() rewrites every addressing field and both CURIE forms", async () => { const output = await normalizePublicAudience({ "@context": AS_CONTEXT, type: "Note", id: "https://example.com/notes/1", to: "as:Public", cc: ["as:Public", "https://example.com/bob"], bto: "Public", bcc: ["Public"], audience: ["as:Public", "Public"] }); assertEquals(output.to, PUBLIC_URI); assertEquals(output.cc, [PUBLIC_URI, "https://example.com/bob"]); assertEquals(output.bto, PUBLIC_URI); assertEquals(output.bcc, [PUBLIC_URI]); assertEquals(output.audience, [PUBLIC_URI, PUBLIC_URI]); }); test("normalizePublicAudience() normalises activities serialized by @fedify/vocab", async () => { const compact = await new Create({ id: new URL("https://example.com/activities/1"), actor: new URL("https://example.com/alice"), object: new Note({ id: new URL("https://example.com/notes/1"), content: "Hello, world!", tos: [PUBLIC_COLLECTION] }), tos: [PUBLIC_COLLECTION], ccs: [new URL("https://example.com/followers")] }).toJsonLd({ format: "compact" }); assertEquals(compact.to, "as:Public"); const normalized = await normalizePublicAudience(compact, getDocumentLoader()); assertEquals(normalized.to, PUBLIC_URI); const nestedObject = normalized.object; assertEquals(nestedObject.to, PUBLIC_URI); }); test("normalizePublicAudience() is a no-op without the CURIE", async () => { const input = { "@context": AS_CONTEXT, type: "Note", id: "https://example.com/notes/3", to: PUBLIC_URI }; assertEquals(await normalizePublicAudience(input), input); }); test("normalizePublicAudience() leaves non-addressing fields untouched", async () => { const output = await normalizePublicAudience({ "@context": AS_CONTEXT, type: "Note", id: "https://example.com/notes/4", name: "as:Public", to: "as:Public" }); assertEquals(output.name, "as:Public"); assertEquals(output.to, PUBLIC_URI); }); test("normalizePublicAudience() rewrites without canonicalization for known-safe contexts", async () => { const rejecting = () => { throw new Error("contextLoader should not be called for a known-safe @context"); }; assertEquals((await normalizePublicAudience({ "@context": AS_CONTEXT, type: "Note", id: "https://example.com/notes/fast1", to: "as:Public" }, rejecting)).to, PUBLIC_URI); assertEquals((await normalizePublicAudience({ "@context": [AS_CONTEXT, "https://w3id.org/security/data-integrity/v1"], type: "Note", id: "https://example.com/notes/fast2", to: "as:Public" }, rejecting)).to, PUBLIC_URI); }); test("normalizePublicAudience() falls back to canonicalization for unknown-URL contexts", async () => { let loaderCalls = 0; const loader = (url) => { loaderCalls++; return Promise.resolve({ contextUrl: null, documentUrl: url, document: preloadedContexts[AS_CONTEXT] }); }; assertEquals((await normalizePublicAudience({ "@context": [AS_CONTEXT, "https://custom.example/ctx"], type: "Note", id: "https://example.com/notes/unknown", to: "as:Public" }, loader)).to, PUBLIC_URI); assertEquals(loaderCalls > 0, true); }); test("normalizePublicAudience() leaves @context subtrees untouched", async () => { const inlineCtx = (await normalizePublicAudience({ "@context": [AS_CONTEXT, { "customTerm": "as:Public" }], type: "Note", id: "https://example.com/notes/context", to: PUBLIC_URI }))["@context"][1]; assertEquals(inlineCtx.customTerm, "as:Public"); }); test("normalizePublicAudience() does not traverse prototype-polluted keys", async () => { const polluted = Object.create({ to: "as:Public" }); polluted["@context"] = AS_CONTEXT; polluted.type = "Note"; polluted.id = "https://example.com/notes/proto"; const output = await normalizePublicAudience(polluted); assertEquals(Object.hasOwn(output, "to"), false); }); test("normalizePublicAudience() stops before blowing the stack on pathological nesting", async () => { let deep = { to: "as:Public" }; for (let i = 0; i < 256; i++) deep = { nested: deep }; assertEquals(typeof await normalizePublicAudience({ "@context": AS_CONTEXT, type: "Note", id: "https://example.com/notes/deep", to: "as:Public", object: deep }), "object"); }); test("normalizePublicAudience() does not poison the global prototype via a __proto__ key", async () => { await normalizePublicAudience(JSON.parse(`{ "@context": "https://www.w3.org/ns/activitystreams", "type": "Note", "id": "https://example.com/notes/proto-pollution", "to": "as:Public", "__proto__": { "polluted": true } }`)); assertEquals(Object.prototype.polluted, void 0); }); test("normalizePublicAudience() bails out on nested @context that redefines as:", async () => { const output = await normalizePublicAudience({ "@context": AS_CONTEXT, type: "Create", id: "https://example.com/activities/nested", actor: "https://example.com/alice", to: "as:Public", object: { "@context": { "as": "https://not-activitystreams.example/" }, type: "https://www.w3.org/ns/activitystreams#Note", id: "https://example.com/objects/nested", to: "as:Public" } }); assertEquals(output.to, "as:Public"); const nested = output.object; assertEquals(nested.to, "as:Public"); }); test("normalizePublicAudience() bails out when the rewrite changes semantics", async () => { const output = await normalizePublicAudience({ "@context": { "as": "https://not-activitystreams.example/", "to": { "@id": "https://www.w3.org/ns/activitystreams#to", "@type": "@id" }, "type": "@type", "id": "@id" }, type: "https://www.w3.org/ns/activitystreams#Note", id: "https://example.com/notes/5", to: "as:Public" }); assertEquals(output.to, "as:Public"); assertNotEquals(output.to, PUBLIC_URI); }); //#endregion export {};