Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/improsocial/impro
An extensible Bluesky client for web impro.social
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459import { describe, it } from "node:test";import assert from "node:assert/strict";import { Permissions } from "/js/plugins/pluginPermissions.js";
const normalizeFetchOrigin = Permissions.normalizeFetchOrigin;
// The historical free-function surface, expressed through the Permissions// class so every case exercises the public API.const parsePermissions = (raw) => Permissions.parse(raw).toJSON();const isActionAllowed = (action, permissions) => Permissions.parse(permissions).allowsAction(action);const diffPermissions = (current, next) => Permissions.parse(current).diff(Permissions.parse(next));const isEmptyPermissions = (obj) => Permissions.parse(obj).isEmpty();const isUserFetchAllowed = (permissions) => Permissions.parse(permissions).allowsUserFetch();const isFetchAllowed = (url, permissions) => Permissions.parse(permissions).allowsFetch(url);
describe("parsePermissions", () => { it("returns an empty object when fetch is missing", () => { assert.deepEqual(parsePermissions({}), {}); });
it("omits the fetch key when no valid patterns remain", () => { assert.deepEqual(parsePermissions({ fetch: [] }), {}); assert.deepEqual(parsePermissions({ fetch: [42, null] }), {}); });
it("wraps a string fetch value into an array", () => { assert.deepEqual(parsePermissions({ fetch: "https://x.com/*" }), { fetch: ["https://x.com/*"], }); });
it("filters non-string entries from fetch", () => { assert.deepEqual( parsePermissions({ fetch: ["https://a.com/*", 42, null, "https://b.com/*"], }), { fetch: ["https://a.com/*", "https://b.com/*"] }, ); });
it("dedupes fetch entries", () => { assert.deepEqual( parsePermissions({ fetch: ["https://a.com/*", "https://b.com/*", "https://a.com/*"], }), { fetch: ["https://a.com/*", "https://b.com/*"] }, ); });
it("parses known action scopes and drops unknown ones", () => { assert.deepEqual( parsePermissions({ actions: ["mute", "block", "feedFeedback", "deleteEverything"], }), { actions: ["mute", "block", "feedFeedback"] }, ); });
it("accepts the privateData action scope", () => { assert.deepEqual(parsePermissions({ actions: ["privateData"] }), { actions: ["privateData"], }); });
it("wraps a string actions value into an array", () => { assert.deepEqual(parsePermissions({ actions: "mute" }), { actions: ["mute"], }); });
it("omits the actions key when no valid scopes remain", () => { assert.deepEqual(parsePermissions({ actions: [] }), {}); assert.deepEqual(parsePermissions({ actions: ["feedback"] }), {}); });});
describe("isActionAllowed", () => { it("allows only granted action scopes", () => { const permissions = { actions: ["mute", "feedFeedback"] }; assert(isActionAllowed("mute", permissions)); assert(isActionAllowed("feedFeedback", permissions)); assert(!isActionAllowed("block", permissions)); });
it("denies everything when the actions key is missing", () => { assert(!isActionAllowed("mute", {})); });});
describe("diffPermissions", () => { it("returns null when there are no new permissions", () => { assert.deepEqual( diffPermissions( { fetch: ["https://a.com/*"] }, { fetch: ["https://a.com/*"] }, ), null, ); });
it("returns null when incoming is a subset of stored", () => { assert.deepEqual( diffPermissions( { fetch: ["https://a.com/*", "https://b.com/*"] }, { fetch: ["https://a.com/*"] }, ), null, ); });
it("returns only the newly-added fetch patterns", () => { assert.deepEqual( diffPermissions( { fetch: ["https://a.com/*"] }, { fetch: ["https://a.com/*", "https://b.com/*", "https://c.com/*"], }, ), { fetch: ["https://b.com/*", "https://c.com/*"] }, ); });
it("treats an empty stored set as 'everything new'", () => { assert.deepEqual( diffPermissions({ fetch: [] }, { fetch: ["https://a.com/*"] }), { fetch: ["https://a.com/*"] }, ); });
it("treats a missing stored key the same as an empty array", () => { assert.deepEqual(diffPermissions({}, { fetch: ["https://a.com/*"] }), { fetch: ["https://a.com/*"], }); });
it("returns null when next has no keys", () => { assert.deepEqual(diffPermissions({ fetch: ["https://a.com/*"] }, {}), null); });
it("omits keys from the diff when no additions for that key", () => { assert.deepEqual( diffPermissions( { fetch: ["https://a.com/*"] }, { fetch: ["https://a.com/*"] }, ), null, ); });});
describe("diffPermissions (boolean scopes)", () => { it("reports a newly declared userFetch", () => { assert.deepEqual(diffPermissions({}, { userFetch: true }), { userFetch: true, }); });
it("does not report a userFetch that was already granted", () => { assert.equal( diffPermissions({ userFetch: true }, { userFetch: true }), null, ); });
it("diffs patterns alongside a boolean scope", () => { assert.deepEqual( diffPermissions( { fetch: ["https://a.com/*"] }, { fetch: ["https://a.com/*", "https://b.com/*"], userFetch: true }, ), { fetch: ["https://b.com/*"], userFetch: true }, ); });
it("survives a stored value whose shape doesn't match the manifest", () => { assert.deepEqual( diffPermissions({ fetch: true }, { fetch: ["https://a.com/*"] }), { fetch: ["https://a.com/*"] }, ); });});
describe("isEmptyPermissions (missing-key shape)", () => { it("returns true for an empty object", () => { assert(isEmptyPermissions({})); });});
describe("isEmptyPermissions", () => { it("returns true for an all-empty object", () => { assert(isEmptyPermissions({ fetch: [] })); });
it("returns false when any array is non-empty", () => { assert(!isEmptyPermissions({ fetch: ["https://a.com/*"] })); });
// Load-bearing: this is what keeps a prompting plugin out of preview // installs, and userFetch is a boolean rather than an array. it("returns false for a userFetch grant", () => { assert(!isEmptyPermissions({ userFetch: true })); });
it("returns true for a falsy userFetch", () => { assert(isEmptyPermissions({ userFetch: false })); });});
describe("parsePermissions (userFetch)", () => { it("keeps a literal true", () => { assert.deepEqual(parsePermissions({ userFetch: true }), { userFetch: true, }); });
it("drops truthy non-boolean values", () => { assert.deepEqual(parsePermissions({ userFetch: "yes" }), {}); assert.deepEqual(parsePermissions({ userFetch: 1 }), {}); assert.deepEqual(parsePermissions({ userFetch: false }), {}); });});
describe("isUserFetchAllowed", () => { it("requires the parsed flag", () => { assert(isUserFetchAllowed({ userFetch: true })); assert(!isUserFetchAllowed({})); assert(!isUserFetchAllowed({ fetch: ["https://a.com/*"] })); });});
describe("normalizeFetchOrigin", () => { it("discards the path and keeps the origin", () => { assert.equal( normalizeFetchOrigin("https://api.example.com/v1/chat?key=1#x"), "https://api.example.com/*", ); });
it("preserves an explicit port", () => { assert.equal( normalizeFetchOrigin("http://localhost:11434/api/generate"), "http://localhost:11434/*", ); });
it("drops a default port, matching URL normalization", () => { assert.equal( normalizeFetchOrigin("https://example.com:443/foo"), "https://example.com/*", ); });
it("lowercases the host", () => { assert.equal( normalizeFetchOrigin("https://API.Example.COM/foo"), "https://api.example.com/*", ); });
it("allows http only for loopback", () => { assert.equal( normalizeFetchOrigin("http://127.0.0.1:8080/"), "http://127.0.0.1:8080/*", ); assert.equal( normalizeFetchOrigin("http://[::1]:8080/"), "http://[::1]:8080/*", ); assert.equal(normalizeFetchOrigin("http://example.com/"), null); });
it("rejects embedded credentials", () => { assert.equal(normalizeFetchOrigin("https://user:pass@example.com/"), null); assert.equal(normalizeFetchOrigin("https://user@example.com/"), null); });
it("rejects non-http(s) schemes", () => { assert.equal(normalizeFetchOrigin("ftp://example.com/"), null); assert.equal(normalizeFetchOrigin("javascript:alert(1)"), null); assert.equal(normalizeFetchOrigin("data:text/plain,hi"), null); });
it("rejects unparseable input", () => { assert.equal(normalizeFetchOrigin("not a url"), null); assert.equal(normalizeFetchOrigin(""), null); assert.equal(normalizeFetchOrigin(null), null); });
it("produces a pattern that isFetchAllowed accepts for that origin only", () => { const permissions = { fetch: [normalizeFetchOrigin("https://api.example.com/v1")], }; assert(isFetchAllowed("https://api.example.com/other", permissions)); assert(!isFetchAllowed("https://evil.example.com/", permissions)); assert(!isFetchAllowed("http://api.example.com/", permissions)); });});
describe("isFetchAllowed", () => { it("matches any path when the pattern has no path component", () => { const permissions = { fetch: ["https://example.com"] }; assert(isFetchAllowed("https://example.com/", permissions)); assert(isFetchAllowed("https://example.com/foo", permissions)); assert(isFetchAllowed("https://example.com/foo/bar", permissions)); });
it("still enforces the host when the pattern has no path", () => { const permissions = { fetch: ["https://example.com"] }; assert(!isFetchAllowed("https://other.com/", permissions)); assert(!isFetchAllowed("https://sub.example.com/", permissions)); });
it("supports wildcard hosts without a path component", () => { const permissions = { fetch: ["https://*.example.com"] }; assert(isFetchAllowed("https://example.com/", permissions)); assert(isFetchAllowed("https://a.example.com/foo", permissions)); assert(!isFetchAllowed("https://other.com/", permissions)); });
it("treats a trailing * in the path as a prefix wildcard", () => { const permissions = { fetch: ["https://example.com/foobar*"] }; assert(isFetchAllowed("https://example.com/foobar", permissions)); assert(isFetchAllowed("https://example.com/foobarbaz", permissions)); assert(isFetchAllowed("https://example.com/foobar/sub", permissions)); assert(!isFetchAllowed("https://example.com/foo", permissions)); assert(!isFetchAllowed("https://example.com/other", permissions)); });
it("rejects non-https urls", () => { const permissions = { fetch: ["https://example.com"] }; assert(!isFetchAllowed("http://example.com/", permissions)); });
it("rejects http patterns for non-loopback hosts", () => { const permissions = { fetch: ["http://example.com/*"] }; assert(!isFetchAllowed("http://example.com/", permissions)); });
it("allows http for loopback hosts", () => { const permissions = { fetch: [ "http://localhost:11434/*", "http://127.0.0.1:1234/*", "http://[::1]:8080/*", "http://ollama.localhost/*", ], }; assert(isFetchAllowed("http://localhost:11434/api/generate", permissions)); assert(isFetchAllowed("http://127.0.0.1:1234/v1/chat", permissions)); assert(isFetchAllowed("http://[::1]:8080/v1/chat", permissions)); assert(isFetchAllowed("http://ollama.localhost/api", permissions)); });
it("does not let an https pattern authorize an http url", () => { const permissions = { fetch: ["https://localhost/*"] }; assert(!isFetchAllowed("http://localhost/", permissions)); });
it("does not let an http loopback pattern authorize a remote host", () => { const permissions = { fetch: ["http://localhost/*"] }; assert(!isFetchAllowed("http://localhost.evil.com/", permissions)); assert(!isFetchAllowed("http://notlocalhost/", permissions)); });
it("enforces the port when the pattern specifies one", () => { const permissions = { fetch: ["http://localhost:11434/*"] }; assert(!isFetchAllowed("http://localhost:1234/api", permissions)); assert(!isFetchAllowed("http://localhost/api", permissions)); });
it("matches any port when the pattern omits one", () => { const permissions = { fetch: ["https://example.com/*"] }; assert(isFetchAllowed("https://example.com/foo", permissions)); assert(isFetchAllowed("https://example.com:8443/foo", permissions)); });
it("treats a default port in the url as matching an explicit pattern port", () => { const permissions = { fetch: ["https://example.com:443/*"] }; assert(isFetchAllowed("https://example.com/foo", permissions)); });
it("denies everything when the fetch key is missing", () => { assert(!isFetchAllowed("https://example.com/", {})); });});
describe("Permissions", () => { it("parse is idempotent over its own output", () => { const parsed = Permissions.parse({ fetch: ["https://a.com/*"], userFetch: true, actions: ["mute"], }); assert.deepEqual( Permissions.parse(parsed.toJSON()).toJSON(), parsed.toJSON(), ); });
it("fromManifest reads the manifest permissions key", () => { const permissions = Permissions.fromManifest({ name: "x", permissions: { actions: ["block"] }, }); assert.deepEqual(permissions.toJSON(), { actions: ["block"] }); assert.deepEqual(Permissions.fromManifest({}).toJSON(), {}); });
it("exposes the parsed keys as properties for templates", () => { const permissions = Permissions.parse({ fetch: ["https://a.com/*"], userFetch: true, actions: ["mute"], }); assert.deepEqual(permissions.fetch, ["https://a.com/*"]); assert.deepEqual(permissions.userFetch, true); assert.deepEqual(permissions.actions, ["mute"]); });
it("serializes to the canonical plain shape via JSON.stringify", () => { const permissions = Permissions.parse({ actions: ["mute"] }); assert.deepEqual(JSON.parse(JSON.stringify(permissions)), { actions: ["mute"], }); });
it("withFetchOrigins returns a new grant with the origins appended", () => { const permissions = Permissions.parse({ userFetch: true, fetch: ["https://a.com/*"], }); const merged = permissions.withFetchOrigins(["https://granted.example/*"]); assert.deepEqual(merged.fetch, [ "https://a.com/*", "https://granted.example/*", ]); assert.deepEqual(permissions.fetch, ["https://a.com/*"]); assert.deepEqual(permissions.withFetchOrigins([]), permissions); });
it("allowsAction throws on an unknown action scope", () => { const permissions = Permissions.parse({ actions: ["mute"] }); assert.throws( () => permissions.allowsAction("privatedata"), /unknown action scope "privatedata"/, ); });
it("hasFetchPattern checks for an exact stored pattern", () => { const permissions = Permissions.parse({ fetch: ["https://a.com/*"] }); assert(permissions.hasFetchPattern("https://a.com/*")); assert(!permissions.hasFetchPattern("https://a.com/x")); });});