('br', lines)) {
+ html += x === 'br' ? '
' : x;
}
- return el;
+ return `${html}
`;
},
url: (node) => {
- const a = doc.createElement('a');
- a.setAttribute('href', node.props.url);
- a.textContent = node.props.url;
- return a;
+ return `${escapeHtml(node.props.url)}`;
},
search: (node) => {
- const a = doc.createElement('a');
- a.setAttribute('href', `https://www.google.com/search?q=${node.props.query}`);
- a.textContent = node.props.content;
- return a;
+ return `${escapeHtml(node.props.content)}`;
},
plain: (node) => {
- const el = doc.createElement('span');
- appendChildren(node.children, el);
- return el;
+ return `${toHtml(node.children)}`;
},
};
- appendChildren(nodes, body);
-
- for (const additionalAppender of additionalAppenders) {
- additionalAppender(doc, body);
- }
-
- // Remove the unnecessary namespace
- const serialized = new XMLSerializer().serializeToString(body).replace(/^\s*/, '
');
-
- happyDOM.close().catch(err => {});
-
- return serialized;
+ return `
${toHtml(nodes)}${extraHtml ?? ''}
`;
}
}
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 55521d6e3a..64248ca12f 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -19,7 +19,7 @@ import type { MiEmoji } from '@/models/Emoji.js';
import type { MiPoll } from '@/models/Poll.js';
import type { MiPollVote } from '@/models/PollVote.js';
import { UserKeypairService } from '@/core/UserKeypairService.js';
-import { MfmService, type Appender } from '@/core/MfmService.js';
+import { MfmService } from '@/core/MfmService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import type { MiUserKeypair } from '@/models/UserKeypair.js';
@@ -384,7 +384,7 @@ export class ApRendererService {
inReplyTo = null;
}
- let quote;
+ let quote: string | undefined;
if (note.renoteId) {
const renote = await this.notesRepository.findOneBy({ id: note.renoteId });
@@ -430,29 +430,18 @@ export class ApRendererService {
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
}
- const apAppend: Appender[] = [];
+ let extraHtml: string | null = null;
- if (quote) {
+ if (quote != null) {
// Append quote link as `
RE: ...`
// the claas name `quote-inline` is used in non-misskey clients for styling quote notes.
// For compatibility, the span part should be kept as possible.
- apAppend.push((doc, body) => {
- body.appendChild(doc.createElement('br'));
- body.appendChild(doc.createElement('br'));
- const span = doc.createElement('span');
- span.className = 'quote-inline';
- span.appendChild(doc.createTextNode('RE: '));
- const link = doc.createElement('a');
- link.setAttribute('href', quote);
- link.textContent = quote;
- span.appendChild(link);
- body.appendChild(span);
- });
+ extraHtml = `
RE: ${quote}`;
}
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
- const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend);
+ const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, extraHtml);
const emojis = await this.getEmojis(note.emojis);
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index 61d328ccac..49298a1d22 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -6,7 +6,7 @@
import * as crypto from 'node:crypto';
import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common';
-import { Window } from 'happy-dom';
+import * as htmlParser from 'node-html-parser';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { MiUser } from '@/models/User.js';
@@ -215,29 +215,9 @@ export class ApRequestService {
_followAlternate === true
) {
const html = await res.text();
- const { window, happyDOM } = new Window({
- settings: {
- disableJavaScriptEvaluation: true,
- disableJavaScriptFileLoading: true,
- disableCSSFileLoading: true,
- disableComputedStyleRendering: true,
- handleDisabledFileLoadingAsSuccess: true,
- navigation: {
- disableMainFrameNavigation: true,
- disableChildFrameNavigation: true,
- disableChildPageNavigation: true,
- disableFallbackToSetURL: true,
- },
- timer: {
- maxTimeout: 0,
- maxIntervalTime: 0,
- maxIntervalIterations: 0,
- },
- },
- });
- const document = window.document;
+
try {
- document.documentElement.innerHTML = html;
+ const document = htmlParser.parse(html);
const alternate = document.querySelector('head > link[rel="alternate"][type="application/activity+json"]');
if (alternate) {
@@ -248,8 +228,6 @@ export class ApRequestService {
}
} catch (e) {
// something went wrong parsing the HTML, ignore the whole thing
- } finally {
- happyDOM.close().catch(err => {});
}
}
//#endregion
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index f9d904f3cd..fef6a27087 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -15,7 +15,6 @@ import fastifyStatic from '@fastify/static';
import fastifyView from '@fastify/view';
import fastifyProxy from '@fastify/http-proxy';
import vary from 'vary';
-import htmlSafeJsonStringify from 'htmlescape';
import type { Config } from '@/config.js';
import { getNoteSummary } from '@/misc/get-note-summary.js';
import { DI } from '@/di-symbols.js';
@@ -63,6 +62,20 @@ const frontendViteOut = `${_dirname}/../../../../../built/_frontend_vite_/`;
const frontendEmbedViteOut = `${_dirname}/../../../../../built/_frontend_embed_vite_/`;
const tarball = `${_dirname}/../../../../../built/tarball/`;
+const ESCAPE_LOOKUP = {
+ '&': '\\u0026',
+ '>': '\\u003e',
+ '<': '\\u003c',
+ '\u2028': '\\u2028',
+ '\u2029': '\\u2029',
+} as Record;
+
+const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
+
+function htmlSafeJsonStringify(obj: any): string {
+ return JSON.stringify(obj).replace(ESCAPE_REGEX, x => ESCAPE_LOOKUP[x]);
+}
+
@Injectable()
export class ClientServerService {
private logger: Logger;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2133763d7d..0317a4a6f4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -246,15 +246,9 @@ importers:
got:
specifier: 14.6.4
version: 14.6.4
- happy-dom:
- specifier: 20.0.10
- version: 20.0.10
hpagent:
specifier: 1.2.0
version: 1.2.0
- htmlescape:
- specifier: 1.1.1
- version: 1.1.1
http-link-header:
specifier: 1.1.3
version: 1.1.3
@@ -481,9 +475,6 @@ importers:
'@types/fluent-ffmpeg':
specifier: 2.1.28
version: 2.1.28
- '@types/htmlescape':
- specifier: 1.1.3
- version: 1.1.3
'@types/http-link-header':
specifier: 1.0.7
version: 1.0.7
@@ -4674,9 +4665,6 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
- '@types/htmlescape@1.1.3':
- resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
-
'@types/http-cache-semantics@4.0.4':
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
@@ -7283,10 +7271,6 @@ packages:
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
- htmlescape@1.1.1:
- resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==}
- engines: {node: '>=0.10'}
-
htmlparser2@10.0.0:
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
@@ -11413,7 +11397,7 @@ snapshots:
'@apm-js-collab/tracing-hooks@0.3.1':
dependencies:
'@apm-js-collab/code-transformer': 0.8.2
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
module-details-from-path: 1.0.4
transitivePeerDependencies:
- supports-color
@@ -12003,7 +11987,7 @@ snapshots:
'@babel/types': 7.28.5
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -12162,7 +12146,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/template': 7.27.2
'@babel/types': 7.28.5
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -12492,7 +12476,7 @@ snapshots:
'@eslint/config-array@0.21.1':
dependencies:
'@eslint/object-schema': 2.1.7
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -12512,7 +12496,7 @@ snapshots:
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
espree: 10.4.0
globals: 14.0.0
ignore: 5.3.2
@@ -13347,7 +13331,7 @@ snapshots:
dependencies:
agent-base: 7.1.4
http-proxy-agent: 7.0.2
- https-proxy-agent: 7.0.6(supports-color@10.2.2)
+ https-proxy-agent: 7.0.6
lru-cache: 10.4.3
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
@@ -15017,7 +15001,7 @@ snapshots:
'@tokenizer/inflate@0.2.7':
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
fflate: 0.8.2
token-types: 6.1.1
transitivePeerDependencies:
@@ -15025,7 +15009,7 @@ snapshots:
'@tokenizer/inflate@0.3.1':
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
fflate: 0.8.2
token-types: 6.1.1
transitivePeerDependencies:
@@ -15033,7 +15017,7 @@ snapshots:
'@tokenizer/inflate@0.4.1':
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
token-types: 6.1.1
transitivePeerDependencies:
- supports-color
@@ -15159,8 +15143,6 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
- '@types/htmlescape@1.1.3': {}
-
'@types/http-cache-semantics@4.0.4': {}
'@types/http-errors@2.0.5': {}
@@ -15438,7 +15420,7 @@ snapshots:
'@typescript-eslint/types': 8.47.0
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.47.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
eslint: 9.39.1
typescript: 5.9.3
transitivePeerDependencies:
@@ -15448,7 +15430,7 @@ snapshots:
dependencies:
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
'@typescript-eslint/types': 8.47.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -15467,7 +15449,7 @@ snapshots:
'@typescript-eslint/types': 8.47.0
'@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3)
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
eslint: 9.39.1
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
@@ -15482,7 +15464,7 @@ snapshots:
'@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3)
'@typescript-eslint/types': 8.47.0
'@typescript-eslint/visitor-keys': 8.47.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
@@ -15939,7 +15921,7 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
optional: true
@@ -16236,7 +16218,7 @@ snapshots:
axios@0.24.0:
dependencies:
- follow-redirects: 1.15.11(debug@4.4.3)
+ follow-redirects: 1.15.11
transitivePeerDependencies:
- debug
@@ -17065,6 +17047,10 @@ snapshots:
dependencies:
ms: 2.0.0
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
debug@3.2.7(supports-color@8.1.1):
dependencies:
ms: 2.1.3
@@ -17553,7 +17539,7 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
- debug: 3.2.7(supports-color@8.1.1)
+ debug: 3.2.7
is-core-module: 2.16.1
resolve: 1.22.11
transitivePeerDependencies:
@@ -17561,7 +17547,7 @@ snapshots:
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1):
dependencies:
- debug: 3.2.7(supports-color@8.1.1)
+ debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3)
eslint: 9.39.1
@@ -17576,7 +17562,7 @@ snapshots:
array.prototype.findlastindex: 1.2.6
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
- debug: 3.2.7(supports-color@8.1.1)
+ debug: 3.2.7
doctrine: 2.1.0
eslint: 9.39.1
eslint-import-resolver-node: 0.3.9
@@ -17640,7 +17626,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
escape-string-regexp: 4.0.0
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
@@ -18070,6 +18056,8 @@ snapshots:
async: 0.2.10
which: 1.3.1
+ follow-redirects@1.15.11: {}
+
follow-redirects@1.15.11(debug@4.4.3):
optionalDependencies:
debug: 4.4.3(supports-color@10.2.2)
@@ -18422,8 +18410,6 @@ snapshots:
html-void-elements@3.0.0: {}
- htmlescape@1.1.1: {}
-
htmlparser2@10.0.0:
dependencies:
domelementtype: 2.3.0
@@ -18467,7 +18453,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -18487,7 +18473,7 @@ snapshots:
https-proxy-agent@2.2.4:
dependencies:
agent-base: 4.3.0
- debug: 3.2.7(supports-color@8.1.1)
+ debug: 3.2.7
transitivePeerDependencies:
- supports-color
optional: true
@@ -18495,11 +18481,18 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
optional: true
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3(supports-color@5.5.0)
+ transitivePeerDependencies:
+ - supports-color
+
https-proxy-agent@7.0.6(supports-color@10.2.2):
dependencies:
agent-base: 7.1.4
@@ -18603,7 +18596,7 @@ snapshots:
dependencies:
'@ioredis/commands': 1.4.0
cluster-key-slot: 1.1.2
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -18843,7 +18836,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -19244,7 +19237,7 @@ snapshots:
decimal.js: 10.6.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
- https-proxy-agent: 7.0.6(supports-color@10.2.2)
+ https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.22
parse5: 7.3.0
@@ -20131,7 +20124,7 @@ snapshots:
needle@2.9.1:
dependencies:
- debug: 3.2.7(supports-color@8.1.1)
+ debug: 3.2.7
iconv-lite: 0.4.24
sax: 1.4.3
transitivePeerDependencies:
@@ -21252,7 +21245,7 @@ snapshots:
require-in-the-middle@7.5.2:
dependencies:
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
module-details-from-path: 1.0.4
resolve: 1.22.11
transitivePeerDependencies:
@@ -21586,7 +21579,7 @@ snapshots:
dependencies:
'@hapi/hoek': 11.0.7
'@hapi/wreck': 18.1.0
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
joi: 17.13.3
transitivePeerDependencies:
- supports-color
@@ -21686,7 +21679,7 @@ snapshots:
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
socks: 2.8.7
transitivePeerDependencies:
- supports-color
@@ -21981,7 +21974,7 @@ snapshots:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
fast-safe-stringify: 2.1.1
form-data: 4.0.5
formidable: 3.5.4
@@ -22329,7 +22322,7 @@ snapshots:
app-root-path: 3.1.0
buffer: 6.0.3
dayjs: 1.11.19
- debug: 4.4.3(supports-color@10.2.2)
+ debug: 4.4.3(supports-color@5.5.0)
dedent: 1.7.0
dotenv: 16.6.1
glob: 10.5.0
@@ -22771,7 +22764,7 @@ snapshots:
dependencies:
asn1.js: 5.4.1
http_ece: 1.2.0
- https-proxy-agent: 7.0.6(supports-color@10.2.2)
+ https-proxy-agent: 7.0.6
jws: 4.0.0
minimist: 1.2.8
transitivePeerDependencies: