From 5a4c48eef6f02e53c9ccb3c3e6310307f17ac663 Mon Sep 17 00:00:00 2001 From: Vagisha Sharma Date: Mon, 29 Jun 2026 16:57:59 -0700 Subject: [PATCH 1/2] Restricted mock NCBI publication search test actions to dev mode * The three actions (SetupMockNcbiService, RestoreNcbiService, RegisterMockPublication) swap a process-wide service and were reachable on production via a forge-able GET (ReadOnlyApiAction does no CSRF check), despite @RequiresSiteAdmin * Added requireDevModeForMockNcbiService(): the actions now return 404 in non-dev mode * Changed actions to MutatingApiAction (correct type for state mutation; also enforces a CSRF token) * Updated PublicationSearchTest to use SimplePostCommand; verified passing with the mock (POST path) and against real NCBI Co-Authored-By: Claude --- .../PanoramaPublicController.java | 20 ++++++++++++++++--- .../panoramapublic/PublicationSearchTest.java | 8 ++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicController.java b/panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicController.java index e04e7e79..3dc17ea5 100644 --- a/panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicController.java +++ b/panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicController.java @@ -32,6 +32,7 @@ import org.labkey.api.action.FormHandlerAction; import org.labkey.api.action.FormViewAction; import org.labkey.api.action.LabKeyError; +import org.labkey.api.action.MutatingApiAction; import org.labkey.api.action.ReadOnlyApiAction; import org.labkey.api.action.ReturnUrlForm; import org.labkey.api.action.SimpleErrorView; @@ -11233,34 +11234,47 @@ public static ActionURL getCatalogImageDownloadUrl(ExperimentAnnotations expAnno // ======================== Support actions for Selenium tests ======================== + // These actions swap the process-wide NCBI publication search service to a mock so that Selenium tests + // run without calling the live NCBI API. They must never be reachable on a production server (non-dev-mode) + private static void requireDevModeForMockNcbiService() + { + if (!AppProps.getInstance().isDevMode()) + { + throw new NotFoundException("Mock NCBI publication search service actions are only available on a server running in dev mode."); + } + } + @RequiresSiteAdmin - public static class SetupMockNcbiServiceAction extends ReadOnlyApiAction + public static class SetupMockNcbiServiceAction extends MutatingApiAction { @Override public Object execute(Object form, BindException errors) { + requireDevModeForMockNcbiService(); NcbiPublicationSearchServiceImpl.setInstance(new MockNcbiPublicationSearchService()); return new ApiSimpleResponse("mock", true); } } @RequiresSiteAdmin - public static class RestoreNcbiServiceAction extends ReadOnlyApiAction + public static class RestoreNcbiServiceAction extends MutatingApiAction { @Override public Object execute(Object form, BindException errors) { + requireDevModeForMockNcbiService(); NcbiPublicationSearchServiceImpl.setInstance(new NcbiPublicationSearchServiceImpl()); return new ApiSimpleResponse("restored", true); } } @RequiresSiteAdmin - public static class RegisterMockPublicationAction extends ReadOnlyApiAction + public static class RegisterMockPublicationAction extends MutatingApiAction { @Override public Object execute(RegisterMockPublicationForm form, BindException errors) { + requireDevModeForMockNcbiService(); NcbiPublicationSearchService service = NcbiPublicationSearchServiceImpl.getInstance(); if (!(service instanceof MockNcbiPublicationSearchService mock)) { diff --git a/panoramapublic/test/src/org/labkey/test/tests/panoramapublic/PublicationSearchTest.java b/panoramapublic/test/src/org/labkey/test/tests/panoramapublic/PublicationSearchTest.java index 93e7b3bd..fcd7314c 100644 --- a/panoramapublic/test/src/org/labkey/test/tests/panoramapublic/PublicationSearchTest.java +++ b/panoramapublic/test/src/org/labkey/test/tests/panoramapublic/PublicationSearchTest.java @@ -5,7 +5,7 @@ import org.junit.experimental.categories.Category; import org.labkey.remoteapi.CommandException; import org.labkey.remoteapi.Connection; -import org.labkey.remoteapi.SimpleGetCommand; +import org.labkey.remoteapi.SimplePostCommand; import org.labkey.remoteapi.query.Filter; import org.labkey.remoteapi.query.SelectRowsCommand; import org.labkey.remoteapi.query.SelectRowsResponse; @@ -341,7 +341,7 @@ private void initMockNcbiService() try { Connection connection = createDefaultConnection(); - SimpleGetCommand command = new SimpleGetCommand("panoramapublic", "setupMockNcbiService"); + SimplePostCommand command = new SimplePostCommand("panoramapublic", "setupMockNcbiService"); command.execute(connection, "/"); _useMockNcbi = true; log("Using mock NCBI service"); @@ -417,7 +417,7 @@ private void registerMockPublication(String database, String id, String searchKe try { Connection connection = createDefaultConnection(); - SimpleGetCommand command = new SimpleGetCommand("panoramapublic", "registerMockPublication"); + SimplePostCommand command = new SimplePostCommand("panoramapublic", "registerMockPublication"); Map params = new HashMap<>(); params.put("database", database); params.put("id", id); @@ -447,7 +447,7 @@ private void restoreNcbiService() try { Connection connection = createDefaultConnection(); - SimpleGetCommand command = new SimpleGetCommand("panoramapublic", "restoreNcbiService"); + SimplePostCommand command = new SimplePostCommand("panoramapublic", "restoreNcbiService"); command.execute(connection, "/"); log("Restored real NCBI service"); } From aec957700d3f086f14ba2dc8768a26e35e20f338 Mon Sep 17 00:00:00 2001 From: Vagisha Sharma Date: Mon, 29 Jun 2026 18:17:49 -0700 Subject: [PATCH 2/2] Removed unused System.out helpers (printStructuralMod, printIsotopicMod) from ExperimentModificationGetter.TestCase --- .../ExperimentModificationGetter.java | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/panoramapublic/src/org/labkey/panoramapublic/proteomexchange/ExperimentModificationGetter.java b/panoramapublic/src/org/labkey/panoramapublic/proteomexchange/ExperimentModificationGetter.java index 19253a17..4cde5676 100644 --- a/panoramapublic/src/org/labkey/panoramapublic/proteomexchange/ExperimentModificationGetter.java +++ b/panoramapublic/src/org/labkey/panoramapublic/proteomexchange/ExperimentModificationGetter.java @@ -345,8 +345,6 @@ public PxIsotopicMod(String skylineName, long dbModId, UnimodModification uMod, public static class TestCase extends Assert { - private static final boolean debug = false; - @Test public void testBuildIsotopeModFormula() { @@ -701,8 +699,6 @@ public void testStructuralMods() throws IOException { PxModification pxMod = getStructuralUnimodMod(mod, uMods); - printStructuralMod(mod, pxMod); - List matches = unimodMatches.get(pxMod.getSkylineName()); if (matches.isEmpty()) { @@ -727,32 +723,6 @@ else if (matches.size() == 1) } } - private void printStructuralMod(Modification mod, PxModification pxMod) - { - if (!debug) - { - return; - } - - if (pxMod.hasUnimodId()) - { - String term = mod.getTerminus() == null ? "" : (mod.getTerminus().equals("N") ? "N-term" : "C-term"); - String modInfo = pxMod.getSkylineName() + ", " + Formula.normalizeFormula(mod.getFormula()) + ", " + mod.getAminoAcid() + ", TERM: " + term; - System.out.print("Skyline: " + modInfo); - System.out.println(" --- " + pxMod.getUnimodId() + ", " + pxMod.getName()); - } - if (pxMod.hasPossibleUnimods()) - { - String term = mod.getTerminus() == null ? "" : (mod.getTerminus().equals("N") ? "N-term" : "C-term"); - String modInfo = pxMod.getSkylineName() + ", " + Formula.normalizeFormula(mod.getFormula()) + ", " + mod.getAminoAcid() + ", TERM: " + term; - System.out.println("Skyline: " + modInfo); - for (UnimodModification umod: pxMod.getPossibleUnimodMatches()) - { - System.out.println(" --- List.of(new UnimodModification(" + umod.getId() + ", \"" + umod.getName() + "\", null))"); - } - } - } - @Test public void testIsotopicMods() throws IOException { @@ -899,8 +869,6 @@ public void testIsotopicMods() throws IOException } PxModification pxMod = getIsotopicUnimodMod(mod, uMods); - printIsotopicMod(mod, pxMod); - List expectedMatches = matches.get(pxMod.getSkylineName()); if (expectedMatches.isEmpty()) @@ -926,31 +894,6 @@ else if (expectedMatches.size() > 1) } } - private void printIsotopicMod(IsotopeModification mod, PxModification pxMod) - { - if (!debug) - { - return; - } - if (pxMod.hasUnimodId()) - { - String term = mod.getTerminus() == null ? "" : (mod.getTerminus().equals("N") ? "N-term" : "C-term"); - String modInfo = pxMod.getSkylineName() + ", " + Formula.normalizeFormula(mod.getFormula()) + ", " + mod.getAminoAcid() + ", TERM: " + term; - System.out.print("Skyline: " + modInfo); - System.out.println(" --- " + pxMod.getUnimodId() + ", " + pxMod.getName()); - } - if (pxMod.hasPossibleUnimods()) - { - String term = mod.getTerminus() == null ? "" : (mod.getTerminus().equals("N") ? "N-term" : "C-term"); - String modInfo = pxMod.getSkylineName() + ", " + Formula.normalizeFormula(mod.getFormula()) + ", " + mod.getAminoAcid() + ", TERM: " + term; - System.out.println("Skyline: " + modInfo); - for (UnimodModification umod: pxMod.getPossibleUnimodMatches()) - { - System.out.println(" --- List.of(new UnimodModification(" + umod.getId() + ", \"" + umod.getName() + "\", null))"); - } - } - } - private Modification createMod(String name, String formula, String sites, String terminus) { Modification mod = new Modification();