Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion api/src/org/labkey/api/attachments/SvgSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.view.NotFoundException;

Expand All @@ -19,6 +20,8 @@ public class SvgSource
{
private final String _filteredSvg;

private Float _height = null;

public SvgSource(String svg)
{
if (StringUtils.isBlank(svg))
Expand All @@ -29,11 +32,19 @@ public SvgSource(String svg)
if (!svg.contains("xmlns=\"" + SVGDOMImplementation.SVG_NAMESPACE_URI + "\"") && !svg.contains("xmlns='" + SVGDOMImplementation.SVG_NAMESPACE_URI + "'"))
svg = svg.replace("<svg", "<svg xmlns='" + SVGDOMImplementation.SVG_NAMESPACE_URI + "'");

int idx = svg.indexOf("height=\"");
if (idx != -1)
{
int heightStart = idx + 8;
int end = svg.indexOf("\"", heightStart);
_height = Float.parseFloat(svg.substring(heightStart, end));
}

// remove xlink:title to prevent org.apache.batik.transcoder.TranscoderException (issue #16173)
svg = svg.replaceAll("xlink:title", "title");

// Reject hrefs. See #45819.
if (StringUtils.containsIgnoreCase(svg, "xlink:href"))
if (Strings.CI.contains(svg, "xlink:href"))
throw new RuntimeException(new TranscoderException("The security settings do not allow any external resources to be referenced from the document"));

_filteredSvg = svg;
Expand All @@ -56,4 +67,9 @@ public Reader getReader()
{
return new StringReader(_filteredSvg);
}

public Float getHeight()
{
return _height;
}
}
15 changes: 11 additions & 4 deletions api/src/org/labkey/api/data/ContainerManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,8 @@ public static List<Container> findAndCheckContainersMatchingTabs(Container c, Fo
return containersMatchingTabs;
}

private static final Set<Container> containersWithBadFolderTypes = new ConcurrentHashSet<>();
private static final Set<Integer> containersWithBadFolderTypes = new ConcurrentHashSet<>();
private static final Set<String> badFolderTypes = new ConcurrentHashSet<>();

@NotNull
public static FolderType getFolderType(Container c)
Expand All @@ -602,10 +603,16 @@ public static FolderType getFolderType(Container c)
// If we're upgrading then folder types won't be defined yet... don't warn in that case.
if (!ModuleLoader.getInstance().isUpgradeInProgress() &&
!ModuleLoader.getInstance().isUpgradeRequired() &&
!containersWithBadFolderTypes.contains(c))
containersWithBadFolderTypes.add(c.getRowId()))
{
LOG.warn("No such folder type " + name + " for folder " + c.toString());
containersWithBadFolderTypes.add(c);
if (badFolderTypes.add(name))
{
LOG.warn("No such folder type {} for container {}. Additional containers of this folder type will be logged at DEBUG level.", name, c.getPath());
}
else
{
LOG.debug("No such folder type {} for container {}.", name, c.getPath());
}
}

folderType = FolderType.NONE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public static void logUnseenAttachmentTypes()
Set<AttachmentType> unseen = new HashSet<>(AttachmentService.get().getAttachmentTypes());
unseen.removeAll(SEEN);

if (SEEN.isEmpty())
if (unseen.isEmpty())
DatabaseMigrationService.LOG.info("All AttachmentTypes have been seen");
else
DatabaseMigrationService.LOG.info("These AttachmentTypes have not been seen: {}", unseen.stream().map(type -> type.getClass().getSimpleName()).collect(Collectors.joining(", ")));
Expand Down
12 changes: 6 additions & 6 deletions api/src/org/labkey/api/pipeline/PipeRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,22 @@ public interface PipeRoot extends SecurableResource
Path resolveToNioPathFromUrl(String url);

/**
* @return the file that's at the given relativePath from the pipeline root. Will be null if the relative path
* attempts to reference something that's not under the root (such as "../../etc/passwd". When the root
* @return the file that's at the given relativePath from the pipeline root. When the root
* is configured with an alternative file path, we'll check to see if the file exists there. If not, we'll return
* a path relative to the root's primary path.
* @throws org.labkey.api.util.FileUtil.InvalidPathReferenceException if the relative path
* attempts to reference something not under the root (such as "../../etc/passwd")
*/
@Deprecated // prefer resolvePathToFileLike()
@Nullable
File resolvePath(String relativePath);

/**
* @return the file that's at the given relativePath from the pipeline root. Will be null if the relative path
* attempts to reference something that's not under the root (such as "../../etc/passwd". When the root
* @return the file that's at the given relativePath from the pipeline root. When the root
* is configured with an alternative file path, we'll check to see if the file exists there. If not, we'll return
* a path relative to the root's primary path.
* @throws org.labkey.api.util.FileUtil.InvalidPathReferenceException if the relative path
* attempts to reference something not under the root (such as "../../etc/passwd")
*/
@Nullable
FileLike resolvePathToFileLike(String relativePath);

@NotNull
Expand Down
16 changes: 15 additions & 1 deletion api/src/org/labkey/api/products/ProductRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.labkey.api.products;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -219,7 +220,9 @@ public String getPrimaryProductIdForContainer(@NotNull Container container)
public ProductMenuProvider getPrimaryProductMenuForContainer(@NotNull Container container)
{
List<String> productIds = getProductIdsForContainer(container);
_logger.debug("Product Ids in container '{}' of type '{}' are {}", container.getPath(), container.getFolderType().getName(), StringUtils.join(productIds));
List<ProductMenuProvider> providers = getRegisteredProducts().stream().filter(provider -> productIds.contains(provider.getProductId())).toList();
_logger.debug("Menu providers: {}", providers.stream().map(ProductMenuProvider::getProductId).collect(Collectors.toList()));
if (providers.isEmpty())
return null;

Expand All @@ -229,16 +232,27 @@ public ProductMenuProvider getPrimaryProductMenuForContainer(@NotNull Container
// see if there's a provider that matches the folder type (need to check this first so if LabKey LIMS or LKB is the configured product you can still show LKSM folders)
Optional<ProductMenuProvider> optionalProvider = providers.stream().filter(provider -> provider.getFolderTypeName() != null && provider.getFolderTypeName().equals(container.getFolderType().getName())).findFirst();
if (optionalProvider.isPresent())
{
_logger.debug("Found product menu provider for folder type '{}'", container.getFolderType().getName());
return optionalProvider.get();
}

List<String> orderedProducts = getProducts(true, false).stream().filter(Product::isEnabled).map(Product::getProductGroupId).toList();
ProductMenuProvider highestProvider = providers.stream().min(Comparator.comparing(provider -> orderedProducts.indexOf(provider.getProductId()))).orElse(null);
_logger.debug("Products are {}", _products.keySet());
_logger.debug("Ordered products are {}", orderedProducts);
ProductMenuProvider highestProvider = providers.stream()
.min(Comparator.comparing(provider -> orderedProducts.indexOf(provider.getProductId()))).orElse(null);
_logger.debug("Highest product menu provider: {}", highestProvider.getProductId());
// then see if there's a provider that matches the configured product
Product product = new ProductConfiguration().getCurrentProduct();
if (product == null)
{
_logger.debug("Using highest product menu provider.");
return highestProvider;
}

optionalProvider = providers.stream().filter(provider -> provider.getProductId().equals(product.getProductGroupId())).findFirst();
_logger.debug("Product from product group: {}", optionalProvider.isPresent() ? optionalProvider.get() : "null");
// if neither of those is true (when can this happen?), use the highest provider
return optionalProvider.orElseGet(() -> highestProvider);
}
Expand Down
4 changes: 4 additions & 0 deletions api/src/org/labkey/api/settings/ProductConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package org.labkey.api.settings;

import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.data.ContainerManager;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.products.Product;
import org.labkey.api.products.ProductRegistry;
import org.labkey.api.util.logging.LogHelper;

import java.util.Collection;

public class ProductConfiguration extends AbstractWriteableSettingsGroup implements StartupProperty
{
public static final String SCOPE_PRODUCT_CONFIGURATION = "ProductConfiguration";
public static final String PROPERTY_NAME = "productKey";
private static final Logger _logger = LogHelper.getLogger(ProductConfiguration.class, "Product Configuration properties");

@Override
protected String getGroupName()
Expand Down Expand Up @@ -54,6 +57,7 @@ public String getCurrentProductKey()
public Product getCurrentProduct()
{
String productKey = getCurrentProductKey();
_logger.debug("Current product key: {}", productKey);
if (productKey == null)
return null;
return ProductRegistry.getProduct(productKey);
Expand Down
15 changes: 15 additions & 0 deletions api/src/org/labkey/api/util/ExceptionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,21 @@ else if (ex instanceof BadRequestException)
message = ex.getMessage();
unhandledException = null;
}
else if (ex instanceof FileUtil.InvalidPathReferenceException ipre)
{
responseStatus = HttpServletResponse.SC_NOT_FOUND;
errorType = ErrorRenderer.ErrorType.notFound;
if (ex.getMessage() != null)
{
message = ex.getMessage();
responseStatusMessage = message;
}
else
message = responseStatus + ": Page not Found";
unhandledException = null;
log.info("InvalidPathReferenceException: {}", ipre.getInput());
log.debug("InvalidPathReferenceException", ex);
}
else if (ex instanceof NotFoundException)
{
responseStatus = HttpServletResponse.SC_NOT_FOUND;
Expand Down
38 changes: 30 additions & 8 deletions api/src/org/labkey/api/util/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -852,11 +852,20 @@ public static File appendPath(File dir, org.labkey.api.util.Path originalPath)
{
org.labkey.api.util.Path path = originalPath.normalize();
if (path == null || (!path.isEmpty() && "..".equals(path.get(0))))
throw new InvalidPathException(originalPath.toString(), "Path to parent not allowed");
throw new InvalidPathReferenceException(originalPath.toString(), "Path to parent not allowed");
@SuppressWarnings("SSBasedInspection")
var ret = new File(dir, path.toString());
if (!ret.toPath().normalize().startsWith(dir.toPath().normalize()))
throw new InvalidPathException(originalPath.toString(), "Path to parent not allowed");
boolean valid;
try
{
valid = ret.toPath().normalize().startsWith(dir.toPath().normalize());
}
catch (InvalidPathException e)
{
throw new InvalidPathReferenceException(originalPath.toString(), e.getMessage());
}
if (!valid)
throw new InvalidPathReferenceException(originalPath.toString(), "Path to parent not allowed");
return ret;
}

Expand All @@ -866,7 +875,7 @@ public static FileLike appendPath(FileLike dir, org.labkey.api.util.Path path)
{
path = path.normalize();
if (!path.isEmpty() && "..".equals(path.get(0)))
throw new InvalidPathException(path.toString(), "Path to parent not allowed");
throw new InvalidPathReferenceException(path.toString(), "Path to parent not allowed");
return dir.resolveFile(path);
}

Expand Down Expand Up @@ -897,7 +906,7 @@ public static File appendName(File dir, String name)
var ret = new File(dir, name);

if (!ret.toPath().normalize().startsWith(dir.toPath().normalize()))
throw new InvalidPathException(name, "Path to parent not allowed");
throw new InvalidPathReferenceException(name, "Path to parent not allowed");
return ret;
}

Expand All @@ -908,7 +917,7 @@ public static Path appendName(Path dir, String name)
var ret = dir.resolve(name);

if (!ret.normalize().startsWith(dir.normalize()))
throw new InvalidPathException(name, "Path to parent not allowed");
throw new InvalidPathReferenceException(name, "Path to parent not allowed");
return ret;
}

Expand All @@ -919,11 +928,24 @@ public static void legalPathPartThrow(String name)
{
int invalidCharacterIndex = StringUtils.indexOfAny(name, '/', File.separatorChar);
if (invalidCharacterIndex >= 0)
throw new InvalidPathException(name, "Invalid file or directory name", invalidCharacterIndex);
throw new InvalidPathReferenceException(name, "Invalid file or directory name", invalidCharacterIndex);
if (".".equals(name) || "..".equals(name))
throw new InvalidPathException(name, "Invalid file or directory name");
throw new InvalidPathReferenceException(name, "Invalid file or directory name");
}

/** Our own subclass for bogus paths that we can special-case in ExceptionUtil in terms of HTTP response codes and logging */
public static class InvalidPathReferenceException extends InvalidPathException
{
public InvalidPathReferenceException(String path, String reason)
{
super(path, reason);
}

public InvalidPathReferenceException(String name, String reason, int index)
{
super(name, reason, index);
}
}

public static String decodeSpaces(@NotNull String str)
{
Expand Down
8 changes: 4 additions & 4 deletions assay/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion assay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"clean": "rimraf resources/web/assay/gen && rimraf resources/views/gen && rimraf resources/web/gen"
},
"dependencies": {
"@labkey/components": "6.70.2"
"@labkey/components": "6.70.8"
},
"devDependencies": {
"@labkey/build": "8.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@
import org.labkey.api.data.SimpleFilter.SQLClause;
import org.labkey.api.data.SqlSelector;
import org.labkey.api.data.TableInfo;
import org.labkey.api.data.TableSelector;
import org.labkey.api.migration.AssaySkipContainers;
import org.labkey.api.migration.DatabaseMigrationService.DataFilter;
import org.labkey.api.migration.DefaultMigrationSchemaHandler;
import org.labkey.api.migration.ExperimentDeleteService;
import org.labkey.api.util.GUID;
import org.labkey.api.util.StringUtilsLabKey;
import org.labkey.api.util.logging.LogHelper;
import org.labkey.assay.plate.PlateReplicateStatsDomainKind;

import java.util.Collection;
import java.util.Collections;
import java.util.Set;

class AssayResultMigrationSchemaHandler extends DefaultMigrationSchemaHandler
Expand All @@ -38,7 +35,7 @@ public AssayResultMigrationSchemaHandler()
private boolean skipTable(TableInfo sourceTable)
{
// For now, we're ignoring this table since it's empty in our first migration client's database
return Strings.CI.endsWith(sourceTable.getName(), PlateReplicateStatsDomainKind.ASSAY_PLATE_REPLICATE);
return Strings.CI.endsWith(sourceTable.getName(), PlateReplicateStatsDomainKind.NAME);
}

@Override
Expand All @@ -63,8 +60,8 @@ public FilterClause getContainerClause(TableInfo sourceTable, Set<GUID> containe
@Override
public void addDomainDataFilterClause(OrClause orClause, DataFilter filter, TableInfo sourceTable, Set<String> selectColumnNames)
{
// No filtering on assay results for now; just add the passed in containers. Note that these will be filtered
// if AssaySkipContainers is configured.
// No row-by-row filtering on assay results for now; just add the passed in containers. Note that these will be
// filtered by container if AssaySkipContainers is configured.
orClause.addClause(getContainerClause(sourceTable, filter.containers()));
}

Expand Down Expand Up @@ -92,9 +89,6 @@ public void afterTable(TableInfo sourceTable, TableInfo targetTable, SimpleFilte
// Delete exp.Data, exp.Object, etc. rows associated with the rows that weren't copied
ExperimentDeleteService.get().deleteDataRows(notCopiedObjectIds);
}

// TODO: Temp!
LOG.info(" " + StringUtilsLabKey.pluralize(new TableSelector(sourceTable, Collections.singleton("DataId")).stream(Integer.class).distinct().count(), "distinct DataId"));
}
}
}
8 changes: 4 additions & 4 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading