Skip to content

fix: detect wrong-kind ClickHouse objects, restrict Tinybird stale-deployment cleanup - #35

Merged
Makisuo merged 2 commits into
mainfrom
security/partial-delete-fixes
May 8, 2026
Merged

Makisuo merged 2 commits into
mainfrom
security/partial-delete-fixes

Conversation

@Makisuo

@Makisuo Makisuo commented May 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Addresses two DeepSec HIGH_BUG findings.

other-schema-diff-kind-confusion

computeSchemaDiff previously fell through to the materialized-view presence check after looking up actual.get(table.name) without verifying that the actual object's kind matched the desired kind. A customer with a regular table named the same as a Maple MV would be reported as up_to_date, the apply path would skip creating the MV, and downstream aggregate / derived tables would never get populated while sync still reported success.

Added a new wrong_kind status to TableDiffEntry (and the matching ClickHouseTableDiffEntry schema in the domain HTTP types). Emitted when actualTable.kind !== table.kind. The apply path skips these with a clear "resolve manually" reason rather than auto-remediating.

other-destructive-cleanup

cleanupStaleDeployments filtered with !d.live && d.status !== \"live\", which matches in-flight states like deploying and data_ready and any unrecognised status string from a future Tinybird release. Combined with Effect.ignore on every DELETE, this could silently delete an active rollout.

Restricted the filter to known terminal-failed states (failed, error) and require live !== true as defense in depth. Replaced the silent Effect.ignore with Effect.tapError(Effect.logWarning) so delete failures surface in logs.

Out of scope (deferred)

  • Two-phase organization deletion (3 HIGH_BUG findings on OrganizationService.deleteOrganization). The current path purges local org-scoped tables before calling Clerk and is not transactional; if Clerk fails after the purge, data is gone but the upstream org persists. The fix needs a new deletion_pending column + DB migration + retryable cleanup job.

Test plan

  • 10 schema-diff tests pass (8 original + 2 new wrong_kind cases covering both directions: MV-where-table-found and table-where-MV-found).
  • 11/11 Tinybird cleanup-filter cases handled correctly: only failed/error (with live !== true) deleted; deploying, data_ready, deleting, unknown future statuses, and live: true deployments all preserved.
  • Web settings UI updated to render the new wrong_kind row ("Wrong kind: expected MV, found table — resolve manually").
  • bun turbo typecheck passes across all 18 packages.

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Makisuo and others added 2 commits May 8, 2026 23:13
…ployment cleanup

Addresses two DeepSec HIGH_BUG findings:

`other-schema-diff-kind-confusion` —
`computeSchemaDiff` previously fell through to the materialized-view
presence check after looking up `actual.get(table.name)` without
verifying that the actual object's kind matched the desired kind. A
customer with a regular table named the same as a Maple MV would be
reported as `up_to_date`, the apply path would skip creating the MV,
and downstream aggregate / derived tables would never get populated
while sync still reported success.

Add a new `wrong_kind` status to `TableDiffEntry` (and the matching
`ClickHouseTableDiffEntry` schema in the domain HTTP types). Emit it
when `actualTable.kind !== table.kind`. The apply path in
`OrgClickHouseSettingsService.applySchema` skips these with a clear
"resolve manually" reason rather than auto-remediating, since
dropping the customer's existing object is destructive.

`other-destructive-cleanup` —
`cleanupStaleDeployments` filtered with `!d.live && d.status !==
"live"`, which matches in-flight states like `deploying` and
`data_ready` and any unrecognised status string from a future
Tinybird release. Combined with `Effect.ignore` on every DELETE,
this could silently delete an active rollout.

Restrict the filter to known terminal-failed states (`failed`,
`error`) and require `live !== true` as defense in depth. Replace the
silent `Effect.ignore` with `Effect.tapError(Effect.logWarning)` so
delete failures surface in logs rather than disappearing.

Out of scope (deferred to a follow-up):
- Two-phase organization deletion. The current `OrganizationService.
  deleteOrganization` purges local org-scoped tables before calling
  Clerk and is not transactional; if Clerk fails after the purge,
  data is gone but the upstream org persists. The fix requires a new
  `deletion_pending` column on the orgs table, a DB migration, and a
  retryable cleanup job — significant scope vs the rest of this
  security batch.

Tests:
- 2 new cases in `packages/domain/src/clickhouse/diff.test.ts`
  covering wrong_kind in both directions (MV where table found, and
  table where MV found).
- All 234 apps/api tests continue to pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PR-#6 schema-diff change added a wrong_kind variant to
TableDiffEntry but the settings page consumed the union without
handling it, causing a typecheck failure (counts indexer + columnDrifts
narrowing).

Add `wrong_kind: 0` to the counts initializer and a render branch that
shows "Wrong kind: expected MV, found table — resolve manually" so an
operator sees what the apply path skipped.

Found by running `bun turbo typecheck` across the full monorepo as
part of post-merge verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Makisuo
Makisuo merged commit 1542e7b into main May 8, 2026
1 of 3 checks passed

This branch was previously deployed

1 inactive deployment
pr-preview — ff0cab04 Deployed May 8, 2026 by Makisuo via deploy-pr-preview #105
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant