From 51ccdeb95976400f84303770c87286ba1176b336 Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 10:13:33 +0100 Subject: [PATCH 01/30] test: add comprehensive tests for compound route resolution Add feature tests to verify compound route functionality: - Test valid molecule redirects to spectra page - Test case-insensitive compound identifiers (M188 and m188) - Test 404 response for non-existent molecules - Ensure existing invalid identifier test remains intact Tests follow Laravel 12 testing best practices: - Use model factories for test data generation - Use RefreshDatabase trait for clean test state - Test both success and failure scenarios - Maintain naming conventions consistent with existing tests --- tests/Feature/ApplicationControllerTest.php | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Feature/ApplicationControllerTest.php b/tests/Feature/ApplicationControllerTest.php index f1eb0a94..3cdb4aa3 100644 --- a/tests/Feature/ApplicationControllerTest.php +++ b/tests/Feature/ApplicationControllerTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature; use App\Models\Dataset; +use App\Models\Molecule; use App\Models\Project; use App\Models\Study; use App\Models\Team; @@ -105,6 +106,35 @@ public function test_resolve_compound_returns_404_for_invalid_identifier(): void $response->assertStatus(404); } + public function test_resolve_compound_redirects_to_spectra_for_valid_molecule(): void + { + $molecule = Molecule::factory()->create([ + 'identifier' => 188, + ]); + + $response = $this->get('/compound/M188'); + + $response->assertRedirect('/spectra?compound=188'); + } + + public function test_resolve_compound_with_lowercase_prefix(): void + { + $molecule = Molecule::factory()->create([ + 'identifier' => 189, + ]); + + $response = $this->get('/compound/m189'); + + $response->assertRedirect('/spectra?compound=189'); + } + + public function test_resolve_compound_returns_404_for_non_existent_molecule(): void + { + $response = $this->get('/compound/M99999'); + + $response->assertStatus(404); + } + public function test_resolve_project_renders_default_info_tab(): void { $response = $this->get('/project/P1'); From f77120a8ad3e45c3c1d779192c1f814d8f1a65a9 Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 10:25:34 +0100 Subject: [PATCH 02/30] fix(security): add generic language files for auth and password reset Override Laravel default authentication and password reset messages to prevent user enumeration attacks. The messages now provide generic responses that do not reveal whether an email address exists in the system. --- lang/en/auth.php | 20 ++++++++++++++++++++ lang/en/passwords.php | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 lang/en/auth.php create mode 100644 lang/en/passwords.php diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 00000000..d458da13 --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'These credentials do not match our records.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 00000000..5704d616 --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,23 @@ + 'Your password has been reset.', + 'sent' => 'If an account exists with this email, you will receive a password reset link.', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => 'If an account exists with this email, you will receive a password reset link.', + +]; From ecb38725238f49b047c784419c2bf1c89dfd13e0 Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 10:25:41 +0100 Subject: [PATCH 03/30] fix(security): use generic error message for unverified accounts in API login Change API login response for unverified accounts from revealing account status to generic 'Invalid login details' message. This prevents attackers from determining if an email exists in the system by checking verification status. --- .../Controllers/API/Auth/LoginController.php | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/API/Auth/LoginController.php b/app/Http/Controllers/API/Auth/LoginController.php index ef5f6760..5a395d68 100644 --- a/app/Http/Controllers/API/Auth/LoginController.php +++ b/app/Http/Controllers/API/Auth/LoginController.php @@ -88,21 +88,6 @@ class LoginController extends Controller * ), * * @OA\Response( - * response=403, - * description="Account not verified - Email verification required", - * - * @OA\JsonContent( - * - * @OA\Property( - * property="message", - * type="string", - * description="Error message indicating account verification status", - * example="Account is not yet verified. Please verify your email address by clicking on the link we just emailed to you." - * ) - * ) - * ), - * - * @OA\Response( * response=422, * description="Validation error - Invalid input data", * @@ -189,8 +174,8 @@ public function login(Request $request): JsonResponse if (! $user->hasVerifiedEmail()) { return response()->json([ - 'message' => 'Account is not yet verified. Please verify your email address by clicking on the link we just emailed to you.', - ], 403); + 'message' => 'Invalid login details', + ], 401); } $token = $user->createToken('auth_token')->plainTextToken; From c58d7c24da522b0d02f147a8c1cfe0f1e01fc95a Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 10:25:47 +0100 Subject: [PATCH 04/30] fix(security): use generic error messages for member invitation flows Replace specific error messages in team, project, and study member invitation flows with generic 'Unable to add member with this email address' message. This prevents user enumeration by not revealing whether an email exists or is already a member. --- app/Actions/Jetstream/AddTeamMember.php | 4 ++-- app/Actions/Project/AddProjectMember.php | 4 ++-- app/Actions/Study/AddStudyMember.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Actions/Jetstream/AddTeamMember.php b/app/Actions/Jetstream/AddTeamMember.php index 2e3b8a6f..9221bbf6 100644 --- a/app/Actions/Jetstream/AddTeamMember.php +++ b/app/Actions/Jetstream/AddTeamMember.php @@ -48,7 +48,7 @@ protected function validate($team, string $email, ?string $role) 'email' => $email, 'role' => $role, ], $this->rules(), [ - 'email.exists' => __('We were unable to find a registered user with this email address.'), + 'email.exists' => __('Unable to add member with this email address.'), ])->after( $this->ensureUserIsNotAlreadyOnTeam($team, $email) )->validateWithBag('addTeamMember'); @@ -81,7 +81,7 @@ protected function ensureUserIsNotAlreadyOnTeam($team, string $email) $validator->errors()->addIf( $team->hasUserWithEmail($email), 'email', - __('This user already belongs to the team.') + __('Unable to add member with this email address.') ); }; } diff --git a/app/Actions/Project/AddProjectMember.php b/app/Actions/Project/AddProjectMember.php index ffdb1e2f..f69a4d77 100644 --- a/app/Actions/Project/AddProjectMember.php +++ b/app/Actions/Project/AddProjectMember.php @@ -47,7 +47,7 @@ protected function validate($project, string $email, ?string $role) 'email' => $email, 'role' => $role, ], $this->rules(), [ - 'email.exists' => __('We were unable to find a registered user with this email address.'), + 'email.exists' => __('Unable to add member with this email address.'), ])->after( $this->ensureUserIsNotAlreadyOnProject($project, $email) )->validateWithBag('addModelMember'); @@ -80,7 +80,7 @@ protected function ensureUserIsNotAlreadyOnProject($project, string $email) $validator->errors()->addIf( $project->hasUserWithEmail($email), 'email', - __('This user already belongs to the project.') + __('Unable to add member with this email address.') ); }; } diff --git a/app/Actions/Study/AddStudyMember.php b/app/Actions/Study/AddStudyMember.php index dfedef56..2a5fe13b 100644 --- a/app/Actions/Study/AddStudyMember.php +++ b/app/Actions/Study/AddStudyMember.php @@ -47,7 +47,7 @@ protected function validate($study, string $email, ?string $role) 'email' => $email, 'role' => $role, ], $this->rules(), [ - 'email.exists' => __('We were unable to find a registered user with this email address.'), + 'email.exists' => __('Unable to add member with this email address.'), ])->after( $this->ensureUserIsNotAlreadyOnStudy($study, $email) )->validateWithBag('addStudyMember'); @@ -80,7 +80,7 @@ protected function ensureUserIsNotAlreadyOnStudy($study, string $email) $validator->errors()->addIf( $study->hasUserWithEmail($email), 'email', - __('This user already belongs to the study.') + __('Unable to add member with this email address.') ); }; } From 31ea48efed30f2a367545f2e1ac9df8846cbfe37 Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 10:25:54 +0100 Subject: [PATCH 05/30] fix(security): remove owner.email filter from public API endpoints Remove the owner.email filter and sort options from the public data API to prevent user enumeration attacks. Attackers could previously test if emails exist by filtering public data by owner email. --- app/Http/Controllers/API/DataController.php | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/API/DataController.php b/app/Http/Controllers/API/DataController.php index 15c0f13a..84e43236 100644 --- a/app/Http/Controllers/API/DataController.php +++ b/app/Http/Controllers/API/DataController.php @@ -56,7 +56,7 @@ class DataController extends Controller * in="query", * description="Sort field with optional direction prefix (-created_at for descending)", * - * @OA\Schema(type="string", enum={"created_at", "-created_at", "identifier", "-identifier", "owner.email", "-owner.email"}, default="-created_at") + * @OA\Schema(type="string", enum={"created_at", "-created_at", "identifier", "-identifier"}, default="-created_at") * ), * * @OA\Parameter( @@ -76,14 +76,6 @@ class DataController extends Controller * ), * * @OA\Parameter( - * name="filter[owner.email]", - * in="query", - * description="Filter by data owner email", - * - * @OA\Schema(type="string", format="email", example="researcher@university.edu") - * ), - * - * @OA\Parameter( * name="filter[doi]", * in="query", * description="Filter by Digital Object Identifier", @@ -259,8 +251,8 @@ public function all(Request $request, $model) $per_page = \Request::get('per_page') ?: 100; $defaultSort = '-created_at'; - $allowedSorts = ['created_at', 'identifier', 'owner.email']; - $allowedFilters = ['name', 'created_at', 'identifier', 'owner.email', 'doi']; + $allowedSorts = ['created_at', 'identifier']; + $allowedFilters = ['name', 'created_at', 'identifier', 'doi']; if ($model === 'projects') { return ProjectResource::collection( QueryBuilder::for(Project::class) From bf54a1fb2bd53f743981fca06fbe68532beaca8c Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 15:48:11 +0100 Subject: [PATCH 06/30] feat(assets): add university partner logos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add University of Geneva logo - Add Université Paris-Saclay logo These logos are used in the partner cards section to represent natural products community partners. --- public/img/UniversiteDeGeneve.png | Bin 0 -> 14348 bytes public/img/UniversiteParisSaclay.png | Bin 0 -> 6037 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/img/UniversiteDeGeneve.png create mode 100644 public/img/UniversiteParisSaclay.png diff --git a/public/img/UniversiteDeGeneve.png b/public/img/UniversiteDeGeneve.png new file mode 100644 index 0000000000000000000000000000000000000000..0d428d80ff5c9b466281857f031df029b2eb07c4 GIT binary patch literal 14348 zcmV+nIP=GeP)#Eu>W32cT9ioq0|YnfRPCxFs!*Xqg$gs_Y^kyK-%E}6 z{69+13upmx{O^B9RG~tJ3O9ta<;L>aH;sKw^xhDy@#{Ff%tDfD9M+DhLWK$yZUC@W z^*MdihIail5GT9)3JNK92mPz^EtquZ$WHFWdx2uMa(pws)718&^foJ0s8FE{R?gd9t>~ry3{quFcKu)H?e#ojb<5ozuz&SEKOt86 zs7}e4o-yi!QnbgjmQ|qIvDPnlc6J5?3SVm+);VHlp7Fu+b zolUAxp+bdA!UeP5{CM7OPqX}ulPeNPj`0Ocn?DD2epP#=EM{2 z>({SW**$;=-g3suY~=?0q|xuTx3@b~p+bcU*MJ)#*8!m#-JmQ&Ou!g=vHkOQ%YQz4 zeRQA#`j)NJsJXqpp4_G~B9SOUk1gO6ZOogBXe-1N#Ge2$>x33!q>?V1f zo^CWf|MBm3DV4OJ5(90jbv=64;w+B1#w^j=LPt$HM2x)WWf7+*I)=8g# z?y)NB{qR@&A=EHT4EVd>@SJ_xr1f8~9yv;jmW24ZI98zKFZO936FnFnn?AN!CUyqp|4$l@HqGZA;~B zD{pxRW52TwU3UL*!MRL%{(NJf@I93E;E?Q^G7o`~-Gi`A<~4XZwyZtQh%SS8hVL~G z)}UF%@8I{PMwc(ei)np_fQaM2&|WetX&Quv{>YuFv%gPX=i7{>Bf36BbV=YGu8dp@ zNmZ$iA@jly%|U*;ulL|4P?Qwms#Ew=p<|T^=jZDjz`_%ICGPi+l?mRkwfY1%4y@If z16W?VWr?%D&%}A+h_m7Mg&T}F{)GIp{5jU~+AoKP?Wfz@FQ`IkKwJ&WuFMbis!-$M zsQwEhp>cAG6~T(?&@4cbO2$|(Ff_9zcteQzG>&=n@_3y>d+!W(eCfxG$vZ2dFh_hfY<#@_Y`xrT%EPb7AWnS z;UgZT`jW{{hUi)Axu|~w?1K<k!mYR;zlFJ^ zYc*J)UAPm1AXp|Iw|L7Ku7ZaPVVo z(M=;@Yf}5s$q1see>PV0{JMGI(az(X{k)+?2FEtoGCj$AK(Gq|zl&(XR=WKAk>45r z)|aWWa0PQCS7)sr4}F?|ISK3a=boUvt3Yf0=l0J(A7pI@lB=VmqaFfNy#6Debimf< z2PR9Z>$uks)JtrSN%$o)xRh4-GT*6CE#n7ZuvZ}3Gni}?3(y< z%Eh}>0uZ+=8(5U}64Cq(p8h0nJr|s6rKgKKpx{Bt^IotECiA;Kt2b8Tkl@nm(j~zp z)y2MY7r=LHGJNbHd^7G3pB2n4TO}6n&V^h-Rv1zbtyR_KAl26P_D%<%M*Oc(1O!3u zg@UEZ-wb~DYjieNdallsPF%#qSr`9S>YF!($cNOgM=T0)=z~kuk>u2JwU{^$} z`wc$!76-oGIFfBJnVu-S{|Htr-$8G&Oy!H65^TBL2J00+c=iFGs*FM#Jna#cfVc#7 zj!WQ6NtXa3;aBKJNLrxoJzgh#EkL|^QE(7%D?O2om$f`OkyiBlr@z_TgemHOEu2Ξ|Z7Yxq>u*d3|*3e5)3Q-h}n=VbZ7S{I9zd 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 literal 0 HcmV?d00001 From cbe474c6a15978bcb7d7dd19b51382b06fcc2708 Mon Sep 17 00:00:00 2001 From: Venkata Nainala Date: Mon, 2 Feb 2026 15:48:19 +0100 Subject: [PATCH 07/30] feat(faqs): redesign FAQ component with updated content - Update FAQ questions to focus on key user concerns - Add modern accordion-style interface with smooth transitions - Include color-coded states (teal for active, gray for inactive) - Add help links to documentation and support - Remove outdated questions and add new relevant FAQs: * Why should I submit data to nmrXiv? * How nmrXiv is different from Zenodo? * Minimum requirements for submission * Pre-publication data submission * Data ownership rights Follows Vue 3 composition API and Tailwind CSS v3 best practices. --- resources/js/App/FAQs.vue | 124 ++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/resources/js/App/FAQs.vue b/resources/js/App/FAQs.vue index 5b2a2ab6..46dd4fdd 100644 --- a/resources/js/App/FAQs.vue +++ b/resources/js/App/FAQs.vue @@ -1,49 +1,66 @@