fix(page-data): isolate owner API from public pages

This commit is contained in:
john
2026-07-15 10:56:22 +08:00
parent 72fd88219c
commit a07cf07861
15 changed files with 200 additions and 90 deletions
+23 -1
View File
@@ -41,12 +41,21 @@ export function detectPageDataDatasetUsageFromHtml(html) {
for (const match of text.matchAll(/\.listRows\(\s*['"]([^'"]+)['"]/g)) {
remember(match[1], { read: true });
}
for (const match of text.matchAll(/\.deleteRow\(\s*['"]([^'"]+)['"]/g)) {
// The browser client's deleteRow endpoint intentionally falls back to a
// soft delete unless a policy explicitly grants hard_delete. A page using
// this API must therefore only require the safe, default capability.
remember(match[1], { softDelete: true });
}
for (const match of text.matchAll(/\.insertRow\(\s*([A-Za-z_$][\w$]*)/g)) {
remember(constants.get(match[1]) ?? match[1], { insert: true });
}
for (const match of text.matchAll(/\.listRows\(\s*([A-Za-z_$][\w$]*)/g)) {
remember(constants.get(match[1]) ?? match[1], { read: true });
}
for (const match of text.matchAll(/\.deleteRow\(\s*([A-Za-z_$][\w$]*)/g)) {
remember(constants.get(match[1]) ?? match[1], { softDelete: true });
}
return datasets;
}
@@ -55,7 +64,16 @@ export function inferPageDataBindAccessMode(relativePath, html) {
const usage = detectPageDataDatasetUsageFromHtml(html);
const hasRead = [...usage.values()].some((item) => item.read);
const hasInsert = [...usage.values()].some((item) => item.insert);
if (/-admin\.html$/i.test(String(relativePath ?? '')) || (hasRead && !hasInsert)) {
// A page that exchanges a password for a Page Data token is intentionally
// protected even when it both reads and writes its dataset (for example, a
// personal tracker). Do not silently re-bind it as public merely because it
// is not named "-admin.html".
const usesServerAuthentication = /\.\s*authenticate\s*\(/.test(String(html ?? ''));
if (
/-admin\.html$/i.test(String(relativePath ?? '')) ||
(hasRead && !hasInsert) ||
usesServerAuthentication
) {
return 'password';
}
return 'public';
@@ -122,6 +140,10 @@ export function buildPageDataPolicyDatasetsFromRegistry({ html, registryDatasets
entry.columns.read = registered.columns?.read ?? [];
}
}
if (perms.softDelete) {
entry.softDelete = true;
entry.columns.soft_delete = registered.columns?.soft_delete ?? ['id'];
}
datasets[name] = entry;
}