From af26bffa1b420e31104c5e5318f6de0f7b918690 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Thu, 15 May 2025 16:32:58 -0700 Subject: [PATCH 1/2] Issue 53027: Cannot insert a row into a sample type if Allowed File Extensions are set and no file is provided --- .../api/premium/DefaultAVMultipartResolver.java | 15 ++++++++++++--- api/src/org/labkey/api/util/FileUtil.java | 5 ----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java b/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java index cd3b963cbf4..b54f076d12f 100644 --- a/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java +++ b/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java @@ -3,8 +3,10 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.Part; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.labkey.api.util.FileUtil; +import org.labkey.api.view.UnauthorizedException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest; @@ -22,10 +24,17 @@ public class DefaultAVMultipartResolver extends StandardServletMultipartResolver for (Part part : request.getParts()) { // Filter to just file uploads - if (part.getSubmittedFileName() != null) + if (!StringUtils.isEmpty(part.getSubmittedFileName())) { - FileUtil.checkAllowedFileName(part.getSubmittedFileName(), true); - validate(part); + try + { + FileUtil.checkAllowedFileName(part.getSubmittedFileName(), true); + validate(part); + } + catch (IOException e) + { + throw new UnauthorizedException(e.getMessage()); + } } } } diff --git a/api/src/org/labkey/api/util/FileUtil.java b/api/src/org/labkey/api/util/FileUtil.java index 8446e0c3535..dfabf4fcbbc 100644 --- a/api/src/org/labkey/api/util/FileUtil.java +++ b/api/src/org/labkey/api/util/FileUtil.java @@ -1533,11 +1533,6 @@ else if (ch == '-' && if (ch == ' ' || ch == '.') ret[lastIndex] = '_'; - String result = new String(ret); - - assert !AppProps.getWriteableInstance().isInvalidFilenameBlocked() || isAllowedFileName(result, true) == null : - "Failed to make filename safe. Original: " + name + ", transformed: " + result + ", error: " + isAllowedFileName(result, true); - return new String(ret); } From c49433e43e31b82f2a9237d71ff3722581235164 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Fri, 16 May 2025 13:39:30 -0700 Subject: [PATCH 2/2] Switch exception type, code cleanup, include accepted file extensions --- .../premium/DefaultAVMultipartResolver.java | 11 +++++++- api/src/org/labkey/api/util/FileUtil.java | 2 +- .../MockHttpResponseWithRealPassthrough.java | 25 ++++++++++--------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java b/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java index b54f076d12f..0899c3b8f77 100644 --- a/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java +++ b/api/src/org/labkey/api/premium/DefaultAVMultipartResolver.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.Part; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import org.labkey.api.action.ApiUsageException; import org.labkey.api.util.FileUtil; import org.labkey.api.view.UnauthorizedException; import org.springframework.web.multipart.MultipartException; @@ -33,7 +34,7 @@ public class DefaultAVMultipartResolver extends StandardServletMultipartResolver } catch (IOException e) { - throw new UnauthorizedException(e.getMessage()); + throw new InvalidFileExtensionException(e.getMessage()); } } } @@ -49,4 +50,12 @@ protected void validate(Part part) { //do nothing by default, but give subclasses a chance to override } + + public static class InvalidFileExtensionException extends ApiUsageException + { + public InvalidFileExtensionException(String msg) + { + super(msg); + } + } } diff --git a/api/src/org/labkey/api/util/FileUtil.java b/api/src/org/labkey/api/util/FileUtil.java index dfabf4fcbbc..ff7dece5515 100644 --- a/api/src/org/labkey/api/util/FileUtil.java +++ b/api/src/org/labkey/api/util/FileUtil.java @@ -326,7 +326,7 @@ static String isAllowedFileName(String s, boolean checkFileExtension, AppProps a { String badExtension = checkExtension(s, AppProps.getInstance()); if (badExtension != null) - return "This file type [" + badExtension + "] is not allowed."; + return "This file type [" + badExtension + "] is not allowed. Accepted file extensions: " + AppProps.getInstance().getAllowedExtensions(); } return null; } diff --git a/api/src/org/labkey/api/view/MockHttpResponseWithRealPassthrough.java b/api/src/org/labkey/api/view/MockHttpResponseWithRealPassthrough.java index ef97b11cf24..41f136bbeb8 100644 --- a/api/src/org/labkey/api/view/MockHttpResponseWithRealPassthrough.java +++ b/api/src/org/labkey/api/view/MockHttpResponseWithRealPassthrough.java @@ -15,6 +15,7 @@ */ package org.labkey.api.view; +import org.jetbrains.annotations.NotNull; import org.labkey.api.action.ApiUsageException; import org.springframework.mock.web.MockHttpServletResponse; @@ -50,7 +51,7 @@ public HttpServletResponse getResponse() } @Override - public PrintWriter getWriter() throws UnsupportedEncodingException + public @NotNull PrintWriter getWriter() throws UnsupportedEncodingException { if (_writer == null) { @@ -76,7 +77,7 @@ public SizeLimitExceededException() /** Checks the size of the current output to make sure it hasn't exceeded the limit before appending the new content */ private class SizeLimitingPrintWriter extends PrintWriter { - private PrintWriter _out; + private final PrintWriter _out; public SizeLimitingPrintWriter(PrintWriter out) { @@ -100,28 +101,28 @@ private void checkSize() } @Override - public void write(char[] buf, int off, int len) + public void write(char @NotNull [] buf, int off, int len) { checkSize(); _out.write(buf, off, len); } @Override - public void write(char[] buf) + public void write(char @NotNull [] buf) { checkSize(); _out.write(buf); } @Override - public void write(String s, int off, int len) + public void write(@NotNull String s, int off, int len) { checkSize(); _out.write(s, off, len); } @Override - public void write(String s) + public void write(@NotNull String s) { checkSize(); _out.write(s); @@ -170,7 +171,7 @@ public void print(double d) } @Override - public void print(char[] s) + public void print(char @NotNull [] s) { checkSize(); _out.print(s); @@ -240,7 +241,7 @@ public void println(double x) } @Override - public void println(char[] x) + public void println(char @NotNull [] x) { checkSize(); _out.println(x); @@ -261,28 +262,28 @@ public void println(Object x) } @Override - public PrintWriter printf(String format, Object... args) + public PrintWriter printf(@NotNull String format, Object... args) { checkSize(); return _out.printf(format, args); } @Override - public PrintWriter printf(Locale l, String format, Object... args) + public PrintWriter printf(Locale l, @NotNull String format, Object... args) { checkSize(); return _out.printf(l, format, args); } @Override - public PrintWriter format(String format, Object... args) + public PrintWriter format(@NotNull String format, Object... args) { checkSize(); return _out.format(format, args); } @Override - public PrintWriter format(Locale l, String format, Object... args) + public PrintWriter format(Locale l, @NotNull String format, Object... args) { checkSize(); return _out.format(l, format, args);