killmesoonbaby044 opened a new issue, #424:
URL: https://github.com/apache/casbin-pycasbin/issues/424

   ### Description
   
   Calling `get_implicit_permissions_for_user()` (or its async counterpart) and 
then modifying the returned list elements **silently corrupts the enforcer's 
in-memory policy store**. Subsequent `enforce()` calls will produce incorrect 
results without any error or warning.
   
   ### Steps to Reproduce
   
   ```python
   import casbin
   
   e = casbin.Enforcer("model.conf", "policy.csv")
   # policy: p, admin, data1, read
   # policy: p, alice, data2, read
   # g, alice, admin
   
   perms = e.get_implicit_permissions_for_user("alice")
   # perms == [["admin", "data1", "read"], ["alice", "data2", "read"]]
   
   # Mutate the returned list — e.g. to transform/enrich data
   perms[0][2] = "write"
   
   # The enforcer's internal store is now corrupted:
   print(e.get_policy())
   # [["admin", "data1", "write"], ["alice", "data2", "read"]]  ← WRONG
   ```
   
   ### Root Cause
   
   The call chain is:
   
   ```
   get_implicit_permissions_for_user()
     → get_named_implicit_permissions_for_user()
         → get_named_permissions_for_user_in_domain()  [per role]
             → get_filtered_named_policy()
                 → iterates self.model["p"][ptype].policy
                   and appends matching rows directly (no copy)
   ```
   
   The critical lines in `enforcer.py`:
   
   ```python
   for role in roles:
       permissions = self.get_named_permissions_for_user_in_domain(ptype, role, 
...)
       res.extend(permissions)  # ← appends references, not copies
   return res
   ```
   
   `get_filtered_named_policy` yields the **same `list` objects** that are 
stored in `self.model["p"][ptype].policy`. They are not copied at any point in 
the chain. As a result, the returned list contains direct aliases into the 
enforcer's internal storage, and any in-place modification by the caller 
immediately corrupts it.
   
   This affects all related methods that go through the same chain:
   - `get_implicit_permissions_for_user()`
   - `get_named_implicit_permissions_for_user()`
   - `get_permissions_for_user()`
   - `get_filtered_policy()`
   
   ### Expected Behavior
   
   The returned list should be independent of the enforcer's internal state. 
Modifying it must not affect the enforcer.
   
   ### Proposed Fix
   
   The fix should be applied in `get_filtered_named_policy` in 
`casbin/model/policy.py`, so all callers are protected automatically. Change 
the filter loop to append copies of the matching rows instead of the rows 
themselves:
   
   ```python
   # Before
   return [rule for rule in assertion.policy if <filter condition>]
   
   # After
   return [rule[:] for rule in assertion.policy if <filter condition>]
   ```
   
   Using `rule[:]` (shallow slice copy) is sufficient because policy rows are 
`list[str]` — strings are immutable, so a one-level copy fully isolates the 
caller. `copy.deepcopy` is not needed and would add unnecessary overhead.
   
   Alternatively, the copy can be applied one level up in 
`get_named_implicit_permissions_for_user`:
   
   ```python
   for role in roles:
       permissions = self.get_named_permissions_for_user_in_domain(ptype, role, 
...)
       res.extend([p[:] for p in permissions])  # copy each row
   ```
   
   The `policy.py` level is preferable since it fixes all callers at once.
   
   ### Workaround (until fixed)
   
   Users can protect themselves by deep-copying the result immediately:
   
   ```python
   import copy
   perms = copy.deepcopy(e.get_implicit_permissions_for_user(user))
   ```
   
   Or, if transforming to typed objects, by never mutating the raw rows 
in-place:
   
   ```python
   # Safe — constructs new objects from raw data without touching the rows
   result = [MyDTO(sub=row[0], obj=row[1], act=row[2]) for row in raw]
   ```
   
   ### Environment
   
   - **pycasbin version:** v2.8.0
   - **Python version:** v3.10.12
   - **Async enforcer:** yes
   - **Async enforcer version:**  v1.17.0


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to