Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions packages/core/src/eventFilterMatches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@ import type { EventFilter } from "./schemas/eventFilter.js";
// If the values of the array are objects, then we are doing content filtering
// An example would be [{ $endsWith: ".png" }, { $startsWith: "images/" } ]
export function eventFilterMatches(payload: any, filter: EventFilter): boolean {
if (payload === undefined || payload === null) {
if (Object.entries(filter).length === 0) {
return true;
} else {
return false;
}
}
const safePayload = payload ?? {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 User-facing filter fix lacks a changeset

This changes published filter behavior, but the PR has no changeset. Repository guidance calls for one for user-facing package fixes.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.


for (const [patternKey, patternValue] of Object.entries(filter)) {
const payloadValue = payload[patternKey];
const payloadValue = safePayload[patternKey];
Comment on lines +8 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Missing nested keys appear to exist

For a nullish parent, safePayload makes $exists: false fail on keys like constructor. The fallback {} inherits those keys, so a missing child appears present.

Learn more

The matcher treats nullish payloads as an empty object so nested filters can evaluate missing children. Ordinary {} inherits keys such as constructor, toString, and __proto__. Reading one of those keys returns an inherited value rather than undefined, which makes $exists: false reject an absent child. The same lookup also affects a parent supplied as {}, but that case predates this change.

Example: With payload { user: null } and filter { user: { constructor: [{ $exists: false }] } }, the matcher returns false. No constructor child exists, so this filter must match.

Recommended fix: Resolve payloadValue using an own-property check before indexing safePayload, returning undefined for inherited keys. Cover nullish parents and ordinary empty objects with a regression test.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.


if (Array.isArray(patternValue)) {
if (patternValue.length === 0) {
Expand Down
14 changes: 14 additions & 0 deletions packages/core/test/eventFilterMatches.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ describe("eventFilterMatches", () => {
expect(eventFilterMatches(payload, filter)).toBe(true);
});

it("should return true for nested $exists: false when parent key is missing or null", () => {
const filter: EventFilter = {
user: {
email: [{ $exists: false }],
},
};

expect(eventFilterMatches({}, filter)).toBe(true);
expect(eventFilterMatches({ user: null }, filter)).toBe(true);
expect(eventFilterMatches({ user: {} }, filter)).toBe(true);
expect(eventFilterMatches({ user: { email: undefined } }, filter)).toBe(true);
expect(eventFilterMatches({ user: { email: "user@example.com" } }, filter)).toBe(false);
});

it("should return true when payload does match numeric condition", () => {
const payload = {
name: "John",
Expand Down