Skip to content

fix(knowledge): scope keyword detail/delete to the caller's tenant (cross-org IDOR)#68

Open
dmitrymaranik wants to merge 1 commit into
UnicomAI:mainfrom
dmitrymaranik:fix/keyword-cross-org-idor
Open

fix(knowledge): scope keyword detail/delete to the caller's tenant (cross-org IDOR)#68
dmitrymaranik wants to merge 1 commit into
UnicomAI:mainfrom
dmitrymaranik:fix/keyword-cross-org-idor

Conversation

@dmitrymaranik

Copy link
Copy Markdown

Summary

The keyword ("专名词") endpoints GET /v1/knowledge/keywords/detail and DELETE /v1/knowledge/keywords were not scoped to the caller's tenant (org_id + user_id), so any authenticated user could read or permanently delete another organization's keyword by its (small, enumerable) integer id. Their sibling endpoints already enforce the boundary — the list / repeat-check helpers use sqlopt.WithPermit(org, user), and the other /knowledge/* routes carry middleware.AuthKnowledge(...) — these two were the exception.

Root cause:

  • knowledge-service GetKnowledgeKeywordsDetail calls orm.GetKeywordsById(id) (scoped by id only) and never checks the Identity already on the request.
  • bff DeleteDocCategoryKeywords forwards only Id to DeleteKnowledgeKeywords, and the ORM DeleteKeywords is a bare-id Unscoped() (hard) delete.

Change

  • GetKnowledgeKeywordsDetail — after loading the keyword, verify it belongs to the caller (req.Identity.OrgId/UserId); otherwise return the existing not-found error. Root-cause fix for the read, using the Identity already present on the request.
  • bff DeleteDocCategoryKeywords — gate the delete on that now tenant-scoped detail lookup, so a caller can only delete a keyword that is their own (the delete RPC itself carries no Identity).

No behaviour change for legitimate same-tenant calls. 18 lines, no proto changes.

Defense-in-depth follow-up (not in this PR)

Thread the caller Identity into DeleteKnowledgeKeywordsReq and scope the ORM DeleteKeywords with WithPermit(org, user) (plus a RowsAffected == 0 → not-found check), and/or add middleware.AuthKnowledge(...) to the two routes — so the gRPC delete is safe independent of the bff.

Fixes #67


Reported and verified with Sectum AI — open-source multi-tenant isolation verification.

GET /v1/knowledge/keywords/detail and DELETE /v1/knowledge/keywords were not
scoped to the caller's org/user, so any authenticated user could read or
permanently delete another organization's KB keyword by its enumerable integer
id. The sibling list/repeat-check helpers already scope via WithPermit(org,user)
and the other /knowledge/* routes carry AuthKnowledge; these two were the
exception.

- GetKnowledgeKeywordsDetail: verify the loaded keyword belongs to the caller
  (req.Identity) before returning.
- bff DeleteDocCategoryKeywords: gate the delete on the now tenant-scoped detail
  lookup (the delete RPC carries no Identity).
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.

Cross-organization access on knowledge-base keyword detail/delete endpoints

1 participant