server: handle redirects in signed get

part of FoundKeyGang/FoundKey#288

Changelog: Fixed
This commit is contained in:
Johann150 2022-12-20 22:06:54 +01:00
parent d75e295ee8
commit aa33708b90
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1
2 changed files with 30 additions and 18 deletions

View file

@ -35,7 +35,7 @@ export async function getHtml(url: string, accept = 'text/html, */*', timeout =
return await res.text(); return await res.text();
} }
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number }) { export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number, redirect: 'follow' | 'manual' | 'error' = 'follow' }) {
const timeout = args.timeout || 10 * SECOND; const timeout = args.timeout || 10 * SECOND;
const controller = new AbortController(); const controller = new AbortController();
@ -47,8 +47,9 @@ export async function getResponse(args: { url: string, method: string, body?: st
method: args.method, method: args.method,
headers: args.headers, headers: args.headers,
body: args.body, body: args.body,
redirect: args.redirect,
timeout, timeout,
size: args.size || 10 * 1024 * 1024, size: args.size || 10 * 1024 * 1024, // 10 MiB
agent: getAgentByUrl, agent: getAgentByUrl,
signal: controller.signal, signal: controller.signal,
}); });

View file

@ -1,3 +1,4 @@
import { URL } from 'node:url';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { getUserKeypair } from '@/misc/keypair-store.js'; import { getUserKeypair } from '@/misc/keypair-store.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@ -37,22 +38,32 @@ export async function request(user: { id: User['id'] }, url: string, object: any
export async function signedGet(url: string, user: { id: User['id'] }): Promise<any> { export async function signedGet(url: string, user: { id: User['id'] }): Promise<any> {
const keypair = await getUserKeypair(user.id); const keypair = await getUserKeypair(user.id);
const req = createSignedGet({ for (let redirects = 0; redirects < 3; redirects++) {
key: { const req = createSignedGet({
privateKeyPem: keypair.privateKey, key: {
keyId: `${config.url}/users/${user.id}#main-key`, privateKeyPem: keypair.privateKey,
}, keyId: `${config.url}/users/${user.id}#main-key`,
url, },
additionalHeaders: { url,
'User-Agent': config.userAgent, additionalHeaders: {
}, 'User-Agent': config.userAgent,
}); },
});
const res = await getResponse({ const res = await getResponse({
url, url,
method: req.request.method, method: req.request.method,
headers: req.request.headers, headers: req.request.headers,
}); });
return await res.json(); if (res.status >= 300 && res.status < 400) {
// Have been redirected, need to make a new signature.
// Use Location header and fetched URL as the base URL.
url = new URL(res.headers.get('Location'), url).href;
} else {
return await res.json();
}
}
throw new Error('too many redirects');
} }