Merge branch 'hotfix/mindspace-url-canonical-0710'
This commit is contained in:
+58
-1
@@ -453,6 +453,9 @@ const PUBLIC_HTML_LINK_PATTERN =
|
||||
/https?:\/\/[^\s<>"')\]]+\/MindSpace\/([0-9a-f-]{36}|[a-z0-9._-]+)\/([^\s<>"')\]]+\.html)/gi;
|
||||
const PUBLIC_HTML_MARKDOWN_LINK_PATTERN =
|
||||
/\[([^\]\n]*)\]\((https?:\/\/[^\s<>"')\]]+\/MindSpace\/([0-9a-f-]{36}|[a-z0-9._-]+)\/([^\s<>"')\]]+\.html))\)/gi;
|
||||
const MARKDOWN_LINK_PATTERN = /\[([^\]\n]*)\]\((https?:\/\/[^\s<>"')\]]+)\)/g;
|
||||
const PLAIN_HTML_URL_PATTERN = /https?:\/\/[^\s<>"')\]]+\.html/gi;
|
||||
const INLINE_PUBLIC_HTML_PATH_PATTERN = /`((?:\.\/)?public\/[^`\s<>"')\]]+\.html)`/gi;
|
||||
const USER_IDENTITY_BLOCK_PATTERN = /^\[用户身份\][\s\S]*?(?:\n{2,}|$)/;
|
||||
const IMAGE_URL_LINES_PATTERN = /\n*\[图片\d+]: [^\n]+/g;
|
||||
const TKMIND_VISION_NOTE_PATTERN = /\n*【TKMind 图片分析结果[\s\S]*$/;
|
||||
@@ -524,6 +527,48 @@ function sanitizeOwnPublicHtmlUrl(publicUrl, owner, rawRelativePath, currentUser
|
||||
};
|
||||
}
|
||||
|
||||
function isCurrentUserPublicOwner(owner, currentUser) {
|
||||
const normalizedOwner = String(owner ?? '').trim().toLowerCase();
|
||||
const normalizedUserId = String(currentUser?.id ?? '').trim().toLowerCase();
|
||||
const normalizedUsername = String(currentUser?.username ?? '').trim().toLowerCase();
|
||||
return Boolean(normalizedOwner && (
|
||||
normalizedOwner === normalizedUserId ||
|
||||
normalizedOwner === normalizedUsername
|
||||
));
|
||||
}
|
||||
|
||||
function sanitizeMissingMindSpacePublicHtmlUrl(url, currentUser) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const parts = parsed.pathname
|
||||
.split('/')
|
||||
.filter(Boolean)
|
||||
.map((part) => decodePathSegment(part));
|
||||
if (parts[0]?.toLowerCase() === 'mindspace') return null;
|
||||
const owner = parts[0];
|
||||
if (!isCurrentUserPublicOwner(owner, currentUser)) return null;
|
||||
const rawRelativePath = parts.slice(1).join('/');
|
||||
if (!rawRelativePath.toLowerCase().endsWith('.html')) return null;
|
||||
const result = sanitizeOwnPublicHtmlUrl(url, owner, rawRelativePath, currentUser);
|
||||
return result.ok ? result.url : result.notice;
|
||||
}
|
||||
|
||||
function sanitizeOwnPublicHtmlRelativePath(rawRelativePath, currentUser) {
|
||||
const owner = currentUser?.id || currentUser?.username;
|
||||
if (!owner) return null;
|
||||
const normalizedRelativePath = normalizeStaticHtmlRelativePath(rawRelativePath);
|
||||
if (!normalizedRelativePath.toLowerCase().endsWith('.html')) return null;
|
||||
if (!publicHtmlExistsForUser(owner, normalizedRelativePath, currentUser)) return null;
|
||||
return {
|
||||
label: path.posix.basename(normalizedRelativePath),
|
||||
url: buildPublicUrl(resolvePublicBaseUrl(), owner, normalizedRelativePath),
|
||||
};
|
||||
}
|
||||
|
||||
export function sanitizePublicHtmlLinksInText(text, currentUser) {
|
||||
let next = String(text ?? '').replace(
|
||||
PUBLIC_HTML_MARKDOWN_LINK_PATTERN,
|
||||
@@ -533,10 +578,22 @@ export function sanitizePublicHtmlLinksInText(text, currentUser) {
|
||||
return label ? `[${label}](${result.url})` : result.url;
|
||||
},
|
||||
);
|
||||
return next.replace(PUBLIC_HTML_LINK_PATTERN, (match, owner, rawRelativePath) => {
|
||||
next = next.replace(PUBLIC_HTML_LINK_PATTERN, (match, owner, rawRelativePath) => {
|
||||
const result = sanitizeOwnPublicHtmlUrl(match, owner, rawRelativePath, currentUser);
|
||||
return result.ok ? result.url : result.notice;
|
||||
});
|
||||
next = next.replace(MARKDOWN_LINK_PATTERN, (match, label, url) => {
|
||||
const sanitizedUrl = sanitizeMissingMindSpacePublicHtmlUrl(url, currentUser);
|
||||
if (!sanitizedUrl) return match;
|
||||
return label ? `[${label}](${sanitizedUrl})` : sanitizedUrl;
|
||||
});
|
||||
next = next.replace(PLAIN_HTML_URL_PATTERN, (match) => (
|
||||
sanitizeMissingMindSpacePublicHtmlUrl(match, currentUser) ?? match
|
||||
));
|
||||
return next.replace(INLINE_PUBLIC_HTML_PATH_PATTERN, (match, rawRelativePath) => {
|
||||
const result = sanitizeOwnPublicHtmlRelativePath(rawRelativePath, currentUser);
|
||||
return result ? `[${result.label}](${result.url})` : match;
|
||||
});
|
||||
}
|
||||
|
||||
export function sanitizeUserVisibleMessageText(text, currentUser) {
|
||||
|
||||
Reference in New Issue
Block a user