خوش آمدید


برای شروع، از شما می‌خواهیم به کسانی که بعد از شما وارد مسابقه می‌شوند خوش‌آمد بگویید. برای این منظور یک تابع با نام HelloCodeCup تعریف کرده‌ایم.

اما از آن‌جا که می‌خواهیم برنامه‌ برای مسابقات بعدی کدکاپ نیز قابل استفاده باشد، از کاربر می‌خواهیم که شماره‌ی مسابقه را به تابع بدهد، سپس براساس آن، عبارت خوش‌آمدگویی مناسب را برگردانیم.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

hello-codecup
├── go.mod
├── go.sum
├── main.go
└── main_sample_test.go
Plain text

در فایل main.go تابعی با نام HelloCodeCup تعریف شده که امضای آن به‌صورت زیر است:

func HelloCodeCup(n int) string {
    // TODO: Implement
}
Go

این تابع را طوری پیاده‌سازی کنید که به ازای ورودی n، رشته‌ی Hello Codecup n را برگرداند.

مثال🔗

به ازای ورودی 6، تابع باید چنین مقداری را برگرداند:

Hello CodeCup 6
Plain text

توجه: در این سؤال مجاز به استفاده از کتاب‌خانه‌های شخص ثالث نیستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی تابع HelloCodeCup، فایل main.go را آپلود کنید.

زیرنویس‌های ناهم‌نام


پس از کنکور سخت و طاقت‌فرسا، مهدی تصمیم گرفته سریال جدیدی را شروع کند.

برای این منظور، چند قسمت از آن سریال را دانلود کرده (ویدیوهایی با فرمت mkv) و همچنین از آ‌ن‌جا که زبانش خوب نیست، یک سری زیرنویس (با فرمت srt) هم برای برخی قسمت‌ها دانلود کرده است.

او همه‌ی فایل‌ها را در یک پوشه ریخته، اما مشکلی که وجود دارد این است که برای این که زیرنویس‌ها به درستی کنار ویدیوها پخش شوند، باید هر زیرنویس دقیقاً هم‌نام فایل ویدیویی باشد، با این تفاوت که extension ها متفاوت است.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

subtitles
├── go.mod
├── go.sum
├── main.go
├── main_sample_test.go
├── sub.srt
└── video.mkv
Plain text

مهدی می‌خواهد در فایل main.go تابعی با نام Renamify پیاده‌سازی کند که زیرنویس‌ها را هم‌نام با ویدیوها کند. این تابع آدرس یک دایرکتوری را دریافت کرده و باید عملیات تغییر نام را انجام دهد:

func Renamify(path string) {
    // TODO: Implement
}
Go

مثلاً پوشه‌ی ورودی می‌تواند به این شکل باشد:

Serial1.S01E01.mkv
Serial1.sub.S01E01.srt
Plain text

ولی می‌خواهیم زیرنویس‌ها به ویدیوی متناظر خود تغییر نام داده شوند؛ مثلاً پوشه‌ی نهایی چنین محتویاتی داشته باشد:

Serial1.S01E01.mkv
Serial1.S01E01.srt
Plain text

مثال🔗

نمونه پوشه‌ی ورودی🔗

serial1.S1E1.mkv
serial1.S1E2.mkv
serial1.S1E3.mkv
subof.series1.S01E01.srt
subof.series1.S01E02.srt
subof.series1.S01E03.srt
Plain text

نمونه پوشه‌ی خروجی🔗

serial1.S1E1.mkv
serial1.S1E2.mkv
serial1.S1E3.mkv
serial1.S1E1.srt
serial1.S1E2.srt
serial1.S1E3.srt
Plain text

نکات🔗

  • دقیقاً یک سریال در پوشه موجود است.
  • از هر قسمت حداکثر یک زیرنویس و حداکثر یک ویدیو داریم.
  • به جز فایل‌های mkv (ویدیوها) و فایل‌های srt (زیرنویس‌ها) هیچ فایل دیگری در پوشه موجود نیست.
  • نام همه‌ی فایل‌ها، اطلاعات شماره قسمت و فصل مربوطه را به فرم S01E01‍ در خود دارد.
  • در این سؤال مجاز به استفاده از کتاب‌خانه‌های شخص ثالث نیستید.
  • شما تنها مجاز به اعمال تغییرات در فایل main.go هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی تابع Renamify، فایل main.go را آپلود کنید.

آسیب‌پذیری Log4j


یک هکر کلاه‌سفید قصد دارد سایت‌های مختلف برای برای آسیب‌پذیری Log4jShell بررسی کند. همان‌طور که می‌دانید، این یک آسیب‌پذیری از نوع RCE است و می‌تواند بسیار مخرب باشد؛ بنابراین باید به سرعت تشخیص داده شود.

این هکر برای بررسی یک سایت، باید تمام مسیر‌های ممکن آن را بررسی کند. برای این که از روی آدرس سایت تمام مسیر‌های آن را پیدا کند، از HATEOAS استفاده می‌کند (فرض کنیم همه‌ی سایت‌ها HATEOAS دارند).

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

log4j
├── go.mod
├── go.sum
├── main.go
├── main_sample_test.go
└── utils.go
Plain text

هکر تاکنون یک تابع Retrieve در فایل utils.go نوشته که با دریافت آدرس یک مسیر، مسیر‌های دیگری که از این مسیر امکان رفتن به‌صورت مستقیم دارند را به او می‌دهد. مثلاً به ازای ورودی snapp.ir/، آدرس‌های زیر را برمی‌گرداند:

  • snapp.ir/blog
  • snapp.ir/contact
  • snapp.ir/about

و به ازای ورودی snapp.ir/blog، آدرس های زیر را برمی‌گرداند:

  • snapp.ir/blog/post1
  • snapp.ir/blog/post2

نسخه‌ی اصلی این تابع که هکر پیاده‌سازی کرده، به آدرس موردنظر درخواست HTTP ارسال می‌کند و نتیجه را پردازش کرده و لینک مسیر‌ها را استخراج گرده و برمی‌گرداند. همان‌طور که حدس می‌زنید، هر بار اجرای این تابع با توجه به این که از شبکه استفاده می‌کند تا به سرور دسترسی پیدا کند، بسیار زمان‌بر است.

علاوه بر تابع Retrieve، یک تابع به نام Hack نوشته که قرار است به شکل بازگشتی، از ریشه‌ی یک سایت شروع کرده و تمام مسیر‌هایی که به شکل مستقیم یا غیرمستقیم می‌توان به آن‌ها رسید را پیدا کند (به شکل بدون ترتیب). پیاده‌سازی اولیه‌‌ای نیز از این تابع در اختیار ما قرار گرفته، اما متأسفانه با توجه به کندی تابع ‍Retrieve، بسیار کند است.

package main

func Hack(start string) []string {
    finalResult := []string{}

    tempResult := Retrieve(start)
    finalResult = append(finalResult, tempResult...)

    for _, v := range tempResult {
        finalResult = append(finalResult, Hack(v)...)
    }

    return finalResult
}
Go

هکر کلاه‌سفید از شما خواسته تا بدون تغییر عملکرد و امضا، تابع Hack را برایش سریع‌تر کنید. با توجه به این که تابع Retrieve تابع ایمنی در مقابل هم‌روندی است، می‌خواهیم از امکانات هم‌روندی Go استفاده کنیم تا زمان اجرای تابع Hack کمینه شود.

نکات🔗

  • در این سؤال حق استفاده از پکیج runtime زبان Go را ندارید.
  • داوری این سؤال ممکن است اندکی بیشتر از سایر سؤالات طول بکشد.
  • تابع Retrieve را در فایل ارسالی قرار ندهید.
  • شما تنها مجاز به اعمال تغییرات در فایل main.go هستید.
  • در صورت نیاز، می‌توانید توابع دیگری نیز در فایل main.go تعریف کنید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، فایل main.go را آپلود کنید.

کم‌تر ریکوئست بزن!


آرش قصد دارد یک وب‌سرویس خفن پیاده‌سازی کند، اما او می‌داند که از همان ابتدای کار، تعداد درخواست‌های کاربران به endpoint هایی که بار پردازشی بالایی دارند بسیار زیاد خواهد بود. بنابراین او تصمیم گرفته که از rate limiter استفاده کند. از آن‌جایی که او نمی‌داند این rate limiter را چگونه پیاده‌سازی کند، از شما خواسته تا آن را برایش پیاده‌سازی کنید.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

rate-limit
├── db
│   └── connection.go
├── handlers
│   └── root.go
├── limiters
│   ├── by_app_key.go
│   └── by_ip.go
├── test
│   └── ratelimit_sample_test.go
├── go.mod
├── go.sum
└── main.go
Plain text

وابستگی time/rate در پروژه تعریف شده و برای پیاده‌سازی rate limiter باید از آن استفاده کنید.

پایگاه داده🔗

در این پروژه از پایگاه داده‌ی PostgreSQL استفاده شده است. برنامه شامل جدولی با نام app_keys است که شامل دو ستون id از نوع BIGSERIAL و key از نوع VARCHAR(255) است.

در پکیج db تابعی با نام GetConnection تعریف شده که باید از آن برای دریافت کانکشن دیتابیس استفاده کنید. می‌توانید اطلاعات اتصال به دیتابیس را در این فایل تغییر دهید.

روت‌ها🔗

تنها یک روت با آدرس / در برنامه تعریف شده است که می‌توان rate limiter های مختلف را روی handler آن اعمال کرد.

تابع ByIp🔗

این تابع به‌ترتیب شامل سه پارامتر زیر است:

  • next از نوع http.Handler: همان handler اصلی روت است.
  • refillRate از نوع rate.Limit: میزان افزایش تعداد درخواست‌های مجاز کاربر در هر ثانیه (برای مثال اگر مقدار آن برابر با ۳ باشد، در هر ثانیه، ۳ واحد به تعداد درخواست‌های مجاز کاربر اضافه می‌شود، به شرطی که تعداد درخواست‌های مجاز فعلی‌اش کوچک‌تر از tokenBucketSize باشد.)
  • tokenBucketSize از نوع int: سقف تعداد درخواست‌های پیاپی کاربر

این تابع را طوری پیاده‌سازی کنید که درخواست‌ها را براساس آی‌پی محدود کند. در صورتی که کاربر با محدودیت مواجه نشده باشد، پردازش درخواست باید توسط next صورت گیرد. در غیر این‌صورت، کد پاسخ باید به 429 تغییر کند، مقدار هدر Content-Type باید برابر با application/json قرار داده شود و بدنه‌ی پاسخ به‌صورت زیر باشد:

{"error": "too many requests"}
JSON

تابع ByAppKey🔗

امضای این تابع مشابه تابع ByIp است.

این تابع را طوری پیاده‌سازی کنید که درخواست‌ها را براساس مقدار هدر X-App-Key موجود در درخواست محدود کند. اگر مقدار این هدر در جدول app_keys وجود نداشته باشد، محدودیت نباید اعمال شود. در غیر این‌صورت، محدودیت باید اعمال شود. اگر کاربر با محدودیت مواجه شده باشد، پاسخ باید مشابه پاسخ مورد انتظار برای تابع ByIp باشد.

تضمین می‌شود که هنگام استفاده از این تابع، هدر X-App-Key در درخواست موجود است.

ترکیب rate limiter ها🔗

ممکن است بخواهیم rate limiter ها را ترکیب کنیم. اگر از توابع ByIp و ByAppKey در کنار یکدیگر استفاده شود، محدودیت باید هم براساس آی‌پی و هم براساس مقدار هدر X-App-Key اعمال شود. مثلاً اگر مقدار tokenBucketSize برابر با ۵ باشد و ۵ درخواست پیاپی با آی‌پی‌های مختلف، اما با X-App-Key یکسان (به‌طوری که در جدول app_keys موجود باشد) ارسال کنیم، باید با محدودیت مواجه شویم. همچنین اگر این درخواست‌ها را با آی‌پی یکسان، اما با X-App-Keyهای متفاوت (به‌طوری که در جدول app_keys موجود باشند) ارسال کنیم، باز هم باید با محدودیت مواجه شویم.

نکات🔗

  • نیازی به persistent بودن اطلاعات مربوط به rate limiter ها نیست (با خاتمه‌ی برنامه، داده‌های مربوط به rate limiter ها می‌توانند از بین بروند).
  • شما تنها مجاز به اعمال تغییرات در پوشه‌ی limiters هستید.
  • در صورت نیاز، می‌توانید فایل‌های جدیدی در پوشه‌ی limiters تعریف کنید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، پوشه‌ی limiters را زیپ کرده و آپلود کنید.