Security review fixes: admin permissions, guest sessions, payment edge cases, assistant leaks
- Admin: refund/cancel/mark-resolved/Bazaar-link actions need change permission; question and curriculum import need add/delete; content browser needs view_question; guest device ids are shown to superusers only. - Accounts: registering a guest's phone revokes the guest's earlier sessions; self-deletion keeps Redemption, UserOffer and the phone's OTP rows; the public demo guest cannot be deleted (DEMO_GUEST_DEVICE_IDS); display-name contact filter covers any separator and social handles. - Payment: callback without Authority is 400; a second Bazaar purchase on a paid order is 409; orders snapshot credits/hearts/days at creation (payment.0011). - Assistant: a broken diagram no longer makes a streamed answer free; a late off-topic marker refuses the answer (not stored, not cached); a disconnect saves the partial answer; only KNOWN_COLLECTIONS are searched. - Infra: secret-key file is created atomically (workers no longer split keys); no default INTERNAL_SERVICE_KEY; Kavenegar key and Bazaar token kept out of error logs; Swagger/ReDoc pinned; schema throttled; docker compose bound to 127.0.0.1; non-ASCII digit ids are 400; ticket text capped at 5000. - docs/security-before-production.md: remaining open items 24-31 and the guard tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -402,7 +402,15 @@ def stream_answer(question: str, context_chunks: list[dict] | None,
|
||||
if not clean:
|
||||
continue
|
||||
if released:
|
||||
yield clean.replace(guardrails.OFF_TOPIC_MARKER, '')
|
||||
if guardrails.OFF_TOPIC_MARKER in clean:
|
||||
# علامت دیر آمد: بخشِ پیش از آن رفته است، ولی بقیه نمیرود و جواب «ردشده» حساب میشود
|
||||
# تا نه ذخیره شود نه در کش برود (پیشتر کاربرِ بعدی همین متن را از کش میگرفت).
|
||||
head = clean.split(guardrails.OFF_TOPIC_MARKER, 1)[0]
|
||||
if head.strip():
|
||||
yield head
|
||||
refused = True
|
||||
continue
|
||||
yield clean
|
||||
continue
|
||||
held += clean
|
||||
if guardrails.OFF_TOPIC_MARKER in held:
|
||||
@@ -420,7 +428,7 @@ def stream_answer(question: str, context_chunks: list[dict] | None,
|
||||
result['diagram'] = None if refused else diagram_mod.fits(
|
||||
f'{question}\n{focus}\n{_previous_question(history)}', splitter.diagram)
|
||||
if refused:
|
||||
yield guardrails.REFUSAL
|
||||
yield ('\n\n' if released else '') + guardrails.REFUSAL
|
||||
return
|
||||
if not released and held.strip():
|
||||
yield held
|
||||
|
||||
Reference in New Issue
Block a user