Harden auth/vault path sanitization and clean WeKnora docs

This commit is contained in:
Agent Board
2026-07-28 17:04:27 +08:00
parent 2deaf59f7b
commit 26ff1a9c9a
190 changed files with 13454 additions and 4987 deletions
@@ -1186,15 +1186,15 @@
var documentId = String(urlDocumentId || activeWorkspacePath.documentId || active.documentId || '').trim();
var fileTreeWorkspacePath = activeFileTreeWorkspacePath(documentId) || {};
var workspacePath = Object.assign({}, activeWorkspacePath, fileTreeWorkspacePath);
var session = piLabState.session || {};
var pagePath = normalizeSlashes(urlPagePath || workspacePath.relativePath || workspacePath.path || active.relativePath || active.path || localMarkdownRelativePathFromDocumentId(documentId) || session.pagePath || '');
// Do not fall back to piLabState.session.* — that re-amplifies stale context after navigation.
var pagePath = normalizeSlashes(urlPagePath || workspacePath.relativePath || workspacePath.path || active.relativePath || active.path || localMarkdownRelativePathFromDocumentId(documentId) || '');
var resourceKind = String(workspacePath.resourceKind || workspacePath.kind || active.resourceKind || active.kind || '').trim().toLowerCase();
var isDirectory = resourceKind === 'directory' || resourceKind === 'folder' || resourceKind === 'workspace';
var isStandalonePiPage = currentUrl().pathname === '/page-ai/pi';
var isLocalMarkdownPage = documentId.indexOf('local-md:') === 0 || /\.md$/i.test(pagePath);
if (isDirectory || (isStandalonePiPage && pagePath && !isLocalMarkdownPage)) pagePath = '';
var rootUri = String(currentRootUri() || workspacePath.rootUri || active.rootUri || session.rootUri || '').trim();
var workspaceId = String(currentWorkspaceId() || workspacePath.workspaceId || active.workspaceId || session.workspaceId || '').trim();
var rootUri = String(currentRootUri() || workspacePath.rootUri || active.rootUri || '').trim();
var workspaceId = String(currentWorkspaceId() || workspacePath.workspaceId || active.workspaceId || '').trim();
var titleNode = document.querySelector('[data-page-title-current="true"]') || document.querySelector('.wolai-breadcrumb-current');
var title = String(active.title || workspacePath.title || (titleNode && titleNode.textContent) || document.title || '').trim();
var selection = currentSelectionText();
@@ -1278,12 +1278,13 @@
var context = currentPageContext();
refreshSelectionSummary();
if (!piLabState.session) piLabState.session = {};
if (context.pagePath) piLabState.session.pagePath = context.pagePath;
if (context.pageTitle) piLabState.session.pageTitle = context.pageTitle;
if (context.rootUri) piLabState.session.rootUri = context.rootUri;
if (context.workspaceId) piLabState.session.workspaceId = context.workspaceId;
// Always sync (including empty) so navigation away from a page clears stale context.
piLabState.session.pagePath = context.pagePath || '';
piLabState.session.pageTitle = context.pageTitle || '';
piLabState.session.rootUri = context.rootUri || '';
piLabState.session.workspaceId = context.workspaceId || '';
var folderPath = currentFolderPathFromPagePath(context.pagePath);
if (folderPath !== null) piLabState.session.folderPath = folderPath;
piLabState.session.folderPath = folderPath === null ? '' : folderPath;
updateContextStrip();
return context;
}
@@ -2294,11 +2295,11 @@
updateConfig();
}
function deleteHistorySession(sessionId) {
async function deleteHistorySession(sessionId) {
if (!sessionId) return Promise.resolve();
var existing = currentHistorySession(sessionId);
var title = existing ? historyTitle(existing) : sessionId;
if (!window.confirm('删除历史对话“' + title + '”?')) return Promise.resolve();
if (!(await window.mnote.confirm('删除历史对话“' + title + '”?'))) return Promise.resolve();
return fetch(API.SESSIONS + '/' + encodeURIComponent(sessionId), {
method: 'DELETE',
credentials: 'same-origin',
@@ -2313,10 +2314,10 @@
});
}
function clearHistorySessions() {
async function clearHistorySessions() {
var count = Array.isArray(piLabState.history) ? piLabState.history.length : 0;
if (!count) return Promise.resolve();
if (!window.confirm('清空全部 ' + count + ' 条 Pi 历史对话?')) return Promise.resolve();
if (!(await window.mnote.confirm('清空全部 ' + count + ' 条 Pi 历史对话?'))) return Promise.resolve();
return fetch(API.SESSIONS + '?limit=1000', {
method: 'DELETE',
credentials: 'same-origin',
@@ -2618,8 +2619,10 @@
if (lightrag) lightrag.textContent = '唯一默认';
if (changedFiles) {
var files = {};
piLabState.changedFiles.forEach(function (file) { if (file) files[file] = true; });
piLabState.messages.forEach(function (msg) {
(Array.isArray(piLabState.changedFiles) ? piLabState.changedFiles : []).forEach(function (file) {
if (file) files[file] = true;
});
(Array.isArray(piLabState.messages) ? piLabState.messages : []).forEach(function (msg) {
if (msg && msg.diffSummary && Array.isArray(msg.diffSummary.files)) {
msg.diffSummary.files.forEach(function (file) { if (file) files[file] = true; });
}
@@ -3264,7 +3267,7 @@
function collectPiArtifacts() {
var artifacts = [];
var seen = {};
piLabState.messages.forEach(function (msg) {
(Array.isArray(piLabState.messages) ? piLabState.messages : []).forEach(function (msg) {
if (!msg) return;
(Array.isArray(msg.citations) ? msg.citations : []).forEach(function (cit, index) {
var key = 'citation:' + (cit.url || cit.source || cit.title || index);
@@ -3307,7 +3310,7 @@
});
});
});
piLabState.changedFiles.forEach(function (file) {
(Array.isArray(piLabState.changedFiles) ? piLabState.changedFiles : []).forEach(function (file) {
if (!file || seen['diff:' + file]) return;
seen['diff:' + file] = true;
artifacts.push({ kind: 'diff', title: file, summary: '文件变更' });
@@ -3722,8 +3725,10 @@
function handleToolCallEventPayload(payload) {
payload = payload || {};
// Fail-closed: only explicit allowed===true triggers side effects / changedFiles.
var allowed = payload.allowed === true;
applyToolCallSideEffects({
ok: payload.allowed !== false,
ok: allowed,
toolName: payload.toolName,
result: {
rootUri: payload.rootUri,
@@ -3733,7 +3738,7 @@
});
piLabState.receipts.push({
toolName: payload.toolName,
allowed: payload.allowed !== false,
allowed: allowed,
denyReason: payload.denyReason || null,
diffSummary: payload.diffSummary || null,
citationCount: payload.citationCount || 0,
@@ -3746,9 +3751,9 @@
toolCallId: payload.toolCallId || payload.id || ('receipt:' + (payload.toolName || 'tool') + ':' + piLabState.receipts.length),
toolName: payload.toolName || 'tool',
args: payload.params || payload.args || {},
}, payload.allowed === false ? 'denied' : 'done', {
}, allowed ? 'done' : 'denied', {
result: {
allowed: payload.allowed !== false,
allowed: allowed,
denyReason: payload.denyReason || null,
diffSummary: payload.diffSummary || null,
citationCount: payload.citationCount || 0,
@@ -3757,7 +3762,9 @@
approvalConfirmed: payload.approvalConfirmed === true,
toolPolicy: payload.toolPolicy || '',
});
if (payload.normalizedFilePath && payload.diffSummary) piLabState.changedFiles.push(payload.normalizedFilePath);
if (allowed && payload.normalizedFilePath && payload.diffSummary) {
piLabState.changedFiles.push(payload.normalizedFilePath);
}
updateReceiptDisplay();
updateContextStrip();
}
@@ -4591,7 +4598,8 @@
function parseUnifiedPatch(patchText) {
if (!patchText) return [];
var files = [];
var fileLines = patchText.split('\\n');
// Real newlines (JSON-parsed patch text), not the two-char sequence \ + n.
var fileLines = String(patchText).replace(/\r\n/g, '\n').split('\n');
var currentFile = null;
var currentHunk = null;
@@ -4982,8 +4990,8 @@
}
var copyMarkdown = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-copy-markdown]') : null;
if (copyMarkdown) {
copyTextToClipboard(copyMarkdown.getAttribute('data-page-ai-pi-lab-copy-markdown') || '').then(function () {
showPiToast('Markdown copied', 'success');
copyTextToClipboard(copyMarkdown.getAttribute('data-page-ai-pi-lab-copy-markdown') || '').then(function (ok) {
showPiToast(ok ? 'Markdown copied' : '复制失败(剪贴板不可用)', ok ? 'success' : 'warning');
});
return;
}
@@ -4999,7 +5007,11 @@
}
var openArtifact = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-artifact-open]') : null;
if (openArtifact) {
var artUrl = openArtifact.getAttribute('data-page-ai-pi-lab-artifact-open') || openArtifact.getAttribute('href') || '';
var artUrl = String(openArtifact.getAttribute('data-page-ai-pi-lab-artifact-open') || openArtifact.getAttribute('href') || '').trim();
if (!artUrl || artUrl === '#') {
showPiToast('引用无效或无法访问', 'warning');
return;
}
window.dispatchEvent(new CustomEvent('mnote:open-reference', {
detail: {
source: 'page_ai_pi_lab_artifact',
@@ -5007,9 +5019,7 @@
url: artUrl,
},
}));
if (!artUrl || artUrl === '#') {
showPiToast('引用无效或无法访问', 'warning');
}
return;
}
var viewDiff = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-artifact-view-diff]') : null;
if (viewDiff) {
@@ -5020,15 +5030,17 @@
}
var copyArtifactMarkdown = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-artifact-copy-markdown]') : null;
if (copyArtifactMarkdown) {
copyTextToClipboard(copyArtifactMarkdown.getAttribute('data-page-ai-pi-lab-artifact-copy-markdown') || '').then(function () {
showPiToast('Artifact markdown copied', 'success');
copyTextToClipboard(copyArtifactMarkdown.getAttribute('data-page-ai-pi-lab-artifact-copy-markdown') || '').then(function (ok) {
showPiToast(ok ? 'Artifact markdown copied' : '复制失败(剪贴板不可用)', ok ? 'success' : 'warning');
});
return;
}
var copyArtifact = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-artifact-copy]') : null;
if (copyArtifact) {
var raw = copyArtifact.getAttribute('data-page-ai-pi-lab-artifact-copy') || '';
copyTextToClipboard(raw).then(function () { showPiToast('Artifact raw copied', 'success'); });
copyTextToClipboard(raw).then(function (ok) {
showPiToast(ok ? 'Artifact raw copied' : '复制失败(剪贴板不可用)', ok ? 'success' : 'warning');
});
return;
}
var deleteTarget = event.target && event.target.closest ? event.target.closest('[data-page-ai-pi-lab-history-delete]') : null;