Skip to content

fix(watchers,feeds): guard id-less source queries and cross-org entity_ids#1146

Merged
buremba merged 1 commit into
mainfrom
feat/watcher-feed-correctness
May 28, 2026
Merged

fix(watchers,feeds): guard id-less source queries and cross-org entity_ids#1146
buremba merged 1 commit into
mainfrom
feat/watcher-feed-correctness

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 28, 2026

Fixes two watcher/feed correctness bugs found while building the HN engagement watcher in the lobu-crm org.

Bug A — id-less source query silently skips the reaction

A watcher whose source SQL omits id (e.g. SELECT origin_id, payload_text FROM events) produces an empty content_ids in the signed window token, so complete_window reports content_linked: 0 and silently skips the reaction — no error, even though the agent received the rows and extracted a result. This cost hours to diagnose in prod.

Fix: new queryProjectsIdColumn() (AST inspection, fails open on parse errors) wired into validateWatcherConfig, so create/create_version reject an id-less source at save time with a clear message. SELECT * (the default source) still passes.

Bug B — feeds/watchers accept cross-org entity_ids

lobu-crm feeds were created with entity_ids: {54}, but entity 54 belongs to a different org — so synced events never link to a valid in-org entity. The create/update path never validated entity ownership.

Fix: new assertEntityIdsInOrg() (org-scoped existence check, binds via pgBigintArray to dodge the prod fetch_types:false array gotcha) wired into manage_feeds create/update and manage_watchers create_from_version.

Tests

Red→green reproducers for both bugs (unit + integration), plus regression coverage. All deterministic gates green: typecheck/unit/integration = 0.

Prod remediation (operator, post-deploy)

Repoint lobu-crm's cross-org feeds:

UPDATE feeds SET entity_ids = array_replace(entity_ids, 54, 1957), updated_at = NOW()
WHERE organization_id = (SELECT id FROM organization WHERE slug='lobu-crm') AND 54 = ANY(entity_ids);

Follow-up (out of scope)

handleCreate's scalar entity_id path adopts the looked-up entity's org rather than validating against request context — same class, deferred.


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

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Feeds now validate that specified entity IDs belong to the same organization, preventing cross-organization entity references.
    • Watchers now require source queries to explicitly project an ID column.
    • Watchers reject cloning to entities belonging to other organizations.
  • Tests

    • Added comprehensive test coverage for cross-organization entity validation in feeds and watchers.
    • Added unit test coverage for source query ID column projection validation.

Review Change Stack

…y_ids

Two silent correctness bugs found while building an HN engagement watcher
in the prod lobu-crm org.

BUG A — a watcher source query that omits `id` (e.g.
`SELECT origin_id, payload_text FROM events`) silently skips the reaction.
Watcher-mode aggregation keys every row by `row.id` and the signed
window_token only carries those ids, so an id-less source produces
content_count=0 → complete_window reports `content_linked: 0` and skips the
reaction even though the agent received the rows. No error surfaced anywhere.
Fix: validate at watcher create/create_version time that each source query
projects an `id` column (queryProjectsIdColumn, AST-based, fails open on
parse errors) and reject with a clear message otherwise.

BUG B — feeds/watchers accepted entity_ids belonging to another org, so
synced events linked to a non-existent in-org entity. Fix: assertEntityIdsInOrg
validates every entity_id is in the requesting org on create_feed/update_feed
and watcher create_from_version; the create_from_version name lookup is now
org-scoped too.

Tests: unit coverage for the id-projection predicate plus red→green
integration reproducers for both bugs (embedded Postgres).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR enforces two correctness constraints for feeds and watchers: watcher SQL sources must explicitly project an id column for proper aggregation, and entity_ids in both features must belong to the same organization as their parent resource to prevent cross-organization data leakage.

Changes

Cross-org validation and watcher source hardening

Layer / File(s) Summary
SQL id projection detection utility
packages/server/src/utils/execute-data-sources.ts, packages/server/src/__tests__/unit/watcher-source-id-projection.test.ts
New queryProjectsIdColumn function parses SQL using node-sql-parser and inspects the top-level projection to detect whether an id column is included. Handles *, bare id, qualified id, and AS id aliases, with "fail open" behavior returning true on parse failures to avoid blocking saves. Unit test suite validates positive cases (multiple projection patterns and CTEs), negative cases (missing id), and edge cases (unparseable SQL and watcher template placeholders).
Cross-org entity validation helper
packages/server/src/tools/admin/helpers/db-helpers.ts
New assertEntityIdsInOrg helper dedupes and filters input entity_ids, short-circuits on empty input, and queries the entities table using parameterized ANY(...) over bigint arrays to validate all ids belong to a specific organization. Throws with a joined list of offending ids on validation failure.
Feeds cross-org entity enforcement
packages/server/src/tools/admin/manage_feeds.ts, packages/server/src/__tests__/integration/feeds-cross-org-entity.test.ts
handleCreateFeed and handleUpdateFeed now invoke assertEntityIdsInOrg to validate entity_ids belong to the feed's organization. Validation failures return error responses without creating or updating rows. Integration test confirms rejection of cross-org entity_ids, successful creation with in-org ids, and non-mutation of stored entity_ids on failed update.
Watchers id projection and cross-org validation
packages/server/src/tools/admin/manage_watchers.ts, packages/server/src/__tests__/integration/watchers/source-id-and-cross-org.test.ts
validateWatcherConfig rejects source queries that do not project an id column via queryProjectsIdColumn, preventing silent downstream aggregation skipping. handleCreateFromVersion calls assertEntityIdsInOrg before cloning a watcher template to validate all entity_ids belong to the watcher's organization, and scopes entity-name lookup to organization-owned entities. Integration tests cover id projection rejection on create and createVersion, cross-org entity rejection on createFromVersion with DB leak verification, and successful watcher creation with valid sources and in-org entities.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • lobu-ai/lobu#756: Both PRs modify manage_watchers.ts's create_from_version path, with this PR adding cross-org entity_ids validation while the referenced PR adds lifecycle/audit event emission for the same flow.

Poem

🐰 Bouncing through the org boundaries,
Watchers now check their ids with care,
No more leaks from foreign lands—
Each entity stays with its kin,
Validation guards the garden fair!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: adding guards for id-less source queries and cross-org entity_ids in watchers and feeds.
Description check ✅ Passed The description provides a clear summary of both bugs, their fixes, test coverage details, and prod remediation steps. The Test plan section is missing but not filled out per template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/watcher-feed-correctness

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@buremba
Copy link
Copy Markdown
Member Author

buremba commented May 28, 2026

Review gate: make review BASE=origin/maintypecheck=0, unit=0, integration=0 (all green). Red→green reproducers confirmed for both bugs (cross-org feed create+update fail without the fix; id-less source rejected at save time). pi could not emit a verdict (Codex gpt-5.5 quota exhausted, infra flake — not a code signal). Diff manually verified. Merging.

@buremba buremba merged commit f04469a into main May 28, 2026
20 of 22 checks passed
@buremba buremba deleted the feat/watcher-feed-correctness branch May 28, 2026 22:28
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.

2 participants