Topology dependency cross-tenant IDOR (unauthorized delete/rewrite of another tenant's data)
Maintainers usually reply within 1 day
@smartcalsystems-sketch is already working on this.
Since Oct 18, 2026.
- #6882 by @smartcalsystems-sketch — open
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
Research direction
Start with keep/topologies/topologies_service.py, especially get_dependency_by_id, validate_non_manual_exists, update_dependency, and delete_dependency, then inspect their callers in keep/api/routes/topology.py. Review existing topology API tests and run the relevant test suite; completion means same-tenant users can modify only dependencies belonging to their tenant, while cross-tenant update and delete requests are rejected. Note that linked PR #6882 already targets this issue, so coordinate before starting.
Written by the indexing model from the issue text.
Description
Affected versions: confirmed on commit 465e99152e7586fcbe4c2732213d31c5c28def4b (pyproject version 0.54.3).
Summary
Any authenticated user with the write:topology scope in one tenant can delete or arbitrarily modify another tenant's TopologyServiceDependency record by its numeric ID, with zero relationship between the caller's tenant and the record being touched. The authorization check that is supposed to gate these mutations is evaluated against attacker-controlled or mismatched inputs, so it never fires across the tenant boundary.
Details
TopologyServiceDependency rows are not themselves tenant-scoped; the only tenant association is indirect, through the TopologyService rows they reference by service_id / depends_on_service_id (keep/api/models/db/topology.py). Enforcement of that association is left entirely to validate_non_manual_exists, a helper that checks whether a non-manually-created service matching a given set of service IDs exists for a given tenant_id (keep/topologies/topologies_service.py:102-112).
The lookup used by update/delete, get_dependency_by_id (keep/topologies/topologies_service.py:441-445), takes no tenant_id parameter at all and fetches by primary key across every tenant:
@staticmethod
def get_dependency_by_id(_id: int, session: Session) -> TopologyServiceDependency:
return session.exec(
select(TopologyServiceDependency).where(TopologyServiceDependency.id == _id)
).first()
delete_dependency (keep/topologies/topologies_service.py:677-700, called from DELETE /topology/dependency/{dependency_id} in keep/api/routes/topology.py) fetches the record this way, then validates using the record's own (victim-owned) service_id / depends_on_service_id against the caller's tenant_id. Since the victim's service IDs never belong to the caller's tenant, validate_non_manual_exists always returns False for a cross-tenant target, so the exception that is supposed to block the operation never raises, and the delete proceeds unconditionally for any guessable/enumerable integer ID.
update_dependency (keep/topologies/topologies_service.py:637-675, called from PUT /topology/dependency) has the same lookup bug, but additionally validates against the request body's service_id / depends_on_service_id rather than the fetched record's. An attacker can supply their own (same-tenant, manual) service IDs there, pass the validate_non_manual_exists check entirely legitimately for their own tenant, and then overwrite a victim-tenant dependency's service_id, depends_on_service_id, and protocol fields with attacker-chosen values, repointing another tenant's topology edge at the attacker's own services.
Steps To Reproduce
(available upon request)
Impact
In a multi-tenant Keep deployment (the project's primary SaaS/self-hosted multi-org deployment model), any tenant can silently delete or corrupt any other tenant's service-topology dependency graph purely by guessing or incrementing small integer IDs, which are sequential and therefore trivial to enumerate. This breaks topology-driven incident correlation and alert routing for the victim tenant without their knowledge, and the rewrite path lets an attacker splice their own services into another tenant's topology view.
CWE-862 (Missing Authorization), CWE-284 (Improper Access Control). CVSS 3.1: AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N (7.1, High).
- Dominant language
- Python
- Stars
- 12.4k
- Forks
- 1.5k
- Avg merge
- 14h 30m
- Merged PRs (30d)
- 2
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from keephq/keep
-
[🐛 Bug]: ARQ Worker fails to connect to password-protected Redis Sentinel (missing sentinel_kwargs)Possibly taken @Amandot claimed this 6 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
keephq/keep#6886 · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
keephq/keep#6803 · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
keephq/keep#6572 · 2 comments ·
Maintainers usually reply within 1 day
-
[🐛 Bug]: Images trust the compromised emSign ECC Root CA - G3 (certifi 2024.12.14, node:20)Possibly taken @ausias-armesto claimed this 5 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 35/100
keephq/keep#6891 · 1 reaction ·
Maintainers usually reply within 1 day
-
[Bug]: Google Chat delivery has no HTTP timeout and ignores transient retry boundariesPossibly taken @cayo-aura claimed this 8 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 57/100
keephq/keep#6885 · 1 reaction ·
Maintainers usually reply within 1 day
Similar issues
-
filesystem: `file://C:/...` bucket_url is read as a UNC share named `C:` and silently lists no filesOpen
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
comp/desktop desktop/shell os/windows P3 tool/file type/bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
NousResearch/hermes-agent#145928 ·
Maintainers usually reply within 1 day
-
integration:coding-agents
Difficulty 1/5 Under an hour Newbie friendliness 83/100
vectorize-io/hindsight#6151 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
FormalLanguageConstrainedPathQuerying/FLPQ_Data#174 ·
Maintainers usually reply within 1 day
-
[VLM] granite_vision preset produces empty output and runs ~3× slower with Granite Vision 3.3-2B than with Granite Vision 4.1-4BPossibly taken @prassu017 claimed this today. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
docling-project/docling#5068 · 4 comments ·
Maintainers usually reply within 1 day