Describe the bug
Extension::delete_notification() builds its WHERE clause with the source predicate commented out, so a call that passes only an $entry_key deletes matching entries across every source and every campaign — not just the calling extension's own.
https://github.com/WPDevelopers/notificationx/blob/master/includes/Extensions/Extension.php#L635-L649
public function delete_notification($entry_key = null, $nx_id = null) {
$where = [
// 'source' => $this->id // <-- commented out
];
if (!empty($entry_key)) {
$where['entry_key'] = $entry_key;
}
if (!empty($nx_id)) {
$where['nx_id'] = $nx_id;
}
if (!empty($where)) {
return Entries::get_instance()->delete_entries($where);
}
return false;
}
This matters because entry_key is not namespaced per source and is frequently a small integer. Existing callers pass a bare upstream ID:
WPComments.php:256 — comment ID, on delete_comment
WOOReviews.php:375 — comment ID
Tutor.php:317 — {$order_id}-{$item_id}
A WooCommerce order with ID 42 and a blog comment with ID 42 therefore share an entry_key. Deleting comment 42 removes the WooCommerce order-42 notification too.
The nx_entries schema has no unique constraint that would prevent this — only PRIMARY KEY (entry_id) plus non-unique KEY source / KEY nx_id (includes/Core/Database.php#L62-L73).
Steps to reproduce
- Set up two campaigns on one site: a WooCommerce Sales notification and a WordPress Comments notification.
- Let both accumulate entries, ensuring a WooCommerce order and a comment happen to share the same numeric ID (on a fresh site this is close to guaranteed for low IDs).
- Delete that comment in WP Admin → Comments. This fires
delete_comment → WPComments::delete_notification($comment_id).
- Inspect
{$prefix}nx_entries, or view the WooCommerce campaign's entries.
Expected: only the WP Comments entry with that entry_key is removed.
Actual: the WooCommerce entry with the same entry_key is removed as well, silently. The order notification stops displaying with no indication why.
Suggested fix
Uncomment the predicate:
$where = [
'source' => $this->id,
];
Every current caller is a method on the extension that owns the entry, so scoping by $this->id should be safe. Worth confirming against the Pro plugin's callers too.
A defensive alternative — or an addition — would be to namespace entry_key per source at write time, but that needs a migration for existing rows.
Impact
Silent, cross-campaign data loss. No error, no log entry — the notification simply disappears. Sites running several integrations at once are the most exposed, and low-numbered IDs on newer sites make collisions more likely rather than less.
Environment
Found on master @ 55f62f10 while building a new integration whose entry_key is a booking ID from a custom table — i.e. exactly the collision-prone shape.
Describe the bug
Extension::delete_notification()builds itsWHEREclause with thesourcepredicate commented out, so a call that passes only an$entry_keydeletes matching entries across every source and every campaign — not just the calling extension's own.https://github.com/WPDevelopers/notificationx/blob/master/includes/Extensions/Extension.php#L635-L649
This matters because
entry_keyis not namespaced per source and is frequently a small integer. Existing callers pass a bare upstream ID:WPComments.php:256— comment ID, ondelete_commentWOOReviews.php:375— comment IDTutor.php:317—{$order_id}-{$item_id}A WooCommerce order with ID
42and a blog comment with ID42therefore share anentry_key. Deleting comment 42 removes the WooCommerce order-42 notification too.The
nx_entriesschema has no unique constraint that would prevent this — onlyPRIMARY KEY (entry_id)plus non-uniqueKEY source/KEY nx_id(includes/Core/Database.php#L62-L73).Steps to reproduce
delete_comment→WPComments::delete_notification($comment_id).{$prefix}nx_entries, or view the WooCommerce campaign's entries.Expected: only the WP Comments entry with that
entry_keyis removed.Actual: the WooCommerce entry with the same
entry_keyis removed as well, silently. The order notification stops displaying with no indication why.Suggested fix
Uncomment the predicate:
Every current caller is a method on the extension that owns the entry, so scoping by
$this->idshould be safe. Worth confirming against the Pro plugin's callers too.A defensive alternative — or an addition — would be to namespace
entry_keyper source at write time, but that needs a migration for existing rows.Impact
Silent, cross-campaign data loss. No error, no log entry — the notification simply disappears. Sites running several integrations at once are the most exposed, and low-numbered IDs on newer sites make collisions more likely rather than less.
Environment
Found on
master@55f62f10while building a new integration whoseentry_keyis a booking ID from a custom table — i.e. exactly the collision-prone shape.