allow loopback to localhost on mcp, only for local MCP clients

This commit is contained in:
Amruth Pillai
2026-04-26 10:59:10 +02:00
parent 76e00ae019
commit 623ca5c675
63 changed files with 84 additions and 460 deletions
+1
View File
@@ -15,6 +15,7 @@ rss: true
## Fixes & Security
- Thank you to [@shaxbozaka](https://github.com/shaxbozaka) for running a security audit on Reactive Resume and helping make the app more secure.
- Hardened auth, OAuth, AI URL validation, printer/export endpoints, resume access controls, Browserless configuration, and public route exposure. [a42dbcd4](https://github.com/amruthpillai/reactive-resume/commit/a42dbcd4)
- Removed the public RPC surface for the printer-only resume fetch endpoint by moving it behind a server function, further shrinking the attack surface for `getByIdForPrinter`, thanks to [@shaxbozaka](https://github.com/shaxbozaka).
- Corrected Computer Modern Sans italic font file mappings, thanks to [@russellbrenner](https://github.com/russellbrenner). [#2881](https://github.com/amruthpillai/reactive-resume/pull/2881)
- Improved email handling and user lookup in OAuth configuration, thanks to [@VedantBhawsar](https://github.com/VedantBhawsar). [#2874](https://github.com/amruthpillai/reactive-resume/pull/2874)
- Aligned role period text in experience items, thanks to [@JamesGoslings](https://github.com/JamesGoslings). [#2908](https://github.com/amruthpillai/reactive-resume/pull/2908)
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Besig om jou wagwoord te herstel..."
msgid "Resources"
msgstr "Hulpbronne"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "CV-ontleding"
@@ -3951,4 +3945,3 @@ msgstr "Zoem uit"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zoeloe"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "የይለፍ ቃልዎን በመዳግም ላይ…"
msgid "Resources"
msgstr "ሀብቶች"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
@@ -3951,4 +3945,3 @@ msgstr "አጉር"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ዙሉ"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "جاري إعادة تعيين كلمة المرور..."
msgid "Resources"
msgstr "الموارد"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "السيرة الذاتية"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "تحليل السيرة الذاتية"
@@ -3951,4 +3945,3 @@ msgstr "تصغير"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "الزولو"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Parolunuz sıfırlanır..."
msgid "Resources"
msgstr "Resurslar"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Təcrübə"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Təcrübə Təhlili"
@@ -3951,4 +3945,3 @@ msgstr "Uzaqlaşdır"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Нулиране на паролата ви..."
msgid "Resources"
msgstr "Ресурси"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Резюме"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Анализ на автобиографията"
@@ -3951,4 +3945,3 @@ msgstr "Намаляване"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулуски"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "আপনার পাসওয়ার্ড রিসেট করা
msgid "Resources"
msgstr "রিসোর্সসমূহ"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "জীবনবৃত্তান্ত"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "রিজিউমে বিশ্লেষণ"
@@ -3951,4 +3945,3 @@ msgstr "জুম আউট"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "জুলু"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Sestà restablint la contrasenya..."
msgid "Resources"
msgstr "Recursos"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Resum"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Anàlisi de currículum"
@@ -3951,4 +3945,3 @@ msgstr "Allunya"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulú"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Obnovování hesla…"
msgid "Resources"
msgstr "Zdroje"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Životopis"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analýza životopisu"
@@ -3951,4 +3945,3 @@ msgstr "Oddálit"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Nulstiller din adgangskode..."
msgid "Resources"
msgstr "Ressourcer"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "CV-analyse"
@@ -3951,4 +3945,3 @@ msgstr "Zoom ud"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Ihr Passwort wird zurückgesetzt..."
msgid "Resources"
msgstr "Ressourcen"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Lebenslauf"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Lebenslauf Analyse"
@@ -3951,4 +3945,3 @@ msgstr "Herauszoomen"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Επαναφορά κωδικού πρόσβασης..."
msgid "Resources"
msgstr "Πόροι"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Βιογραφικό σημείωμα"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Ανάλυση βιογραφικού σημειώματος"
@@ -3951,4 +3945,3 @@ msgstr "Σμίκρυνση"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Ζουλού"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Resetting your password..."
msgid "Resources"
msgstr "Resources"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Resume"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Resume Analysis"
@@ -3951,4 +3945,3 @@ msgstr "Zoom out"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-6
View File
@@ -2767,12 +2767,6 @@ msgstr "Resetting your password..."
msgid "Resources"
msgstr "Resources"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Resume"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Resume Analysis"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Restableciendo tu contraseña..."
msgid "Resources"
msgstr "Recursos"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Currículum"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Análisis del currículum"
@@ -3951,4 +3945,3 @@ msgstr "Alejar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulú"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "در حال تنظیم مجدد گذرواژه شما..."
msgid "Resources"
msgstr "منابع"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "رزومه"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "تحلیل رزومه"
@@ -3951,4 +3945,3 @@ msgstr "کوچک‌نمایی"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "زولو"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Palautetaan salasanaasi..."
msgid "Resources"
msgstr "Resurssit"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Yhteenveto"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Yhteenveto Analyysi"
@@ -3951,4 +3945,3 @@ msgstr "Loitonna"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Réinitialisation de votre mot de passe..."
msgid "Resources"
msgstr "Ressources"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Curriculum vitae"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analyse de CV"
@@ -3951,4 +3945,3 @@ msgstr "Zoom arrière"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zoulou"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "איפוס הסיסמה שלך..."
msgid "Resources"
msgstr "משאבים"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "קורות חיים"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "ניתוח קורות חיים"
@@ -3951,4 +3945,3 @@ msgstr "התרחקות"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "זולו"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "आपका पासवर्ड रीसेट किया जा
msgid "Resources"
msgstr "संसाधन"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "जीवन-परिचय"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "रिज़्यूमे विश्लेषण"
@@ -3951,4 +3945,3 @@ msgstr "ज़ूम आउट"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ज़ुलु"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Jelszó visszaállítása..."
msgid "Resources"
msgstr "Erőforrások"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Önéletrajz"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Összefoglaló elemzés"
@@ -3951,4 +3945,3 @@ msgstr "Kicsinyítés"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Mengatur ulang kata sandi Anda..."
msgid "Resources"
msgstr "Sumber Daya"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Melanjutkan"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Melanjutkan Analisis"
@@ -3951,4 +3945,3 @@ msgstr "Perkecil"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Reimpostazione della tua password in corso..."
msgid "Resources"
msgstr "Risorse"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Il curriculum"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analisi del curriculum"
@@ -3951,4 +3945,3 @@ msgstr "Rimpicciolisci"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "パスワードをリセットしています..."
msgid "Resources"
msgstr "リソース"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "履歴書"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "履歴書分析"
@@ -3951,4 +3945,3 @@ msgstr "ズームアウト"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ズールー語"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "កំពុង​កំណត់​ពាក្យសម្ងាត់
msgid "Resources"
msgstr "ធនធាន"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
@@ -3951,4 +3945,3 @@ msgstr "បង្រួម"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಮರು
msgid "Resources"
msgstr "ಸಂಪನ್ಮೂಲಗಳು"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
@@ -3951,4 +3945,3 @@ msgstr "ಗಾತ್ರ ಕುಗ್ಗಿಸಿ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ಜೂಲೂ"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "비밀번호를 재설정하는 중입니다..."
msgid "Resources"
msgstr "리소스"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "이력서"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "이력서 분석"
@@ -3951,4 +3945,3 @@ msgstr "축소"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "줄루어"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Atstatomas slaptažodis..."
msgid "Resources"
msgstr "Ištekliai"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Gyvenimo aprašymo analizė"
@@ -3951,4 +3945,3 @@ msgstr "Tolinti"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulų"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Tiek atiestatīta jūsu parole..."
msgid "Resources"
msgstr "Resursi"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "CV analīze"
@@ -3951,4 +3945,3 @@ msgstr "Tālināt"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "നിങ്ങളുടെ പാസ്‌വേഡ് റീസെറ
msgid "Resources"
msgstr "റിസോഴ്സുകൾ"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "പുനരാരംഭിക്കുക"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "റെസ്യൂമെ വിശകലനം"
@@ -3951,4 +3945,3 @@ msgstr "സൂം ഔട്ട് ചെയ്യുക"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "സൂളു"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "तुमचा पासवर्ड रीसेट केला ज
msgid "Resources"
msgstr "साधने"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "पुनरावलोकन"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "रेझ्युमे विश्लेषण"
@@ -3951,4 +3945,3 @@ msgstr "बाहेर झूम करा"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "झुलू"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Menetapkan semula kata laluan anda..."
msgid "Resources"
msgstr "Sumber"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Ringkasan"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analisis Resume"
@@ -3951,4 +3945,3 @@ msgstr "Zum keluar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "तपाईंको पासवर्ड रिसेट हुँद
msgid "Resources"
msgstr "स्रोतहरू"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "जीवन बृत्तान्त"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "बायोडाटा विश्लेषण"
@@ -3951,4 +3945,3 @@ msgstr "जूम आउट"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "जुलु"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Uw wachtwoord wordt gereset..."
msgid "Resources"
msgstr "Bronnen"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "C.V. Analyse"
@@ -3951,4 +3945,3 @@ msgstr "Uitzoomen"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Tilbakestiller passordet ditt..."
msgid "Resources"
msgstr "Ressurser"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
@@ -3951,4 +3945,3 @@ msgstr "Zoom ut"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "ଆପଣଙ୍କ ପାସୱାର୍ଡ ରିସେଟ୍ ହେଉ
msgid "Resources"
msgstr "ସମ୍ପଦ"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
@@ -3951,4 +3945,3 @@ msgstr "ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ଜୁଲୁ"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Resetowanie hasła..."
msgid "Resources"
msgstr "Zasoby"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Proszę wznowić"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analiza CV"
@@ -3951,4 +3945,3 @@ msgstr "Pomniejsz"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Redefinindo a sua senha..."
msgid "Resources"
msgstr "Recursos"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Currículo"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Análise de currículo"
@@ -3951,4 +3945,3 @@ msgstr "Diminuir zoom"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "A repor a sua senha..."
msgid "Resources"
msgstr "Recursos"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Currículo"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Análise de currículos"
@@ -3951,4 +3945,3 @@ msgstr "Afastar"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Se resetează parola..."
msgid "Resources"
msgstr "Resurse"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "CV"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analiza CV-ului"
@@ -3951,4 +3945,3 @@ msgstr "Micșorează"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Сброс пароля..."
msgid "Resources"
msgstr "Ресурсы"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Резюме"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Анализ резюме"
@@ -3951,4 +3945,3 @@ msgstr "Уменьшить"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Obnovujem tvoje heslo..."
msgid "Resources"
msgstr "Zdroje"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Životopis"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analýza životopisu"
@@ -3951,4 +3945,3 @@ msgstr "Oddialiť"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Ponastavljanje gesla..."
msgid "Resources"
msgstr "Viri"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Življenjepis"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analiza življenjepisa"
@@ -3951,4 +3945,3 @@ msgstr "Oddalji"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zuluščina"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Po rivendoset fjalëkalimi juaj..."
msgid "Resources"
msgstr "Burime"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Përmbledhje"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analiza e CV-së"
@@ -3951,4 +3945,3 @@ msgstr "Zvogëlo"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Ресетовање ваше лозинке..."
msgid "Resources"
msgstr "Ресурси"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Резиме"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Анализа резимеа"
@@ -3951,4 +3945,3 @@ msgstr "Умањи"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Återställer ditt lösenord..."
msgid "Resources"
msgstr "Resurser"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Meritförteckning"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Analys av CV"
@@ -3951,4 +3945,3 @@ msgstr "Zooma ut"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "உங்கள் கடவுச்சொல்லை மீட்ட
msgid "Resources"
msgstr "வளங்கள்"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "தொடர்"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "விண்ணப்பப் பகுப்பாய்வு"
@@ -3951,4 +3945,3 @@ msgstr "சிறிதாக்கு"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ஜூலு"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "మీ పాస్‌వర్డ్‌ను రీసెట్ చే
msgid "Resources"
msgstr "వనరులు"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "రిజ్యూమ్"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "రిజ్యూమ్ విశ్లేషణ"
@@ -3951,4 +3945,3 @@ msgstr "జూమ్ అవుట్ చేయండి"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "జులు"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "กำลังรีเซ็ตรหัสผ่านของคุ
msgid "Resources"
msgstr "ทรัพยากร"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "ประวัติย่อ"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "การวิเคราะห์ประวัติย่อ"
@@ -3951,4 +3945,3 @@ msgstr "ซูมออก"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "ซูลู"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Şifreniz sıfırlanıyor..."
msgid "Resources"
msgstr "Kaynaklar"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Özgeçmiş"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Özgeçmiş Analizi"
@@ -3951,4 +3945,3 @@ msgstr "Uzaklaştır"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Скидання пароля..."
msgid "Resources"
msgstr "Ресурси"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Резюме"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Аналіз резюме"
@@ -3951,4 +3945,3 @@ msgstr "Зменшити"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Зулу"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Parolingiz tiklanmoqda..."
msgid "Resources"
msgstr "Resurslar"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Rezyume"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Rezyume tahlili"
@@ -3951,4 +3945,3 @@ msgstr "Kichraytirish"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "Đang đặt lại mật khẩu..."
msgid "Resources"
msgstr "Tài nguyên"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "Sơ yếu lý lịch"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "Phân tích sơ yếu lý lịch"
@@ -3951,4 +3945,3 @@ msgstr "Thu nhỏ"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "Zulu"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "正在重置你的密码…"
msgid "Resources"
msgstr "资源"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "简历"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "简历分析"
@@ -3951,4 +3945,3 @@ msgstr "缩小"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "祖鲁语"
-7
View File
@@ -2772,12 +2772,6 @@ msgstr "正在重設您的密碼…"
msgid "Resources"
msgstr "資源"
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr "簡歷"
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr "履歷分析"
@@ -3951,4 +3945,3 @@ msgstr "縮小"
#: src/utils/locale.ts
msgid "Zulu"
msgstr "祖魯語"
-6
View File
@@ -2767,12 +2767,6 @@ msgstr ""
msgid "Resources"
msgstr ""
#. Browser tab suffix for printable resume pages
#. Browser tab title before printable resume data finishes loading
#: src/routes/printer/$resumeId.tsx
msgid "Resume"
msgstr ""
#: src/utils/resume/section.tsx
msgid "Resume Analysis"
msgstr ""
+9 -2
View File
@@ -13,9 +13,16 @@ import { useStoreWithEqualityFn } from "zustand/traditional";
import type { ResumeData } from "@/schema/resume/data";
import { orpc, type RouterOutput } from "@/integrations/orpc/client";
import { orpc } from "@/integrations/orpc/client";
type Resume = Pick<RouterOutput["resume"]["getByIdForPrinter"], "id" | "name" | "slug" | "tags" | "data" | "isLocked">;
type Resume = {
id: string;
name: string;
slug: string;
tags: string[];
data: ResumeData;
isLocked: boolean;
};
type ResumeStoreState = {
resume: Resume;
+4 -21
View File
@@ -17,7 +17,7 @@ import { eq, or } from "drizzle-orm";
import { env } from "@/utils/env";
import { hashPassword, verifyPassword } from "@/utils/password";
import { generateId, toUsername } from "@/utils/string";
import { isPrivateOrLoopbackHost, parseAllowedHostList, parseUrl } from "@/utils/url-security";
import { isAllowedOAuthRedirectUri, parseAllowedHostList } from "@/utils/url-security";
import { schema } from "../drizzle";
import { db } from "../drizzle/client";
@@ -74,25 +74,6 @@ function getTrustedOrigins(): string[] {
const TRUSTED_ORIGINS = getTrustedOrigins();
const OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS = parseAllowedHostList(env.OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS);
function isAllowedDynamicClientRedirectHost(origin: string, hostname: string): boolean {
if (TRUSTED_ORIGINS.includes(origin)) return true;
if (OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS.has(origin)) return true;
return OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS.has(hostname);
}
function isAllowedDynamicClientRedirectUri(value: string) {
const parsed = parseUrl(value);
if (!parsed) return false;
if (parsed.username || parsed.password) return false;
if (parsed.hash) return false;
if (parsed.protocol !== "https:") return false;
if (isPrivateOrLoopbackHost(parsed.hostname)) return false;
const origin = parsed.origin.toLowerCase();
const hostname = parsed.hostname.toLowerCase();
return isAllowedDynamicClientRedirectHost(origin, hostname);
}
async function findExistingUserByEmail(email: string) {
const normalizedEmail = email.trim().toLowerCase();
@@ -284,7 +265,7 @@ const getAuthConfig = () => {
if (typeof uri !== "string") {
throw new APIError("BAD_REQUEST", { message: "redirect_uris entries must be strings" });
}
if (!isAllowedDynamicClientRedirectUri(uri)) {
if (!isAllowedOAuthRedirectUri(uri, TRUSTED_ORIGINS, OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS)) {
throw new APIError("BAD_REQUEST", {
message: "redirect_uri is not allowed for dynamic client registration",
});
@@ -409,6 +390,8 @@ const getAuthConfig = () => {
consentPage: "/auth/oauth",
validAudiences: OAUTH_AUDIENCES,
allowDynamicClientRegistration: true,
// Required for MCP client onboarding (RFC 7591). Phishing vector is closed by the
// redirect_uri allowlist in the hooks.before middleware above and in src/routes/api/auth.$.ts.
allowUnauthenticatedClientRegistration: true,
rateLimit: {
register: { window: 60, max: 5 },
-15
View File
@@ -116,21 +116,6 @@ export const resumeRouter = {
return resumeService.getById({ id: input.id, userId: context.user.id });
}),
getByIdForPrinter: publicProcedure
.route({ tags: ["Internal"], operationId: "getResumeForPrinter", summary: "Get resume by ID for printer" })
.input(
resumeDto.getById.input.extend({
token: z.string().optional(),
}),
)
.handler(async ({ input, context }) => {
return resumeService.getByIdForPrinter({
id: input.id,
currentUserId: context.user?.id,
printerToken: input.token,
});
}),
getBySlug: publicProcedure
.route({
method: "GET",
+10 -22
View File
@@ -2,9 +2,10 @@ import { createFileRoute } from "@tanstack/react-router";
import { auth } from "@/integrations/auth/config";
import { env } from "@/utils/env";
import { isPrivateOrLoopbackHost, parseAllowedHostList, parseUrl } from "@/utils/url-security";
import { isAllowedOAuthRedirectUri, parseAllowedHostList } from "@/utils/url-security";
const oauthDynamicClientRedirectHosts = parseAllowedHostList(env.OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS);
const oauthTrustedOrigins = [new URL(env.APP_URL).origin.toLowerCase()];
const oauthAuthorizeSanitizedParams = [
"prompt",
"redirect_uri",
@@ -17,25 +18,6 @@ const oauthAuthorizeSanitizedParams = [
"resource",
] as const;
function isAllowedDynamicClientRedirectUri(value: string) {
const parsed = parseUrl(value);
if (!parsed) return false;
if (parsed.protocol !== "https:") return false;
if (parsed.username || parsed.password) return false;
if (parsed.hash) return false;
if (isPrivateOrLoopbackHost(parsed.hostname)) return false;
const appOrigin = new URL(env.APP_URL).origin.toLowerCase();
const origin = parsed.origin.toLowerCase();
const hostname = parsed.hostname.toLowerCase();
if (origin === appOrigin) return true;
if (oauthDynamicClientRedirectHosts.has(origin)) return true;
if (oauthDynamicClientRedirectHosts.has(hostname)) return true;
return false;
}
function sanitizeOAuthAuthorizeRequest(request: Request): Request {
if (request.method !== "GET") return request;
@@ -117,8 +99,14 @@ async function validateDynamicClientRegistrationRequest(request: Request): Promi
const redirectUris = Array.isArray(body.redirect_uris) ? body.redirect_uris : [];
for (const redirectUri of redirectUris) {
if (typeof redirectUri !== "string" || !isAllowedDynamicClientRedirectUri(redirectUri)) {
return Response.json({ message: "redirect_uri is not allowed" }, { status: 400 });
if (
typeof redirectUri !== "string" ||
!isAllowedOAuthRedirectUri(redirectUri, oauthTrustedOrigins, oauthDynamicClientRedirectHosts)
) {
return Response.json(
{ error: "invalid_redirect_uri", error_description: "redirect_uri is not allowed" },
{ status: 400 },
);
}
}
}
+10 -17
View File
@@ -1,5 +1,5 @@
import { t } from "@lingui/core/macro";
import { createFileRoute, redirect } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { zodValidator } from "@tanstack/zod-adapter";
import { useEffect } from "react";
import { z } from "zod";
@@ -7,7 +7,7 @@ import { z } from "zod";
import { LoadingScreen } from "@/components/layout/loading-screen";
import { ResumePreview } from "@/components/resume/preview";
import { useResumeStore } from "@/components/resume/store/resume";
import { getORPCClient } from "@/integrations/orpc/client";
import { resumeService } from "@/integrations/orpc/services/resume";
import { env } from "@/utils/env";
import { verifyPrinterToken } from "@/utils/printer-token";
@@ -15,6 +15,12 @@ const searchSchema = z.object({
token: z.string().catch(""),
});
const getResumeForPrinterFn = createServerFn({ method: "GET" })
.inputValidator(z.object({ id: z.string(), token: z.string() }))
.handler(({ data }) => {
return resumeService.getByIdForPrinter({ id: data.id, printerToken: data.token });
});
function assertValidPrinterToken(token: string, resumeId: string): void {
const tokenResumeId = verifyPrinterToken(token);
if (tokenResumeId === resumeId) return;
@@ -35,25 +41,12 @@ export const Route = createFileRoute("/printer/$resumeId")({
},
loaderDeps: ({ search }) => ({ token: search.token }),
loader: async ({ params, deps }) => {
const client = getORPCClient();
const resume = await client.resume.getByIdForPrinter({ id: params.resumeId, token: deps.token });
const resume = await getResumeForPrinterFn({ data: { id: params.resumeId, token: deps.token } });
return { resume };
},
head: ({ loaderData }) => ({
meta: [
{
title: loaderData
? `${loaderData.resume.data.basics.name} - ${t({
comment: "Browser tab suffix for printable resume pages",
message: "Resume",
})}`
: t({
comment: "Browser tab title before printable resume data finishes loading",
message: "Resume",
}),
},
],
meta: [{ title: loaderData ? `${loaderData.resume.data.basics.name} - Resume` : "Resume" }],
}),
});
+27
View File
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vite-plus/test";
import {
isAllowedOAuthRedirectUri,
isAllowedExternalUrl,
isPrivateOrLoopbackHost,
parseAllowedHostList,
@@ -55,6 +56,32 @@ describe("isAllowedExternalUrl", () => {
});
});
describe("isAllowedOAuthRedirectUri", () => {
const trustedOrigins = ["https://rxresu.me"];
const allowedHosts = new Set(["https://client.example.com", "trusted.example.com"]);
it("allows local loopback HTTP callbacks for native OAuth clients", () => {
expect(isAllowedOAuthRedirectUri("http://localhost:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
expect(isAllowedOAuthRedirectUri("http://127.0.0.1:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
expect(isAllowedOAuthRedirectUri("http://[::1]:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
});
it("keeps rejecting unsafe HTTP and malformed redirects", () => {
expect(isAllowedOAuthRedirectUri("http://192.168.1.20:6188/callback", trustedOrigins, allowedHosts)).toBe(false);
expect(isAllowedOAuthRedirectUri("http://example.com/callback", trustedOrigins, allowedHosts)).toBe(false);
expect(isAllowedOAuthRedirectUri("not-a-url", trustedOrigins, allowedHosts)).toBe(false);
});
it("allows only trusted HTTPS redirects without credentials or fragments", () => {
expect(isAllowedOAuthRedirectUri("https://rxresu.me/callback", trustedOrigins, allowedHosts)).toBe(true);
expect(isAllowedOAuthRedirectUri("https://client.example.com/callback", trustedOrigins, allowedHosts)).toBe(true);
expect(isAllowedOAuthRedirectUri("https://trusted.example.com/callback", trustedOrigins, allowedHosts)).toBe(true);
expect(isAllowedOAuthRedirectUri("https://evil.example.com/callback", trustedOrigins, allowedHosts)).toBe(false);
expect(isAllowedOAuthRedirectUri("https://user:pass@rxresu.me/callback", trustedOrigins, allowedHosts)).toBe(false);
expect(isAllowedOAuthRedirectUri("https://rxresu.me/callback#token", trustedOrigins, allowedHosts)).toBe(false);
});
});
describe("sanitizeResumePictureUrl", () => {
const appUrl = "https://rxresu.me";
+23
View File
@@ -47,6 +47,11 @@ export function isPrivateOrLoopbackHost(hostname: string) {
return false;
}
function isOAuthLoopbackRedirectHost(hostname: string) {
const normalized = stripIpv6Brackets(normalizeHostname(hostname));
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
}
export function parseUrl(input: string) {
try {
return new URL(input);
@@ -80,6 +85,24 @@ export function isAllowedExternalUrl(input: string, allowedHosts: Set<string>) {
return allowedHosts.has(origin);
}
export function isAllowedOAuthRedirectUri(input: string, trustedOrigins: string[], allowedHosts: Set<string>) {
const parsed = parseUrl(input);
if (!parsed) return false;
if (parsed.username || parsed.password) return false;
if (parsed.hash) return false;
const origin = parsed.origin.toLowerCase();
const hostname = normalizeHostname(parsed.hostname);
if (parsed.protocol === "http:") return isOAuthLoopbackRedirectHost(hostname);
if (parsed.protocol !== "https:") return false;
if (isPrivateOrLoopbackHost(hostname)) return false;
if (trustedOrigins.includes(origin)) return true;
if (allowedHosts.has(origin)) return true;
return allowedHosts.has(hostname);
}
export function sanitizeResumePictureUrl(url: string, appUrl: string) {
if (!url) return "";
if (url.startsWith("/uploads/")) return url;