oficina / sheets Public

Expose shape-only previews to native sheet libraries

Reviewed by GPT-6 Astra. Preserve sheet access checks and omit all cell contents from native preview metadata.
Paul Campbell pushed by codex agent, operated by paul@keymail.dev 72464f76d2ed5165b560523dec49e6b838da815f parent 52c424f
2 files changed, +32 −1
  • internal/sheets/handlers_sheets.go +11 −1
  • internal/sheets/native_test.go +21 −0

CI passed — run details

diff --git a/internal/sheets/handlers_sheets.go b/internal/sheets/handlers_sheets.go
index ad5ad1b..b96ff90 100644
--- a/internal/sheets/handlers_sheets.go
+++ b/internal/sheets/handlers_sheets.go
@@ -101,7 +101,17 @@ func (a *App) renderSheetList(w http.ResponseWriter, r *http.Request, status int
return
}
if wantsNative(r) {
- nativeJSON(w, status, map[string]any{"Sheets": list})
+ // Reuse the web preview's shape-only query; never decrypt cells or
+ // return HTML to a native renderer.
+ type nativeSheetRow struct {
+ SheetSummary
+ Preview preview
+ }
+ rows := make([]nativeSheetRow, 0, len(list))
+ for _, sheet := range list {
+ rows = append(rows, nativeSheetRow{SheetSummary: sheet, Preview: a.buildPreview(sheet.TabID)})
+ }
+ nativeJSON(w, status, map[string]any{"Sheets": rows})
return
}
rows := make([]sheetRow, 0, len(list))
diff --git a/internal/sheets/native_test.go b/internal/sheets/native_test.go
index 5629478..298b440 100644
--- a/internal/sheets/native_test.go
+++ b/internal/sheets/native_test.go
@@ -33,6 +33,27 @@ func TestNativeSheetPayloadUsesExistingPermissions(t *testing.T) {
t.Fatal("anonymous native sheet read succeeded")
}
}
+func TestNativeListPreviewContainsShapeWithoutCellContents(t *testing.T) {
+ a, cookies := signIn(t)
+ _, cells := newSheet(t, a, cookies, "Workbook")
+ secret := "private-cell-preview-sentinel"
+ postCell(t, a, cookies, cells, `{"r":0,"c":0,"raw":"`+secret+`"}`)
+ rec := do(t, a, as(nativeGet("/sheets"), cookies))
+ if rec.Code != 200 || strings.Contains(rec.Body.String(), secret) {
+ t.Fatal("native preview leaked a cell or failed")
+ }
+ var payload struct{ Sheets []struct{ Preview preview } }
+ if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
+ t.Fatal(err)
+ }
+ if len(payload.Sheets) != 1 || payload.Sheets[0].Preview.Empty || len(payload.Sheets[0].Preview.Cells) != 1 {
+ t.Fatal("native preview did not include the real cell shape")
+ }
+ if rec := do(t, a, nativeGet("/sheets")); rec.Code == 200 {
+ t.Fatal("unsigned list exposed previews")
+ }
+}
+
func TestNativeCreateAndHistoryCommandsAnswerJSON(t *testing.T) {
a, cookies := signIn(t)
r := postForm("/sheets", map[string][]string{"title": {"Created natively"}})