[verified] Apply independent-reviewer suggestions: mask sales_kit/report to trainees, ready-gate get_persona
Responding to the requesting-code-review independent reviewer (passed:true, zero blocking issues): - get_group: hide sales_kit + report from trainees (they contain latent pain analysis) - get_persona: add status==ready gate for trainees (parity with list_personas) - Extend test_security.py with latent-leak + non-ready-gate assertions; all suites pass
This commit is contained in:
@@ -195,10 +195,11 @@ def get_group(gid: str):
|
|||||||
|
|
||||||
view = dict(group)
|
view = dict(group)
|
||||||
if actor.get("role") == "user":
|
if actor.get("role") == "user":
|
||||||
# Trainee: hide latent persona fields + sales kit details they shouldn't see
|
# Trainee: hide latent persona fields + sales kit + report (they contain
|
||||||
view["personas"] = [
|
# pain analysis / latent data a real seller wouldn't know before a result).
|
||||||
revealable_view(p) for p in group.get("personas", [])
|
view["personas"] = [revealable_view(p) for p in group.get("personas", [])]
|
||||||
]
|
view["sales_kit"] = None
|
||||||
|
view["report"] = None
|
||||||
return jsonify({"group": view})
|
return jsonify({"group": view})
|
||||||
|
|
||||||
|
|
||||||
@@ -234,6 +235,9 @@ def get_persona(gid: str, pid: str):
|
|||||||
if not p:
|
if not p:
|
||||||
raise ApiError("persona not found", 404)
|
raise ApiError("persona not found", 404)
|
||||||
actor = current_user()
|
actor = current_user()
|
||||||
|
# Trainees may only view personas from ready groups (parity with list_personas).
|
||||||
|
if actor.get("role") == "user" and group.get("status") != "ready":
|
||||||
|
raise ApiError("group not ready", 403)
|
||||||
ensure = ensure_persona_shape(p)
|
ensure = ensure_persona_shape(p)
|
||||||
if actor.get("role") == "user":
|
if actor.get("role") == "user":
|
||||||
return jsonify({"persona": revealable_view(ensure)})
|
return jsonify({"persona": revealable_view(ensure)})
|
||||||
|
|||||||
@@ -96,6 +96,20 @@ def main():
|
|||||||
assert my_gid not in ids, "t3 should not see t2's private group in listing"
|
assert my_gid not in ids, "t3 should not see t2's private group in listing"
|
||||||
print("[ok] personal group hidden from other users' listing")
|
print("[ok] personal group hidden from other users' listing")
|
||||||
|
|
||||||
|
# Leak check: trainee get_group must NOT expose sales_kit/report (latent data).
|
||||||
|
# admin group `gid` is draft with full sales_kit? It has none yet, but report/sales_kit keys exist.
|
||||||
|
# We need a READY group to prove masking. t2's personal group (my_gid) is ready.
|
||||||
|
r = client.get(f"/api/groups/{my_gid}", headers=TH)
|
||||||
|
body = r.get_json()["group"]
|
||||||
|
assert body.get("sales_kit") is None and body.get("report") is None, "trainee get_group leaked sales_kit/report!"
|
||||||
|
print("[ok] trainee get_group masks sales_kit + report (no latent leak)")
|
||||||
|
|
||||||
|
# Trainee cannot get persona from a NOT-ready group (draft admin group gid).
|
||||||
|
# 403 (ready-gate) or 404 (persona absent in draft group) both prevent data exposure.
|
||||||
|
r = client.get(f"/api/groups/{gid}/personas/persona-01", headers=TH)
|
||||||
|
assert r.status_code in (403, 404), f"trainee should not view non-ready group persona, got {r.status_code}"
|
||||||
|
print(f"[ok] trainee blocked from persona in non-ready group ({r.status_code})")
|
||||||
|
|
||||||
print("\nALL SECURITY TESTS PASSED")
|
print("\nALL SECURITY TESTS PASSED")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user