fix(kbdb): searchByTemplate 真按 owner_id 過濾(修租戶隔離洩漏)
煙霧測試發現 searchByTemplate 的 `if (rec && (!owner_id || true))` 是 stub, `|| true` 讓 owner_id 過濾失效 → 任何租戶查同 template 看得到別人的 record。 改:給 owner_id 時 JOIN entries 在 SQL 限定 e.owner_id(record 歸屬存底層 entries.owner_id,createRecord 寫入時帶);沒給才不限(內部/全域查詢)。 cypher KBDB proxy 強制注入 owner_id,故端到端隔離靠這條 SQL 落地。 searchEntries 早已正確按 owner_id 過濾,無此 bug。kbdb tsc exit 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -107,14 +107,28 @@ export async function getRecord(db: D1Database, recordId: string): Promise<Recor
|
||||
export async function searchByTemplate(db: D1Database, template: string, owner_id?: string, limit = 100): Promise<RecordResult[]> {
|
||||
const tpl = await getTemplate(db, template);
|
||||
if (!tpl) return [];
|
||||
const res = await db
|
||||
// owner_id 過濾在 SQL 做:record 的歸屬存在底層 entries.owner_id(createRecord 寫入時帶)。
|
||||
// 給了 owner_id → JOIN entries 限定該 owner(租戶隔離,cypher proxy 強制注入);
|
||||
// 沒給 → 不限(內部/全域查詢)。先前 `|| true` 是 stub,會洩漏跨租戶資料(2026-06-14 修)。
|
||||
const cap = Math.min(limit, 500);
|
||||
const res = owner_id
|
||||
? await db
|
||||
.prepare(
|
||||
`SELECT DISTINCT ev.record_id as record_id FROM entry_values ev
|
||||
JOIN entries e ON ev.entry_id = e.id
|
||||
WHERE ev.template_id = ? AND e.owner_id = ?
|
||||
ORDER BY ev.created_at DESC LIMIT ?`,
|
||||
)
|
||||
.bind(tpl.id, owner_id, cap)
|
||||
.all<{ record_id: string }>()
|
||||
: await db
|
||||
.prepare(`SELECT DISTINCT record_id FROM entry_values WHERE template_id = ? ORDER BY created_at DESC LIMIT ?`)
|
||||
.bind(tpl.id, Math.min(limit, 500))
|
||||
.bind(tpl.id, cap)
|
||||
.all<{ record_id: string }>();
|
||||
const out: RecordResult[] = [];
|
||||
for (const { record_id } of res.results ?? []) {
|
||||
const rec = await getRecord(db, record_id);
|
||||
if (rec && (!owner_id || true)) out.push(rec);
|
||||
if (rec) out.push(rec);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user