Skip to content

Commit 1d9defa

Browse files
committed
Merge remote-tracking branch 'origin/release26.3-SNAPSHOT' into 26.3_fb_tnprc_migration
2 parents 50314bb + 689a835 commit 1d9defa

37 files changed

Lines changed: 576 additions & 359 deletions

.github/pull_request_template.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
<!-- list of standard tasks (remove this comment to enable)
1111
#### Tasks 📍
1212
- [ ] Manual Testing
13-
- [ ] Needs Automation
13+
- [ ] Test Automation
1414
- [ ] Verify Fix
15-
-->
15+
-->

api/src/org/labkey/api/assay/AbstractAssayProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
import javax.script.ScriptEngine;
125125
import java.io.File;
126126
import java.io.IOException;
127+
import java.io.InputStream;
127128
import java.net.URI;
128129
import java.net.URL;
129130
import java.sql.ResultSet;
@@ -1274,9 +1275,9 @@ public Pair<ValidationException, Pair<String, String>> setValidationAndAnalysisS
12741275
if (!(engine instanceof ExternalScriptEngine && ((ExternalScriptEngine) engine).isBinary(scriptFile)))
12751276
{
12761277
String scriptText;
1277-
try
1278+
try (InputStream is = scriptFile.openInputStream())
12781279
{
1279-
scriptText = IOUtils.toString(scriptFile.openInputStream(), StringUtilsLabKey.DEFAULT_CHARSET);
1280+
scriptText = IOUtils.toString(is, StringUtilsLabKey.DEFAULT_CHARSET);
12801281
}
12811282
catch (IOException e)
12821283
{

api/src/org/labkey/api/exp/list/ListService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static void setInstance(ListService ls)
5151
boolean hasLists(Container container, boolean includeProjectAndShared);
5252
ListDefinition createList(Container container, String name, ListDefinition.KeyType keyType);
5353
ListDefinition createList(Container container, String name, ListDefinition.KeyType keyType, @Nullable TemplateInfo templateInfo, @Nullable ListDefinition.Category category);
54+
void deleteLists(Container container, User user, @Nullable String auditUserComment);
5455
@Nullable ListDefinition getList(Container container, int listId);
5556
@Nullable ListDefinition getList(Container container, String name);
5657
@Nullable ListDefinition getList(Container container, String name, boolean includeProjectAndShared);

api/src/org/labkey/api/exp/property/AbstractDomainKind.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ protected boolean getBlanksSql(Domain domain, DomainProperty prop, SQLFragment b
206206
else
207207
{
208208
// Issue 29047
209-
blankRowsSQL.appendIdentifier(columnId).append(" IS NOT NULL");
209+
blankRowsSQL.appendIdentifier(columnId).append(" IS NULL");
210210
}
211211
if (prop.isMvEnabled())
212212
{

api/src/org/labkey/api/mcp/McpService.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
/// ### MCP Development Guide
3030
/// `McpService` lets you expose functionality over the MCP protocol (only simple http for now). This allows external
3131
/// chat sessions to pull information from LabKey Server. Exposed functionality is also made available to chat sessions
32-
/// hosted by LabKey (see `AbstractAgentAction``).
32+
/// hosted by LabKey (see `AbstractAgentAction`).
3333
///
3434
/// ### Adding a new MCP class
3535
/// 1. Create a new class that implements `McpImpl` (see below) in the appropriate module
36-
/// 2. Register that class in your module `init()` method: `McpService.get().register(new MyMcp())`
36+
/// 2. Register that class in your module's `startup()` method: `McpService.get().register(new MyMcp())`
3737
/// 3. Add tools and resources
3838
///
3939
/// ### Adding a new MCP tool
@@ -44,13 +44,13 @@
4444
/// permission annotation is required, otherwise your tool will not be registered.**
4545
/// 4. Add `ToolContext` as the first parameter to the method
4646
/// 5. Add additional required or optional parameters to the method signature, as needed. Note that "required" is the
47-
/// default. Again here, the parameter descriptions are very important. Provide examples.
47+
/// default. Again here, the parameter descriptions are very important. Provide examples of parameter values.
4848
/// 6. Use the helper method `getContext(ToolContext)` to retrieve the current `Container` and `User`
4949
/// 7. Use the helper method `getUser(ToolContext)` in the rare cases where you need just a `User`
5050
/// 8. Perform additional permissions checking (beyond what the annotations offer), where appropriate
5151
/// 9. Filter all results to the current container, of course
5252
/// 10. For any error conditions, throw exceptions with detailed information. These will get translated into appropriate
53-
/// failure responses and the LLM client will attempt to correct the problem.
53+
/// failure responses and the LLM client will attempt to correct any problems (hopefully).
5454
/// 11. For success cases, return a String with a message or JSON content, for example, `JSONObject.toString()`. Spring
5555
/// has some limited ability to convert other objects into JSON strings, but we haven't experimented with that. See
5656
/// `DefaultToolCallResultConverter` and the ability to provide a custom result converter via the `@Tool` annotation.
@@ -126,6 +126,7 @@ static void setInstance(McpService service)
126126

127127
boolean isReady();
128128

129+
// Register MCPs in Module.startup()
129130
default void register(McpImpl mcp)
130131
{
131132
try

api/src/org/labkey/api/module/ModuleLoader.java

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -580,19 +580,6 @@ private void doInit(Execution execution) throws ServletException
580580
throw new IllegalStateException("Core module was not first or could not find the Core module. Ensure that Tomcat user can create directories under the <LABKEY_HOME>/modules directory.");
581581
setProjectRoot(coreModule);
582582

583-
for (Module module : modules)
584-
{
585-
module.registerFilters(_servletContext);
586-
}
587-
for (Module module : modules)
588-
{
589-
module.registerServlets(_servletContext);
590-
}
591-
for (Module module : modules)
592-
{
593-
module.registerFinalServlets(_servletContext);
594-
}
595-
596583
// Do this after we've checked to see if we can find the core module. See issue 22797.
597584
verifyProductionModeMatchesBuild();
598585

@@ -790,12 +777,34 @@ public void addStaticWarnings(@NotNull Warnings warnings, boolean showAllWarning
790777
if (!modulesRequiringUpgrade.isEmpty() || !additionalSchemasRequiringUpgrade.isEmpty())
791778
setUpgradeState(UpgradeState.UpgradeRequired);
792779

793-
// Don't accept any requests if we're bootstrapping empty schemas or migrating from SQL Server
780+
// Don't accept any requests if we're bootstrapping empty schemas or doing a database migration
794781
if (!shouldInsertData())
795782
execution = Execution.Synchronous;
796783

797784
startNonCoreUpgradeAndStartup(execution, lockFile);
798785

786+
// Register filters and servlets at the last minute, just before Tomcat starts. At this point, the list of
787+
// modules is final. We've had one case where the CSP filter was getting initialized before the core module
788+
// was initialized, GitHub Issue 1008. We have no idea how that happened, but registering late won't hurt.
789+
790+
_log.info("Registering filters");
791+
792+
for (Module module : _modules)
793+
{
794+
module.registerFilters(_servletContext);
795+
}
796+
797+
_log.info("Registering servlets");
798+
799+
for (Module module : _modules)
800+
{
801+
module.registerServlets(_servletContext);
802+
}
803+
for (Module module : _modules)
804+
{
805+
module.registerFinalServlets(_servletContext);
806+
}
807+
799808
_log.info("LabKey Server startup is complete; {}", execution.getLogMessage());
800809
}
801810

api/src/org/labkey/api/query/QueryService.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,38 @@
1616

1717
package org.labkey.api.query;
1818

19+
import jakarta.servlet.http.HttpSession;
1920
import org.apache.commons.collections4.SetValuedMap;
2021
import org.jetbrains.annotations.NotNull;
2122
import org.jetbrains.annotations.Nullable;
2223
import org.json.JSONObject;
2324
import org.labkey.api.audit.AuditHandler;
2425
import org.labkey.api.audit.DetailedAuditTypeEvent;
26+
import org.labkey.api.data.ColumnHeaderType;
27+
import org.labkey.api.data.ColumnInfo;
28+
import org.labkey.api.data.CompareType;
29+
import org.labkey.api.data.Container;
30+
import org.labkey.api.data.ContainerFilter;
31+
import org.labkey.api.data.DbSchema;
32+
import org.labkey.api.data.DisplayColumn;
33+
import org.labkey.api.data.Filter;
34+
import org.labkey.api.data.JdbcType;
35+
import org.labkey.api.data.MethodInfo;
36+
import org.labkey.api.data.MutableColumnInfo;
37+
import org.labkey.api.data.ParameterDescription;
38+
import org.labkey.api.data.ParameterDescriptionImpl;
39+
import org.labkey.api.data.QueryLogging;
40+
import org.labkey.api.data.Results;
41+
import org.labkey.api.data.SQLFragment;
42+
import org.labkey.api.data.Sort;
43+
import org.labkey.api.data.SqlSelector;
44+
import org.labkey.api.data.TableInfo;
45+
import org.labkey.api.data.TableSelector;
46+
import org.labkey.api.data.dialect.SqlDialect;
2547
import org.labkey.api.gwt.client.model.GWTPropertyDescriptor;
48+
import org.labkey.api.module.Module;
2649
import org.labkey.api.pipeline.PipelineJob;
2750
import org.labkey.api.query.column.ColumnInfoTransformer;
28-
import org.labkey.api.data.*;
29-
import org.labkey.api.data.dialect.SqlDialect;
30-
import org.labkey.api.module.Module;
3151
import org.labkey.api.query.column.ConceptURIColumnInfoTransformer;
3252
import org.labkey.api.query.snapshot.QuerySnapshotDefinition;
3353
import org.labkey.api.security.User;
@@ -41,7 +61,6 @@
4161
import org.labkey.data.xml.TableType;
4262
import org.springframework.web.servlet.mvc.Controller;
4363

44-
import jakarta.servlet.http.HttpSession;
4564
import java.io.IOException;
4665
import java.sql.ResultSet;
4766
import java.sql.SQLException;
@@ -653,6 +672,13 @@ default MutableColumnInfo applyColumnTransformer(MutableColumnInfo col)
653672
@Nullable
654673
ContainerFilter getContainerFilterForLookups(Container container, User user);
655674

675+
/**
676+
* Provides the configured ContainerFilter to utilize when requesting data that is being read
677+
* within a folder context. Equivalent to the client side function in @labkey/components
678+
*/
679+
@Nullable
680+
ContainerFilter getContainerFilterForFolder(Container container, User user);
681+
656682

657683
interface SelectBuilder
658684
{

api/src/org/labkey/api/study/publish/StudyPublishService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ ActionURL publishData(User user, Container sourceContainer, @Nullable Container
156156

157157
String checkForLockedLinks(Dataset def, @Nullable List<Long> rowIds);
158158

159-
void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Pair<String,Long>> datasetRowLsidAndSourceRowIds);
159+
void addRecallAuditEvent(Container sourceContainer, User user, Dataset def, int rowCount, @Nullable Collection<Long> rowIds);
160160

161161
/**
162162
* Adds columns to an assay data table, providing a link to any datasets that have

api/src/org/labkey/api/util/XmlBeansUtil.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public static void addComment(XmlTokenSource doc, String comment)
132132
public static final SAXParserFactory SAX_PARSER_FACTORY_ALLOWING_DOCTYPE;
133133
public static final XMLInputFactory XML_INPUT_FACTORY;
134134
public static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY;
135+
public static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY_ALLOWING_DOCTYPE;
135136

136137
static
137138
{
@@ -145,16 +146,9 @@ public static void addComment(XmlTokenSource doc, String comment)
145146
SAX_PARSER_FACTORY = saxParserFactory(false);
146147
SAX_PARSER_FACTORY_ALLOWING_DOCTYPE = saxParserFactory(true);
147148

148-
//noinspection XMLInputFactory
149-
DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
150-
DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true);
151-
DOCUMENT_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
152-
DOCUMENT_BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-general-entities", false);
153-
DOCUMENT_BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
154-
DOCUMENT_BUILDER_FACTORY.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
155-
DOCUMENT_BUILDER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
156-
DOCUMENT_BUILDER_FACTORY.setXIncludeAware(false);
157-
DOCUMENT_BUILDER_FACTORY.setExpandEntityReferences(false);
149+
DOCUMENT_BUILDER_FACTORY = documentBuilderFactory(false);
150+
// Use the ALLOWING_DOCTYPE variant when parsing XML that contains a <!DOCTYPE> declaration (e.g. NCBI's eSummary responses)
151+
DOCUMENT_BUILDER_FACTORY_ALLOWING_DOCTYPE = documentBuilderFactory(true);
158152
}
159153
catch (ParserConfigurationException | SAXException e)
160154
{
@@ -181,4 +175,26 @@ private static SAXParserFactory saxParserFactory(boolean allowDocType) throws SA
181175
result.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
182176
return result;
183177
}
178+
179+
private static DocumentBuilderFactory documentBuilderFactory(boolean allowDocType) throws ParserConfigurationException
180+
{
181+
//noinspection XMLInputFactory
182+
DocumentBuilderFactory result = DocumentBuilderFactory.newInstance();
183+
result.setNamespaceAware(true);
184+
185+
// Disable features that could lead to XXE or other vulnerabilities.
186+
// When allowDocType is true the DOCTYPE declaration is permitted. External entity
187+
// resolution remains disabled, so XXE protection is still in effect.
188+
if (!allowDocType)
189+
{
190+
result.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
191+
}
192+
result.setFeature("http://xml.org/sax/features/external-general-entities", false);
193+
result.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
194+
result.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
195+
result.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
196+
result.setXIncludeAware(false);
197+
result.setExpandEntityReferences(false);
198+
return result;
199+
}
184200
}

0 commit comments

Comments
 (0)