Health: public gets only ok/error; details need X-Internal-Key. Docs/schema: admin session only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
32 KiB
پیش از پروداکشنِ واقعی: چیزهایی که عمداً باز ماندهاند
آخرین بازبینی با کد: ۲۰۲۶-۱۰-۰۷ (۱۵ مهر ۱۴۰۵)؛ با سرورِ زنده: ۲۰۲۶-۱۰-۰۴ برای بقیهی بندها؛ آنچه ۱۰-۰۷ دیپلو شد از بیرون و با آزمونِ زنده سنجیده شد: کلیدِ امضا، سقفِ نرخ، پایانِ جزیره/درس، سلامت.
این پروژه تا این تاریخ در حالتِ دمو و ارائهی کارآموزی است، نه محصولِ نهایی. چند چیز عمداً باز گذاشته شده تا توسعهدهندهها، فرانتکار و ارائه کار کنند. هر کدام برای یک سرورِ عمومی با کاربرِ واقعی (کودک، پرداختِ واقعی) خطر است و باید پیش از آمدنِ اولین کاربرِ واقعی بسته شود.
برای agentها و توسعهدهندههای بعدی: این فهرست ادعاست، نه ضمانت؛ ممکن است از این تاریخ به بعد چیزی عوض شده باشد. پیش از بستنِ هر مورد، وضعیتِ فعلیاش را با خودِ سرور یا کد بسنجید (ستونِ «چطور بسنجید»). مورد که بسته شد، از فهرست برداشته میشود و یک خط در «حفاظهایی که هست» (پایینِ همین فایل) میگیرد، با نامِ تستی که آن را نگه میدارد. تاریخ و دلیلِ تغییر در پیامِ کامیت است، نه اینجا.
هر آزمونِ «از بیرون» با curl بدونِ توکن انجام شده و در تاریخِ بالا تأیید شده است.
خلاصه، به ترتیبِ خطر
| # | مورد | خطر | کارِ کیست |
|---|---|---|---|
| ۲ | ۷ حسابِ دمو با کدِ ورودِ ثابت روی پروداکشن | بالا | .env سرور |
| ۳ | دادهی دمو روی دیتابیسِ پروداکشن | بالا | دیتابیسِ سرور |
| ۷ | پنلِ /admin/ روی دامنهی عمومی |
متوسط | nginx + کد |
| ۸ | سرور بدونِ ArvanCloud روی پورتِ ۸۰ هم جواب میدهد | متوسط | ادمینِ سرور |
| ۹ | کشِ مشترک بینِ workerها نیست (Redisِ اختصاصی)؛ سقفهای مبتنی بر کش ≈ ۳ برابرند | متوسط | تصمیم + ساختِ Redis |
| ۱۱ | کدِ OTP بیهش در دیتابیس و در فهرستِ پنل | کم | کد |
| ۱۲ | هزینهی دستیارِ AI با ساختِ شمارهی تازه (هر شماره ۳ سوالِ رایگان) قابلِ بالا بردن است | کم (مالی) | کد |
| ۱۴ | SQLite روی دیسکِ مشترکِ پُر؛ بکاپِ پیش از migrate خودکار است ولی بیرون از ماشین نیست | کم (داده) | زیرساخت |
| ۱۵ | دسترسیِ سرور: حسابِ مشترک، رمز، sudoِ کامل | بالا | ادمینِ سرور |
| ۱۶ | فرانت توکن را در localStorage نگه میدارد |
کم | فرانت |
| ۲۲ | سایتِ فرانت هیچ هدرِ امنیتی ندارد (CSP، frame-ancestors، HSTS) | کم | nginx/فرانت |
| ۲۴ | سقفِ روزانهی ۳۰۰ پیامکِ کلِ سامانه را یک ناشناس پر میکند و ورود با پیامک برای همه بسته میشود | بالا (دسترسپذیری، هزینه) | تصمیمِ طراحی |
| ۲۵ | با دانستنِ شمارهی کسی، ورودِ او تا ۲۴ ساعت بسته میشود | متوسط | تصمیمِ طراحی |
| ۲۷ | کلیدِ جوابِ هر سوال با یک حسابِ دوم درمیآید؛ امتیازِ هر جوابِ درست دو بار داده میشود | کم | تصمیمِ محصولی |
| ۲۸ | استفادهی دوباره از توکنِ تمدیدِ دزدیدهشده تشخیص داده نمیشود؛ «خروج از همهی دستگاهها» نیست | کم | کد، بعد از ارائه |
| ۲۹ | پیشنهادِ ثبتنام با حذفِ حساب و ورودِ دوباره با همان شماره دوباره داده میشود | کم | کد |
| ۳۱ | لپتاپِ توسعه با mobile-preview روی شبکهی محلی باز است و در DEBUG کدِ 0000 را میپذیرد |
کم | لپتاپِ توسعه |
۲. حسابهای دمو با کدِ ورودِ ثابت (TEST_LOGIN_PHONES)
- چیست: ۷ شمارهی
09120000101تا09120000107با کدِ ثابت در.envسرور. شمارهها در ریپو (docs/operations.md،CLAUDE.md) آمدهاند؛ کدها فقط در راهنمای ارائهی صاحبِ پروژهاند. - خطر: کسی که کد را از راهنما، اسکرینشات یا ارائه ببیند وارد آن حسابها میشود. این
کدها با
OTP_MAX_FAILED_ATTEMPTSباطل نمیشوند (ردیفِOTPCodeندارند)؛ فقط سقفِ per-workerِ تلاشِ ورود جلویشان است. - رفع:
TEST_LOGIN_PHONESرا از.envسرور پاک کنید و ریاستارت کنید. - چطور بسنجید:
/api/healthدیگر هشدارِ «شمارههای تستِ ورود با کدِ ثابت فعالاند» ندارد.
۳. دادهی دمو روی دیتابیسِ پروداکشن
- چیست: کاربرهای ۱۰ تا ۱۵ و ۲۵ (حسابهای دمو با کدِ ثابت، بند ۲) با درس، گفتگو، تیکت و سفارش روی دیتابیسِ پروداکشن ساخته شدهاند. حسابِ ۱۰ («علی رضایی»، با ۷ سفارش) حسابِ نمایشیِ اصلی است.
- خطر: دادهی ساختگی در آمار و جدولِ رتبهبندیِ واقعی.
- رفع: پیش از کاربرِ واقعی با
delete_users --ids … --yes(پیش از آن بکاپ) پاک شوند.purge_demo_dataاینها را نمیگیرد (از مسیرِ واقعیِ viewها ساخته شدهاند). - چطور بسنجید:
/api/health→traffic.usersفقط کاربرانِ واقعی را بشمارد.
۷. پنلِ ادمین روی دامنهی عمومی
- چیست:
/admin/رویmathapi.tadrisino2.ir، با ورودِ نامکاربری/رمزِ جنگو. سقفِ ۱۰ تلاش در ۱۵ دقیقه برای هر IP دارد (AdminLoginThrottleMiddleware)، ولی: شمارش per-worker است (مورد ۹)، IP با XFF جعلپذیر است (مورد ۸)، و ۲FA ندارد. - خطر: ادمین همهی محتوا، پلنها، کدهای تخفیف، سفارشها و کاربرها را کنترل میکند.
- رفع: محدود کردنِ
/admin/در nginx به IPهای تیم یا VPN؛ تغییرِ مسیرِ پیشفرض؛ ۲FA؛ رمزهای قوی و حسابِ staffِ جدا برای هر نفر.
۸. دسترسیِ مستقیم به پورتِ ۸۰ (دور زدنِ ArvanCloud)
-
چیست:
http://78.38.35.249/api/healthباHost: mathapi.tadrisino2.irپاسخ میدهد (تأیید: HTTP 200).NUM_PROXIES=2فرض میکند همیشه دو پروکسی جلوی ماست. -
خطر: کسی که IP را بداند
X-Forwarded-Forرا جعل میکند و محدودیتهای IP-محور (OTP، ادمین) را دور میزند. -
رفع (ادمینِ سرور): پورتِ ۸۰/۴۴۳ فقط برای رنجهای ArvanCloud، یا
set_real_ip_from+real_ip_headerدر nginx. -
بازتولید شد ۲۰۲۶-۱۰-۰۴:
curl -H 'Host: mathapi.tadrisino2.ir' -H 'X-Forwarded-For: 1.2.3.4' http://78.38.35.249/api/healthجواب دادclientIp: 1.2.3.4. یعنی هر سقفِ IP-محور (ورودِ ادمین ۱۰ در ۱۵ دقیقه، درخواست و ورودِ OTP) با عوض کردنِ این هدر در هر درخواست دور زده میشود، و حفاظتِ DDoS/WAFِ آروان هم برای این مسیر وجود ندارد. سقفهای per-phone و per-code که در دیتابیس شمرده میشوند متأثر نیستند. پورتهای ۸۰ و ۴۴۳ هر دو باز بودند.
۹. کشِ مشترک بینِ workerها نیست
- چیست:
CACHE_URLروی سرور خالی است و کشLocMemCacheاست؛ ۳ worker هرکدام جدا میشمارند، پس سقفهای مبتنی بر کش (throttleهای DRF، پنجرهی ساعتیِ OTP، سقفِ ورودِ ادمین) ≈ ۳ برابرِ عددِ تنظیمشدهاند و با ریاستارت صفر میشوند. سقفهای دیتابیسی (cooldown و سقفِ روزانهی پیامک، حدسِ کد) درستاند. - چرا باز مانده: Redisِ روی ماشین مالِ تیمهای دیگر است و تصمیمِ مالک (۲۰۲۶-۱۰-۰۷) این است که به آن، یا به هیچ سرویسِ دیگرانی، وصل نشویم.
- رفع: Redisِ اختصاصیِ خودمان (نمونهی جدا، فقط localhost، با رمز؛ ساختنش با مدیرِ سرور یا یک سرویسِ بیرونی)،
بعد
CACHE_URL. کد آماده است:config/cache.py(قطعیِ Redis API را ۵۰۰ نمیکند). - چطور بسنجید:
/api/healthبایدLocMemCacheنگوید و هشدارِ کش نداشته باشد؛ یک رگبارِ موازی روی یک مسیرِ throttleدار باید حدودِ یکبارِ سقف (نه سه برابر) بپذیرد.
۱۱. کدِ OTP بیهش در دیتابیس
- چیست:
OTPCode.codeمتنِ ساده است و در فهرستِ پنلِ ادمین (OTPCodeAdmin) دیده میشود. توکنِ تمدید (Session.refresh_token) هششده است و اینجا شامل نیست. - چرا باز مانده: تیم برای آزمایش بیپیامک یک ردیفِ
OTPCodeدر پنل میسازد و کدش را میبیند. - خطر: کسی که پنل یا دیتابیس را ببیند، در چند دقیقهی عمرِ هر کد میتواند با آن وارد شود.
- رفع: ذخیرهی هش و حذفِ ستون از فهرستِ پنل، همراه با راهِ دیگری برای ورودِ آزمایشی.
۱۲. هزینهی دستیارِ AI
- چیست: هر کاربر روزی ۳ سؤالِ رایگان دارد (
AI_DAILY_QUOTA_FREE) و بعد فقط با اعتبارِ خریدهشده؛ ۲۰ در ساعت. هر شمارهی تازه ۳ سؤالِ رایگان میگیرد و ساختنِ شماره با سقفِ پیامک (بند ۲۴) محدود است. سقفِ روزانهی کلِ سامانه (AI_GLOBAL_DAILY_MAX، ۳۰۰۰) خرجِ کل را محدود میکند. طولِ سؤال حداکثر ۲۰۰۰ نویسه است. - خطر: با چند IP، اعتبارِ GapGPT در چند ساعت تمام میشود (اعتبار یک بار در ۲۰۲۶-۰۹-۲۵ به صفر رسید، البته به دلیلِ دیگر).
- رفع: سقفِ سراسریِ روزانه برای کلِ اپ؛ دستیار فقط برای کاربرِ واردشده با شماره یا
با سقفِ بسیار کمتر برای مهمان؛ هشدار وقتی
aiProvider.stateدر/api/healthبرابرِquota_exhaustedشد.
۱۴. دیتابیس و بکاپ
- چیست: SQLite در
~/mathapp-v2/db.sqlite3، روی دیسکی که بینِ چند سرویس مشترک است و ~۸۳٪ پر است.transaction_mode: IMMEDIATEهمزمانی را درست ولی سریالی میکند.deploy.shپیش ازmigrateبکاپ میگیرد (.deploy-backups/db-before-deploy-*، ۲ تای آخر؛ اگر بکاپ نشد، migrate اجرا نمیشود)، ولی همه در همان پوشه و روی همان دیسکاند. روی لپتاپ و در دیپلوی ۲۰۲۶-۱۰-۰۷ روی سرور آزموده شد (ساخت وintegrity_check؛ حذفِ قدیمیها فقط روی لپتاپ). دیتابیسِ لپتاپ ۱۲۶ مگابایت است و دو بکاپ ~۲۵۰ مگابایت میگیرند؛ پیش از اولین دیپلوdf -h /را ببینید. - خطر: از دست رفتنِ کلِ داده با یک خطای دیسک یا یک دستورِ اشتباه؛ پُر شدنِ دیسک سرویسهای دیگر (Gitea، ایمیل) را هم میخواباند.
- رفع: مهاجرت به Postgres (روی همان ماشین نصب است؛ بعدش بکاپِ
deploy.shبایدpg_dumpشود، الان فقط هشدار میدهد) و نگهداریِ یک نسخه بیرون از ماشین.
۱۵. دسترسیِ سرور
- چیست: ورود با رمز به حسابِ مشترکِ
administratorباsudo (ALL : ALL) ALLو عضوِdockerوlxd، روی ماشینی که Gitea و ایمیلِ شرکت هم رویش است. رمز یک بار در گفتگو با agent گذاشته شده و روی دسکتاپِ صاحبِ پروژه در یک فایلِ متنی نگه داشته میشود. - خطر: هر نشتِ رمز یعنی دسترسیِ root به کلِ ماشینِ شرکت، نه فقط این پروژه.
۲۰۲۶-۱۰-۰۴:
PasswordAuthentication yesدرsshd_configو پورتِ ۲۲ از اینترنت باز است؛ پس رمز از هر جای اینترنت حدسزدنی است (fail2ban سنجیده نشد). - رفع (ادمینِ سرور): کلیدِ SSH بهجای رمز، تعویضِ رمز، حسابِ جدا برای این پروژه با
دسترسیِ فقط به
~/mathapp-v2و یک خطِNOPASSWDفقط برایsystemctl restart zareian-mathapp؛ پاک کردنِ فایلِ رمز از دسکتاپ (Keychain جایگزین است).
۱۶. نگهداریِ توکن در فرانت
- چیست: کلاینتِ فرانت (
src/api/index.tsدر مخزنِ فرانت) توکن و refresh را درlocalStorageنگه میدارد. - خطر: هر XSS در سایت هر دو را میخواند.
- رفع: CSPِ سختگیرانه روی
math.tadrisino2.ir؛ در بلندمدت refresh در کوکیِHttpOnly.
۲۲. هدرهای امنیتیِ فرانت
- چیست:
https://math.tadrisino2.ir/فقطx-xss-protectionدارد؛ نه CSP، نهX-Frame-Options/frame-ancestors، نه HSTS، نهX-Content-Type-Options. API همهی اینها را جز HSTS و CSP دارد. - خطر: صفحهی اپ را میشود در iframeِ سایتِ دیگری گذاشت (clickjacking)؛ و چون توکن در
localStorageاست (مورد ۱۶)، نبودِ CSP اثرِ هر XSSِ احتمالی را کامل میکند. در خودِ فرانت سینکِ XSS پیدا نشد (تنهاdangerouslySetInnerHTMLروی SVGِ ثابتِ شکلکهاست). - رفع: در nginxِ
mathapp-frontیا در آروان:Content-Security-Policyباframe-ancestors 'none'وconnect-srcبه دامنهی API، وX-Content-Type-Options: nosniff.
۲۴. پر کردنِ سقفِ سراسریِ پیامک
- چیست:
OTP_SMS_GLOBAL_DAILY_MAX(۳۰۰ در ۲۴ ساعت) جلوی خرجِ بیسقفِ پیامک را میگیرد. هر ناشناسی با درخواستِ کد برای شمارههای تصادفی آن را پر میکند؛ از یک IP بیجعل ساعتی ۲۰ درخواست، یعنی روزی ۴۸۰، پس یک نفر سقف را همیشه پر نگه میدارد. - بازتولید شد: ۳۰۰ درخواست پذیرفته و ۳۰۰ پیامک فرستاده شد؛ بعد درخواستِ کاربرِ واقعی ۵۰۳.
- خطر: هیچ ثبتنامِ تازه، ورودِ دوباره یا حذفِ حسابی ممکن نیست؛ هزینهی ۳۰۰ پیامک در روز.
- رفع (تصمیمِ طراحی): بعد از گذشتن از یک حد، CAPTCHA یا مانعِ مشابه پیش از ارسال؛ شمارشِ سقف بر اساسِ IP/شبکه؛ هشدار در نیمهی سقف.
۲۵. بستنِ ورودِ یک شمارهی مشخص
- چیست: ۱۰ درخواستِ کد در ~۲ ساعت سقفِ روزانهی آن شماره را پر میکند (صاحبش تا ۲۴ ساعت کد نمیگیرد)، و ۵ حدسِ غلطِ هر کسی کدِ تازهی او را باطل میکند. هر دو بازتولید شد.
- رفع (تصمیمِ طراحی): کد به درخواستکننده گره بخورد (درخواستِ کد یک شناسهی موقت برگرداند که ورود لازمش دارد) و حدسِ غلط برای هر درخواستکننده جدا شمرده شود.
۲۷. کلیدِ جواب با حسابِ دوم، امتیازِ دوباره
- چیست: جوابِ غلط
correctOptionIdرا برمیگرداند و برای همه یکی است؛ پایانِ درس و پایانِ جزیره هر کدام جدا امتیاز میدهند. یک مهمانِ دورریختنی کلید را درمیآورد و حسابِ اصلی همه را درست میزند (۴ سوال، ۸۰ امتیاز). رتبهبندی و آمارِ درصدِ غلط آلوده میشوند. - رفع (تصمیمِ محصولی): امتیازِ هر سوال یک بار؛ گزینهی درست فقط بعد از پایانِ آزمون.
۲۸. توکنِ تمدیدِ دزدیدهشده
- چیست: استفادهی دوباره از یک توکنِ تمدیدِ باطلشده فقط ۴۰۱ میگیرد؛ اگر دزد اول تمدید کند، صاحبِ حساب بیرون میافتد و دزد میماند. «خروج از همهی دستگاهها» هم نیست.
- چرا باز مانده: باطل کردنِ کلِ زنجیره با استفادهی دوباره، حسابهای دمو را که در ارائه چند نفر همزمان با آنها کار میکنند بیرون میاندازد.
- رفع: شناسهی خانواده روی
Session؛ با استفادهی دوباره کلِ خانواده باطل شود؛ گزینهی خروج از همهجا.
۲۹. پیشنهادِ ثبتنام بعد از حذفِ حساب
- چیست: پیشنهادِ خودکارِ ثبتنام به
user_idگره خورده است؛ حذفِ حساب و ورودِ دوباره با همان شماره یک کاربرِ تازه میسازد و پیشنهاد دوباره داده میشود. فقط وقتی اثر دارد که یک Offerِ ثبتنام فعال باشد. - رفع: گره زدنِ پیشنهادِ ثبتنام به هشِ شماره.
۳۰. مسیرهای بیthrottle
- وضع: با پیمایشِ resolverِ جنگو هر مسیرِ
/api/کهthrottle_scopeنداشت فهرست شد. پانزده مسیر سقف گرفتند (جدولِ حفاظها:content/tests_read_throttles.py). دو مسیر عمداً ماندند چون باX-Internal-Keyمحافظت میشوند و عمومی نیستند:rag/chunksوpayment/orders(POST)./api/docs/و/api/redoc/هم سقفِ جدا ندارند (صفحهی ایستا؛ بند ۵). - نسنجیده: رفتارِ سقفِ
read(۱۲۰ در دقیقه) روی ترافیکِ واقعی؛ پشتِ IPِ مشترکِ مدرسه یا CGNAT ممکن است کاربرِ عادی هم به آن برسد (به بندِ «نرخهای throttle» در چکلیستِ لانچ نگاه کنید).
۳۱. لپتاپِ توسعه روی شبکهی محلی
- چیست:
mobile-preview/serve.pyعمداً روی کلِ شبکه گوش میدهد (برای گوشیِ روی همان وایفای) و/api/را به runserverِ محلی میفرستد؛ در DEBUG کدِ0000هر شمارهای را وارد میکند و کلیدِ GapGPTِ لپتاپ همان حسابِ سرور است. (docker composeفقط روی127.0.0.1باز است و کلیدِ سرویسِ داخلی دیگر پیشفرض ندارد.) - رفع: روی شبکهی عمومی (کافه، مدرسه) اجرا نشود؛ برای توسعه کلیدهای جدای GapGPT و Qdrant.
حفاظهایی که هست (تا دوباره باز نشوند)
هر تغییری در این مسیرها باید تستهای نامبرده را سبز نگه دارد. جزئیاتِ پرداخت و تخفیف در
docs/architecture.md.
| حفاظ | تست |
|---|---|
ورودِ مهمان وجود ندارد؛ مدلِ User ستونِ is_guest/device_id ندارد و migrationِ حذفشان تا مهمانی مانده باشد میشکند |
accounts/migrations/0008_remove_guest_login.py |
| پلنِ هر خرید روی سفارش؛ خرید اشتراکِ فعال را دست نمیزند؛ یک کدِ تخفیف پیش از درگاه؛ امتیاز فقط از جوابِ ثبتشده | payment/tests_audit_fixes.py، accounts/tests_audit_fixes.py |
لینکِ پرداخت (/api/payment/orders/{id}/pay) فقط با امضای کوتاهعمرِ همراهِ paymentUrl باز میشود |
payment/tests.py (PaymentRedirectViewTests) |
| توکنِ تمدید فقط بهصورتِ هشِ SHA-256 ذخیره میشود | accounts/tests.py (RefreshTokenStoredHashedTests) |
/api/health بیکلید فقط ok/error میدهد؛ جزئیات با هدرِ X-Internal-Key (کلید در .env سرور). بیکلیدِ تنظیمشده هیچکس جزئیات نمیبیند |
config/tests.py (HealthIsMinimalForThePublicTests) |
Swagger، ReDoc و schema فقط برای ادمینِ واردشده به /admin/ |
config/tests.py (ApiDocsRtlShellTests.test_docs_are_closed_to_anonymous_visitors) |
| جدولِ رتبهبندی فقط برای کاربرِ واردشده | progress/tests.py (LeaderboardPeriodValidationTests) |
| شمارهی موبایل نرمال میشود؛ cooldown، سقفِ روزانهی هر شماره و سقفِ کلِ پیامک در دیتابیس شمرده میشوند | accounts/tests_otp_limits.py |
| OTPِ ۶ رقمی با ۵ حدس برای هر کد؛ خروج از حساب توکنهای قبلی را باطل میکند | accounts/tests_audit_fixes.py |
| قلبِ صفر جوابِ سوال را میبندد؛ چکش داخلِ تراکنش است (بیجوابماندن و جزیرهی قفل جداگانه در ردیفِ بعدی بسته شدهاند) | content/tests_hearts_gate.py |
select_for_update مؤثر با transaction_mode: IMMEDIATE؛ مقایسهی زمانثابتِ X-Internal-Key |
payment/tests_audit_fixes.py |
deploy.sh پیش از migrate بکاپِ db-before-deploy-* میگیرد، integrity_check میکند و اگر نشد migrate را اجرا نمیکند |
مرحلهی «بکاپِ دیتابیس»: ۷ بار روی لپتاپ و یک بار روی سرور (۶ مگابایت، integrity_check: ok) |
دیتابیس و بکاپها فقط برای صاحبِ سرویس خواندنیاند (مرحلهی «مجوزِ فایلهای داده» در deploy.sh) |
با sudo -u nobody head -c 15 ~/mathapp-v2/db.sqlite3 روی سرور باید خطای دسترسی بدهد |
پانزده مسیرِ خواندنی و نوشتنیِ کمخطر سقفِ نرخ دارند (بانکِ سوال و intro بیورود، progress/nodes، user/hearts، اعلان، تیکت، پلنها، وضعیتِ اشتراک، سفارش، پیشنهادها، affiliate، تاریخچه و فیدبکِ دستیار، خروج). با نرخِ ۳ در دقیقه درخواستِ چهارم ۴۲۹ میگیرد |
content/tests_read_throttles.py |
ساختنِ دستیِ OTPCode و Session در پنل فقط برای ابرکاربر (کارمندِ عادی ۴۰۳) |
accounts/tests_security_review.py (StaffCannotMintLoginsTests) |
پایانِ درس و پایانِ آزمونِ جزیره جوابِ ثبتشده به همهی سوالها میخواهند (۴۰۹ answers_missing)، روی جزیرهی قفل ۴۰۳ node_locked میدهند (همان قاعدهی نقشه)، جزیره یا درسِ بیسوال چیزی برای جوابدادن ندارد و اگر باز است بیامتیاز رد میشود (قفل بودنش هنوز چک میشود)، و تعدادِ سوالِ آزمون را کلاینت نمیتواند کمتر از آزمونِ استانداردِ ۴ تایی بگوید (ستارهی دروغین) |
content/tests_finish_requires_answers.py، content/tests_node_completion.py |
کلیدِ امضای توکن تصادفی و فقط روی سرور است (.django_secret_key، ۶۰۰)؛ نه در .env، نه در یونیت؛ توکنِ ساختهشده با کلیدِ داخلِ ریپو 401 میگیرد؛ DJANGO_DEBUG هم در یونیت نیست |
دستی (۲۰۲۶-۱۰-۰۷): توکنِ جعلی 200 ← 401؛ تستِ واحد ندارد |
با CACHE_URL ستشده، قطعیِ Redis API را ۵۰۰ نمیکند (fail-open، ERROR در لاگ، health ۵۰۳؛ شرح در config/cache.py). الان ست نیست |
config/tests_cache_resilience.py (۱۱ تست، با redis-serverِ محلی که وسطِ کار میمیرد) |
بدنهٔ درخواست بزرگتر از ≈ ۲٫۵ مگابایت 413 میگیرد و ERRORِ لاگ نمیسازد (پیشتر 500 + traceback در لاگ؛ RequestDataTooBig). nginx هنوز تا ۱۰۰ مگابایت بافر میکند (بند ۱۸) |
config/tests_request_size.py |
کتابخانهها بدونِ آسیبپذیریِ شناختهشده (urllib3>=2.8.0، python-dotenv 1.2.2) |
پایگاهِ OSV روی pip freeze |
| actionهای لغو/بازپرداختِ سفارش، علامتِ «بررسیشده» و لینکِ بازار اجازهی تغییر میخواهند؛ کارمندِ فقطخواندنی (Supervisor) اجرایشان نمیکند | payment/tests_security_review.py، content/tests_security_review.py |
| ورودِ فایلِ سوال/برنامهی درسی اجازهی افزودن (و برای جایگزینی، حذف) میخواهد؛ مرورگرِ محتوا اجازهی دیدنِ سوال | content/tests_security_review.py |
| حذفِ حساب سابقهی کدِ تخفیف، پیشنهادها و کدهای پیامکیِ شماره را نگه میدارد؛ حسابِ دمو حذف نمیشود | accounts/tests_security_review.py |
| صافیِ اطلاعاتِ تماس در نامِ نمایشی (شماره با هر جداکننده، آیدیِ شبکهی اجتماعی) | accounts/tests_security_review.py |
| کلیدِ کاوهنگار و توکنِ خریدِ بازار در لاگِ خطای شبکه نمیآیند | accounts/tests_security_review.py |
برگشت از زرینپال بیAuthority ۴۰۰؛ خریدِ دومِ بازار روی سفارشِ پرداختشده ۴۰۹؛ آنچه داده یا پس گرفته میشود از خودِ سفارش است، نه پلنِ فعلی |
payment/tests_security_review.py |
شکلِ خرابِ مدل جواب را رایگان نمیکند؛ علامتِ «خارج از ریاضی» در هر جای استریم جواب را رد میکند و در کش نمیرود؛ بستنِ صفحه جوابِ ناقص را ثبت میکند؛ collectionِ بیرون از KNOWN_COLLECTIONS جستجو نمیشود |
ai_tutor/tests_security_review.py |
ورودیِ عددیِ غیرِ ASCII (مثلِ ²) ۴۰۰ است نه ۵۰۰؛ متنِ تیکت/گزارشِ باگ حداکثر ۵۰۰۰ نویسه |
content/tests_security_review.py |
INTERNAL_SERVICE_KEY در هیچ حالتی پیشفرض ندارد |
payment/tests.py (test_debug_mode_has_no_default_internal_key) |
کلیدِ امضای خودساخته (.django_secret_key) یک بار و اتمی ساخته میشود؛ سه workerِ همزمان یک کلید دارند |
دستی: سه پروسهی همزمان که config.settings را import میکنند، بیفایلِ کلید؛ ۲۰ از ۲۰ یک کلید |
| فرانت: شمارهی سفارشِ برگشتی از درگاه فقط عدد است، مسیرها encode میشوند و «پرداخت موفق» فقط از پاسخِ سرور میآید؛ دو تبِ همزمان با تمدیدِ توکن خارج نمیشوند | مخزنِ فرانت: src/app/launchParams.ts، src/api/http.ts |
| سفارش و تراکنش در پنل کاملاً فقطخواندنیاند (کارمندِ لغو/بازپرداخت مبلغ یا اعتبارِ سفارش را عوض نمیکند) و از پنل ساخته نمیشوند | payment/tests_security_review.py (OrderAdminMoneyFieldsReadOnlyTests) |
| ردِ بازار توکنِ خرید را نمیسوزاند؛ سفارشِ بازپرداختشده توکنِ تازه نمیپذیرد | payment/tests_security_review.py (BazaarRejectedVerifyDoesNotBurnTokenTests) |
| کدِ تخفیفِ وسطِ رفتن به درگاه، مبلغِ authority را با سفارش ناهمخوان نمیکند | payment/tests_security_review.py (DiscountDuringPaymentRequestTests) |
| پرداختِ بستهای که پلنش پاک شده از عددهای ثبتشدهی سفارش اعتبار میدهد (و اگر نداشت ERROR لاگ میشود)؛ پلن فقط با IRT ذخیره میشود | subscription/tests_security_review.py (PaidPackWithDeletedPlanTests، PlanCurrencyIsTomanOnlyTests) |
توکنِ بازار (refresh_token، pending_state) با فیلترِ querystringِ پنل حرفبهحرف درنمیآید |
payment/tests_security_review.py (BazaarTokenNotGuessableByAdminFilterTests) |
| شمارهی کاربر را فقط ابرکاربر عوض میکند؛ حسابهای کارمند و گروهها فقط با ابرکاربر ساخته/تغییر میکنند | accounts/tests_security_review.py (StaffCannotSwapLoginPhoneTests، StaffAccountsAdminSuperuserOnlyTests) |
| لاگِ تحویلِ کمپین اجازهی view میخواهد (هر کارمندی نمیبیند) | campaigns/tests_security_review.py |
| هر رقمِ یونیکد (تمامعرض، ریاضی…) در شماره به لاتین برمیگردد؛ کدِ غیرِ ASCII، پایهی ناموجود یا عظیم و تاریخِ تولدِ با «²» ۴۰۰ است نه ۵۰۰؛ صافیِ نامِ نمایشی ارقامِ یونیکد و جداکنندهی دلخواه را میگیرد | accounts/tests_security_review.py (UnicodeInputsAreNormalisedOrRejectedTests) |
/api/home?gradeId= ناموجود کش نمیشود (کشِ پُر شمارندهی سقفِ ورودِ ادمین را پاک میکرد)؛ /api/progress/nodes یک کوئری است و unit عظیم ۴۰۰ |
content/tests_security_review.py (JunkGradeIdsDoNotFlushCacheTests، ProgressNodesInputAndQueriesTests) |