Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about UriCompliance.checkUriCompliance #11361

Closed
leonchen83 opened this issue Feb 1, 2024 · 7 comments · Fixed by #11444
Closed

Question about UriCompliance.checkUriCompliance #11361

leonchen83 opened this issue Feb 1, 2024 · 7 comments · Fixed by #11444
Assignees
Labels
Bug For general bugs on Jetty side Enhancement Question

Comments

@leonchen83
Copy link
Contributor

listener.onComplianceViolation(new ComplianceViolation.Event(compliance, violation, uri.toString()));

should above code to be following?

    public static String checkUriCompliance(UriCompliance compliance, HttpURI uri, ComplianceViolation.Listener listener)
    {
        for (UriCompliance.Violation violation : UriCompliance.Violation.values())
        {
            if (uri.hasViolation(violation) && (compliance == null || !compliance.allows(violation))) {
                if (listener != null)
                    listener.onComplianceViolation(new ComplianceViolation.Event(compliance, violation, uri.toString()));
                return violation.getDescription();
            }
        }
        return null;
    }
@joakime
Copy link
Contributor

joakime commented Feb 1, 2024

I do not understand your question.
Can you elaborate?

@gregw
Copy link
Contributor

gregw commented Feb 1, 2024

@joakime The problem is that the current code will return a violation description OR report it to a listener. The proposed code will report a violation to the listener and return the description.

I also this this method should be gated with hasViolations() and should report multiple violations.

@gregw
Copy link
Contributor

gregw commented Feb 1, 2024

@joakime I think something like the following would be a better implementation:

    public static String checkUriCompliance(UriCompliance compliance, HttpURI uri, ComplianceViolation.Listener listener)
    {
        if (uri.hasViolations())
        {
            StringBuilder violations = null;
            for (UriCompliance.Violation violation : UriCompliance.Violation.values())
            {
                if (uri.hasViolation(violation) && (compliance == null || !compliance.allows(violation)))
                {
                    if (listener != null)
                        listener.onComplianceViolation(new ComplianceViolation.Event(compliance, violation, uri.toString()));
                    
                    if (violations == null)
                        violations = new StringBuilder();
                    else 
                        violations.append(", ");
                    violations.append(violation.getDescription());
                }
            }
            if (violations != null)
                return violations.toString();
        }
        return null;
    }

@gregw
Copy link
Contributor

gregw commented Feb 1, 2024

Oh! are we only reporting violations to listeners if they are allows by a compliance mode? Maybe we should always report them to a listener, but only throw if they are not allowed?

Either way, this method needs to be protected with hasViolations so it isn't creating an iterator for every URI that doesn't have a violation.

@gregw
Copy link
Contributor

gregw commented Feb 1, 2024

@joakime would the following be better:

    public static String checkUriCompliance(UriCompliance compliance, HttpURI uri, ComplianceViolation.Listener listener)
    {
        if (uri.hasViolations())
        {
            StringBuilder violations = null;
            for (UriCompliance.Violation violation : UriCompliance.Violation.values())
            {
                if (uri.hasViolation(violation))
                {
                    // always report any violation to the listeners
                    if (listener != null)
                        listener.onComplianceViolation(new ComplianceViolation.Event(compliance, violation, uri.toString()));
                    
                    // Only produce a violation message to throw if the violations is not allowed
                    if (compliance == null || !compliance.allows(violation))
                    {
                        if (violations == null)
                            violations = new StringBuilder();
                        else
                            violations.append(", ");
                        violations.append(violation.getDescription());
                    }
                }
            }
            if (violations != null)
                return violations.toString();
        }
        return null;
    }

@gregw gregw added Enhancement Bug For general bugs on Jetty side labels Feb 1, 2024
@uschindler
Copy link

uschindler commented Feb 19, 2024

I was wondering about this code, too. Yes the listener should always be called.

At the moment I am also thinking of a better way to have "per context" UriCompliance settings. I was about to use the new feature, but stopped using it due to this. At moment I set the global Uri compliance to allow most violations and adding a handler per context to have restructive settings. This looks a bit reversed and has problems when somebody uses encoded ".." to trick the contexts (it is not an issue for me, because its different virtual hosts). I'd more like to relax UriCompliance only for specific contexts. Should I open a different issue, to maybe implement the whole URICompliance stuff as a handler (which is added by default)?

P.S.: I am using this code:

/** should be inserted into the {@code ContextHandler} of webapp or context */
final class UriComplianceHandler extends Handler.Wrapper {
  
  public static final UriCompliance URI_COMPLIANCE_STRICT = UriCompliance.RFC3986;
  public static final UriCompliance URI_COMPLIANCE_RELAXED = new UriCompliance("RELAXED",
      EnumSet.of(Violation.AMBIGUOUS_PATH_SEGMENT, Violation.AMBIGUOUS_PATH_SEPARATOR, Violation.AMBIGUOUS_PATH_ENCODING, Violation.AMBIGUOUS_EMPTY_SEGMENT));
  
  private final UriCompliance compliance;
  
  UriComplianceHandler(UriCompliance compliance) {
    this.compliance = compliance;
  }

  @Override
  public boolean handle(Request request, Response response, Callback callback) throws Exception {
    final var uri = request.getHttpURI();
    if (uri.hasViolations()) {
      String badMessage = UriCompliance.checkUriCompliance(compliance, uri, HttpChannel.from(request).getComplianceViolationListener());
      if (badMessage != null) {
        Response.writeError(request, response, callback, HttpStatus.BAD_REQUEST_400, badMessage);
        return true;
      }
    }
    return super.handle(request, response, callback);
  }
}

In general I tend to think the whole URI compliance should be implemented like this as a handler in the server root.

@joakime
Copy link
Contributor

joakime commented Feb 23, 2024

PR #11444 opened

joakime added a commit that referenced this issue Feb 27, 2024
* Issue #11361 - UriCompliance.checkUriCompliance improvements
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in Jetty 12.0.7 - FROZEN Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side Enhancement Question
Projects
No open projects
Status: ✅ Done
Development

Successfully merging a pull request may close this issue.

4 participants