Skip to content

Commit

Permalink
Issue #11387: Reintroduce MultiPartCompliance.LEGACY in ee9/ee8
Browse files Browse the repository at this point in the history
  • Loading branch information
joakime committed Feb 7, 2024
1 parent b5e40d7 commit e125ada
Show file tree
Hide file tree
Showing 6 changed files with 1,392 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ public class MultiPartCompliance implements ComplianceViolation.Mode
{
public enum Violation implements ComplianceViolation
{
CONTENT_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7", "Content-Transfer-Encoding is deprecated");
CONTENT_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7", "Content-Transfer-Encoding header is deprecated"),
CR_LINE_TERMINATION("https://tools.ietf.org/html/rfc2046#section-4.1.1", "CR only line termination is forbidden"),
LF_LINE_TERMINATION("https://tools.ietf.org/html/rfc2046#section-4.1.1", "LF only line termination is forbidden"),
NO_CRLF_AFTER_PREAMBLE("https://tools.ietf.org/html/rfc2046#section-5.1.1", "CRLF sequence not allowed after preamble"),
BASE64_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7", "'base64' Content-Transfer-Encoding is deprecated"),
QUOTED_PRINTABLE_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7", "'quoted-printable' Content-Transfer-Encoding is deprecated");

private final String url;
private final String description;
Expand Down Expand Up @@ -55,9 +60,20 @@ public String getDescription()
}
}

/**
* RFC7578 {@code multiPart/form-data} compliant strict parsing.
*/
public static final MultiPartCompliance RFC7578 = new MultiPartCompliance(
"RFC7578", EnumSet.of(Violation.CONTENT_TRANSFER_ENCODING));

/**
* Legacy {@code multiPart/form-data} parsing which is slow, buggy, but forgiving to a fault.
* This mode is not recommended for websites on the public internet.
* It will accept non-compliant preambles and inconsistent line termination that are in violation of RFC7578.
*/
public static final MultiPartCompliance LEGACY = new MultiPartCompliance(
"LEGACY", EnumSet.noneOf(Violation.class));

private static final List<MultiPartCompliance> KNOWN_MODES = Arrays.asList(RFC7578);

public static MultiPartCompliance valueOf(String name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee9.nested;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;

import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.http.Part;
import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.MultiPartCompliance;

/**
* Generic MultiPart Parser
*/
class MultiPart
{
public static MultiPart.Parser newParser(MultiPartCompliance multiPartCompliance,
InputStream inputStream,
String contentType,
MultipartConfigElement config,
File contextTmpDir,
int maxParts) throws IOException
{
switch (multiPartCompliance.getName())
{
case "LEGACY":
return new MultiPartInputStreamLegacyParser(inputStream, contentType, config, contextTmpDir, maxParts);
case "RFC7578":
default:
return new MultiPartFormInputStream(inputStream, contentType, config, contextTmpDir, maxParts);
}
}

public interface Parser
{
void deleteParts();

Part getPart(String name) throws IOException;

Collection<Part> getParts() throws IOException;

List<ComplianceViolation.Event> getNonComplianceWarnings();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
* }</pre>
* @see <a href="https://tools.ietf.org/html/rfc7578">https://tools.ietf.org/html/rfc7578</a>
*/
public class MultiPartFormInputStream
public class MultiPartFormInputStream implements MultiPart.Parser
{
private enum State
{
Expand All @@ -94,14 +94,12 @@ private enum State
DELETED
}

record NonCompliance(ComplianceViolation.Mode mode, MultiPartCompliance.Violation violation, String detail) {}

private static final Logger LOG = LoggerFactory.getLogger(MultiPartFormInputStream.class);
private static final QuotedStringTokenizer QUOTED_STRING_TOKENIZER = QuotedStringTokenizer.builder().delimiters(";").ignoreOptionalWhiteSpace().allowEmbeddedQuotes().build();

private final AutoLock _lock = new AutoLock();
private final MultiMap<Part> _parts = new MultiMap<>();
private final List<NonCompliance> _nonComplianceWarnings = new ArrayList<>();
private final List<ComplianceViolation.Event> _nonComplianceWarnings = new ArrayList<>();
private final InputStream _in;
private final MultipartConfigElement _config;
private final File _contextTmpDir;
Expand All @@ -117,7 +115,8 @@ record NonCompliance(ComplianceViolation.Mode mode, MultiPartCompliance.Violatio
/**
* @return an EnumSet of non compliances with the RFC that were accepted by this parser
*/
public List<NonCompliance> getNonComplianceWarnings()
@Override
public List<ComplianceViolation.Event> getNonComplianceWarnings()
{
return _nonComplianceWarnings;
}
Expand Down Expand Up @@ -487,6 +486,7 @@ private void delete()
* @return the parts
* @throws IOException if unable to get the parts
*/
@Override
public Collection<Part> getParts() throws IOException
{
parse();
Expand All @@ -501,6 +501,7 @@ public Collection<Part> getParts() throws IOException
* @return the parts
* @throws IOException if unable to get the part
*/
@Override
public Part getPart(String name) throws IOException
{
parse();
Expand Down Expand Up @@ -703,7 +704,7 @@ else if (key.equalsIgnoreCase("content-type"))
if (key.equalsIgnoreCase("content-transfer-encoding"))
{
if (!"8bit".equalsIgnoreCase(value) && !"binary".equalsIgnoreCase(value))
_nonComplianceWarnings.add(new NonCompliance(MultiPartCompliance.RFC7578, MultiPartCompliance.Violation.CONTENT_TRANSFER_ENCODING, value));
_nonComplianceWarnings.add(new ComplianceViolation.Event(MultiPartCompliance.RFC7578, MultiPartCompliance.Violation.CONTENT_TRANSFER_ENCODING, value));
}
}

Expand Down
Loading

0 comments on commit e125ada

Please sign in to comment.