use microformats-parser

This commit is contained in:
Kagami Sascha Rosylight 2023-06-29 12:07:52 +02:00
parent 4f35af5590
commit c1d2f72e61
4 changed files with 84 additions and 35 deletions

View File

@ -61,7 +61,7 @@
"@fastify/accepts": "4.2.0",
"@fastify/cookie": "8.3.0",
"@fastify/cors": "8.3.0",
"@fastify/express": "^2.3.0",
"@fastify/express": "2.3.0",
"@fastify/http-proxy": "9.2.1",
"@fastify/multipart": "7.7.0",
"@fastify/static": "6.10.2",
@ -79,7 +79,7 @@
"autwh": "0.1.0",
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "^1.20.2",
"body-parser": "1.20.2",
"bullmq": "4.1.0",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.0",
@ -100,7 +100,7 @@
"got": "13.0.0",
"happy-dom": "9.20.3",
"hpagent": "1.2.0",
"http-link-header": "^1.1.0",
"http-link-header": "1.1.0",
"ioredis": "5.3.2",
"ip-cidr": "3.1.0",
"ipaddr.js": "2.1.0",
@ -112,6 +112,7 @@
"jsrsasign": "10.8.6",
"meilisearch": "0.33.0",
"mfm-js": "0.23.3",
"microformats-parser": "1.4.1",
"mime-types": "2.1.35",
"misskey-js": "workspace:*",
"ms": "3.0.0-canary.1",
@ -120,13 +121,13 @@
"nodemailer": "6.9.3",
"nsfwjs": "2.4.2",
"oauth": "0.10.0",
"oauth2orize": "^1.11.1",
"oauth2orize-pkce": "^0.1.2",
"oauth2orize": "1.11.1",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
"otpauth": "9.1.2",
"parse5": "7.1.2",
"pg": "8.11.0",
"pkce-challenge": "^4.0.1",
"pkce-challenge": "4.0.1",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"pug": "3.0.2",
@ -172,25 +173,25 @@
"@types/accepts": "1.3.5",
"@types/archiver": "5.3.2",
"@types/bcryptjs": "2.4.2",
"@types/body-parser": "^1.19.2",
"@types/body-parser": "1.19.2",
"@types/cbor": "6.0.0",
"@types/color-convert": "2.0.0",
"@types/content-disposition": "0.5.5",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21",
"@types/http-link-header": "^1.0.3",
"@types/http-link-header": "1.0.3",
"@types/jest": "29.5.2",
"@types/js-yaml": "4.0.5",
"@types/jsdom": "21.1.1",
"@types/jsonld": "1.5.9",
"@types/jsrsasign": "10.5.8",
"@types/mime-types": "2.1.1",
"@types/ms": "^0.7.31",
"@types/ms": "0.7.31",
"@types/node": "20.3.1",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.8",
"@types/oauth": "0.9.1",
"@types/oauth2orize": "^1.11.0",
"@types/oauth2orize": "1.11.0",
"@types/pg": "8.10.2",
"@types/pug": "2.0.6",
"@types/punycode": "2.1.0",
@ -202,7 +203,7 @@
"@types/sanitize-html": "2.9.0",
"@types/semver": "7.5.0",
"@types/sharp": "0.32.0",
"@types/simple-oauth2": "^5.0.4",
"@types/simple-oauth2": "5.0.4",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
@ -221,6 +222,6 @@
"execa": "6.1.0",
"jest": "29.5.0",
"jest-mock": "29.5.0",
"simple-oauth2": "^5.0.0"
"simple-oauth2": "5.0.0"
}
}

View File

@ -11,6 +11,7 @@ import pug from 'pug';
import bodyParser from 'body-parser';
import fastifyExpress from '@fastify/express';
import { verifyChallenge } from 'pkce-challenge';
import { mf2 } from 'microformats-parser';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { kinds } from '@/misc/api-permissions.js';
@ -24,6 +25,7 @@ import type { LocalUser } from '@/models/entities/User.js';
import { MemoryKVCache } from '@/misc/cache.js';
import { LoggerService } from '@/core/LoggerService.js';
import Logger from '@/logger.js';
import type { StatusError } from '@/misc/status-error.js';
import type { ServerResponse } from 'node:http';
import type { FastifyInstance } from 'fastify';
@ -111,20 +113,33 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
redirectUris.push(...httpLinkHeader.parse(linkHeader).get('rel', 'redirect_uri').map(r => r.uri));
}
const fragment = JSDOM.fragment(await res.text());
const text = await res.text();
const fragment = JSDOM.fragment(text);
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
const name = fragment.querySelector<HTMLElement>('.h-app .p-name')?.textContent?.trim() ?? id;
let name = id;
if (text) {
const microformats = mf2(text, { baseUrl: res.url });
const nameProperty = microformats.items.find(item => item.type?.includes('h-app') && item.properties.url?.includes(id))?.properties.name[0];
if (typeof nameProperty === 'string') {
name = nameProperty;
}
}
return {
id,
redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()),
name,
name: typeof name === 'string' ? name : id,
};
} catch (err) {
logger.error('Failed to fetch client information', { err });
throw new AuthorizationError('Failed to fetch client information', 'server_error');
console.error(err);
logger.error('Error while fetching client information', { err });
if (err instanceof StatusError) {
throw new AuthorizationError('Failed to fetch client information', 'invalid_request');
} else {
throw new AuthorizationError('Failed to parse client information', 'server_error');
}
}
}

View File

@ -168,7 +168,7 @@ describe('OAuth', () => {
reply.send(`
<!DOCTYPE html>
<link rel="redirect_uri" href="/redirect" />
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
};
});
@ -807,7 +807,7 @@ describe('OAuth', () => {
reply.header('Link', '</redirect>; rel="redirect_uri"');
reply.send(`
<!DOCTYPE html>
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
},
'Mixed links': reply => {
@ -815,14 +815,14 @@ describe('OAuth', () => {
reply.send(`
<!DOCTYPE html>
<link rel="redirect_uri" href="/redirect2" />
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
},
'Multiple items in Link header': reply => {
reply.header('Link', '</redirect2>; rel="redirect_uri",</redirect>; rel="redirect_uri"');
reply.send(`
<!DOCTYPE html>
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
},
'Multiple items in HTML': reply => {
@ -830,7 +830,7 @@ describe('OAuth', () => {
<!DOCTYPE html>
<link rel="redirect_uri" href="/redirect2" />
<link rel="redirect_uri" href="/redirect" />
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
},
};
@ -856,7 +856,7 @@ describe('OAuth', () => {
sender = (reply): void => {
reply.send(`
<!DOCTYPE html>
<div class="h-app"><div class="p-name">Misklient
<div class="h-app"><a href="/" class="u-url p-name">Misklient
`);
};
@ -907,6 +907,29 @@ describe('OAuth', () => {
assert.strictEqual(response.status, 200);
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
});
test('Mismatching URL in h-app', async () => {
sender = (reply): void => {
reply.header('Link', '</redirect>; rel="redirect_uri"');
reply.send(`
<!DOCTYPE html>
<div class="h-app"><a href="/foo" class="u-url p-name">Misklient
`);
reply.send();
};
const client = new AuthorizationCode(clientConfig);
const response = await fetch(client.authorizeURL({
redirect_uri,
scope: 'write:notes',
state: 'state',
code_challenge: 'code',
code_challenge_method: 'S256',
} as AuthorizationParamsExtended));
assert.strictEqual(response.status, 200);
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
});
});
test('Unknown OAuth endpoint', async () => {

View File

@ -99,7 +99,7 @@ importers:
specifier: 8.3.0
version: 8.3.0
'@fastify/express':
specifier: ^2.3.0
specifier: 2.3.0
version: 2.3.0
'@fastify/http-proxy':
specifier: 9.2.1
@ -153,7 +153,7 @@ importers:
specifier: 2.0.5
version: 2.0.5
body-parser:
specifier: ^1.20.2
specifier: 1.20.2
version: 1.20.2
bullmq:
specifier: 4.1.0
@ -216,7 +216,7 @@ importers:
specifier: 1.2.0
version: 1.2.0
http-link-header:
specifier: ^1.1.0
specifier: 1.1.0
version: 1.1.0
ioredis:
specifier: 5.3.2
@ -251,6 +251,9 @@ importers:
mfm-js:
specifier: 0.23.3
version: 0.23.3
microformats-parser:
specifier: 1.4.1
version: 1.4.1
mime-types:
specifier: 2.1.35
version: 2.1.35
@ -276,10 +279,10 @@ importers:
specifier: 0.10.0
version: 0.10.0
oauth2orize:
specifier: ^1.11.1
specifier: 1.11.1
version: 1.11.1
oauth2orize-pkce:
specifier: ^0.1.2
specifier: 0.1.2
version: 0.1.2
os-utils:
specifier: 0.0.14
@ -294,7 +297,7 @@ importers:
specifier: 8.11.0
version: 8.11.0
pkce-challenge:
specifier: ^4.0.1
specifier: 4.0.1
version: 4.0.1
probe-image-size:
specifier: 7.2.3
@ -509,7 +512,7 @@ importers:
specifier: 2.4.2
version: 2.4.2
'@types/body-parser':
specifier: ^1.19.2
specifier: 1.19.2
version: 1.19.2
'@types/cbor':
specifier: 6.0.0
@ -527,7 +530,7 @@ importers:
specifier: 2.1.21
version: 2.1.21
'@types/http-link-header':
specifier: ^1.0.3
specifier: 1.0.3
version: 1.0.3
'@types/jest':
specifier: 29.5.2
@ -548,7 +551,7 @@ importers:
specifier: 2.1.1
version: 2.1.1
'@types/ms':
specifier: ^0.7.31
specifier: 0.7.31
version: 0.7.31
'@types/node':
specifier: 20.3.1
@ -563,7 +566,7 @@ importers:
specifier: 0.9.1
version: 0.9.1
'@types/oauth2orize':
specifier: ^1.11.0
specifier: 1.11.0
version: 1.11.0
'@types/pg':
specifier: 8.10.2
@ -599,7 +602,7 @@ importers:
specifier: 0.32.0
version: 0.32.0
'@types/simple-oauth2':
specifier: ^5.0.4
specifier: 5.0.4
version: 5.0.4
'@types/sinonjs__fake-timers':
specifier: 8.1.2
@ -656,7 +659,7 @@ importers:
specifier: 29.5.0
version: 29.5.0
simple-oauth2:
specifier: ^5.0.0
specifier: 5.0.0
version: 5.0.0
packages/frontend:
@ -15528,6 +15531,13 @@ packages:
twemoji-parser: 14.0.0
dev: false
/microformats-parser@1.4.1:
resolution: {integrity: sha512-BSg9Y/Aik8hvvme/fkxnXMRvTKuVwOeTapeZdaPQ+92DEubyM31iMtwbgFZ1383om643UvfYY5G23E9s1FY2KQ==}
engines: {node: '>=10'}
dependencies:
parse5: 6.0.1
dev: false
/micromatch@3.1.10:
resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
engines: {node: '>=0.10.0'}