From 9af670dbebac61e558d3588c855aa7671f9ffd33 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Tue, 25 Jul 2023 21:21:29 -0600 Subject: [PATCH] fedimbed: finally impl request signing untested --- .gitignore | 3 +- package.json | 1 + pnpm-lock.yaml | 106 +++++++++++++++++++++++++++++++++++++++- src/modules/fedimbed.js | 56 +++++++++++++++++---- 4 files changed, 155 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 71700cd..a29090e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ node_modules/ +priv/ config.json config.prod.json config.testing.json apikeys.json database.db -private_reminders.json \ No newline at end of file +private_reminders.json diff --git a/package.json b/package.json index a68b98e..4785588 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "homepage": "https://gitdab.com/Cynosphere/HiddenPhox#readme", "dependencies": { "@ctrl/tinycolor": "^3.6.0", + "@peertube/http-signature": "^1.7.0", "@projectdysnomia/dysnomia": "^0.1.2", "dumpy": "github:Cynosphere/dumpy.js", "google-images": "^2.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90e0480..ce3f5f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ dependencies: '@ctrl/tinycolor': specifier: ^3.6.0 version: 3.6.0 + '@peertube/http-signature': + specifier: ^1.7.0 + version: 1.7.0 '@projectdysnomia/dysnomia': specifier: ^0.1.2 version: 0.1.2 @@ -898,6 +901,15 @@ packages: dev: false optional: true + /@peertube/http-signature@1.7.0: + resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.17.0 + dev: false + /@projectdysnomia/dysnomia@0.1.2: resolution: {integrity: sha512-F64G64JwFWn/QUFqkhsyBvXJ0Du3E6Y0yu8tSrcukAnSeW8qV+reKqeQnMmHcQWYopwuYM8Q6OF/VX6VKggtOA==} engines: {node: '>=10.4.0'} @@ -1057,6 +1069,17 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + dev: false + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1064,6 +1087,12 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false + /bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + dependencies: + tweetnacl: 0.14.5 + dev: false + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -1218,6 +1247,10 @@ packages: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} dev: false + /core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + dev: false + /create-error-class@3.0.2: resolution: {integrity: sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==} engines: {node: '>=0.10.0'} @@ -1253,6 +1286,13 @@ packages: resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} dev: false + /dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -1337,6 +1377,13 @@ packages: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} dev: false + /ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false @@ -1480,6 +1527,11 @@ packages: engines: {node: '>=6'} dev: false + /extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -1583,6 +1635,12 @@ packages: engines: {node: '>=4'} dev: false + /getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + dependencies: + assert-plus: 1.0.0 + dev: false + /gifwrap@0.9.4: resolution: {integrity: sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==} dependencies: @@ -1890,14 +1948,32 @@ packages: argparse: 2.0.1 dev: true + /jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + dev: false + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true + /json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: false + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true + /jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + dev: false + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2476,7 +2552,6 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false - optional: true /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -2593,6 +2668,22 @@ packages: - supports-color dev: false + /sshpk@1.17.0: + resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + dev: false + /ssri@8.0.1: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} engines: {node: '>= 8'} @@ -2723,6 +2814,10 @@ packages: safe-buffer: 5.2.1 dev: false + /tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + dev: false + /tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} requiresBuild: true @@ -2800,6 +2895,15 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false + /verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + dev: false + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false diff --git a/src/modules/fedimbed.js b/src/modules/fedimbed.js index 938fb8d..25e5e25 100644 --- a/src/modules/fedimbed.js +++ b/src/modules/fedimbed.js @@ -1,4 +1,7 @@ const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants; +const fs = require("node:fs"); +const path = require("node:path"); +const httpSignature = require("@peertube/http-signature"); const events = require("../lib/events.js"); const logger = require("../lib/logger.js"); @@ -64,6 +67,38 @@ async function resolvePlatform(url) { return nodeinfo.software.name; } +const keyId = "https://hf.c7.pm/action#main-key"; +const privKey = fs.readFileSync(path.resolve("../../priv/private.pem")); +async function signedFetch(url, options) { + const urlObj = new URL(url); + + const headers = { + host: urlObj.host, + date: new Date().toUTCString(), + }; + + const headerNames = ["(request-target)", "host", "date"]; + + httpSignature.sign( + { + getHeader: (name) => headers[name.toLowerCase()], + setHeader: (name, value) => (headers[name] = value), + method: options.method ?? "GET", + path: urlObj.pathname, + }, + { + keyId, + key: privKey, + headers: headerNames, + authorizationHeaderName: "signature", + } + ); + + options.headers = Object.assign(headers, options.headers ?? {}); + + return await fetch(url, options); +} + async function processUrl(msg, url, spoiler = false) { let urlObj = new URL(url); let platform = await resolvePlatform(url); @@ -78,7 +113,7 @@ async function processUrl(msg, url, spoiler = false) { let content, cw, author, timestamp; // Fetch post - const rawPostData = await fetch(url, { + const rawPostData = await signedFetch(url, { headers: { "User-Agent": FRIENDLY_USERAGENT, Accept: "application/activity+json", @@ -106,7 +141,7 @@ async function processUrl(msg, url, spoiler = false) { // Follow redirect from /object since we need the ID from /notice if (PATH_REGEX.pleroma.test(urlObj.pathname)) { - url = await fetch(url, { + url = await signedFetch(url, { method: "HEAD", headers: { "User-Agent": FRIENDLY_USERAGENT, @@ -157,7 +192,7 @@ async function processUrl(msg, url, spoiler = false) { options )}, ${JSON.stringify(headers)}` ); - const rawPostData2 = await fetch( + const rawPostData2 = await signedFetch( redirUrl, Object.assign(options, { headers: Object.assign(headers, { @@ -299,12 +334,15 @@ async function processUrl(msg, url, spoiler = false) { } // Author data is not sent with the post with AS2 - const authorData = await fetch(postData.actor ?? postData.attributedTo, { - headers: { - "User-Agent": FRIENDLY_USERAGENT, - Accept: "application/activity+json", - }, - }) + const authorData = await signedFetch( + postData.actor ?? postData.attributedTo, + { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: "application/activity+json", + }, + } + ) .then((res) => res.json()) .catch((err) => { logger.error("fedimbed", `Failed to get author for "${url}": ${err}`);