ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/valves/rewrite/RewriteValve.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/valves/rewrite/RewriteValve.java
Status
scanned
Type
file
Name
RewriteValve.java
Extension
.java
Programming language
Java
Mime type
text/html
File type
HTML document, ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
41776 (40.8 KB)
MD5
688c8ffde341dfd7be2dc29998d57be8
SHA1
9d6b12fd7678344559254b2b0221e36a8766ecb8
SHA256
4c2c55882fc9e0e42376dd4819870c792ede38d19d002d9c4f5163480221e73b
SHA512

      
    
SHA1_git
6287bfedab8b93f949fbffb329b83c1429ce0c1d
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
RewriteValve.java | 40.8 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.valves.rewrite; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.Pipeline; import org.apache.catalina.Valve; import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.util.URLEncoder; import org.apache.catalina.valves.ValveBase; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.CharChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.buf.UriUtil; import org.apache.tomcat.util.file.ConfigFileLoader; import org.apache.tomcat.util.file.ConfigurationSource; import org.apache.tomcat.util.http.RequestUtil; /** * Note: Extra caution should be used when adding a Rewrite Rule. When specifying a regex to match for in a Rewrite * Rule, certain regex could allow an attacker to DoS your server, as Java's regex parsing is vulnerable to * "catastrophic backtracking" (also known as "Regular expression Denial of Service", or ReDoS). There are some open * source tools to help detect vulnerable regex, though in general it is a hard problem. A good defence is to use a * regex debugger on your desired regex, and read more on the subject of catastrophic backtracking. * * @see <a href= "https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS">OWASP ReDoS</a> */ public class RewriteValve extends ValveBase { private static final URLEncoder REWRITE_DEFAULT_ENCODER; private static final URLEncoder REWRITE_QUERY_ENCODER; static { /* * See the detailed explanation of encoding/decoding during URL re-writing in the invoke() method. * * These encoders perform the second stage of encoding, after re-writing has completed. These rewrite specific * encoders treat '%' as a safe character so that URLs and query strings already processed by encodeForRewrite() * do not end up with double encoding of '%' characters. */ REWRITE_DEFAULT_ENCODER = (URLEncoder) URLEncoder.DEFAULT.clone(); REWRITE_DEFAULT_ENCODER.addSafeCharacter('%'); REWRITE_QUERY_ENCODER = (URLEncoder) URLEncoder.QUERY.clone(); REWRITE_QUERY_ENCODER.addSafeCharacter('%'); } /** * The rewrite rules that the valve will use. */ protected RewriteRule[] rules = null; /** * If rewriting occurs, the whole request will be processed again. */ protected ThreadLocal<Boolean> invoked = new ThreadLocal<>(); /** * Relative path to the configuration file. Note: If the valve's container is a context, this will be relative to * /WEB-INF/. */ protected String resourcePath = "rewrite.config"; /** * Will be set to true if the valve is associated with a context. */ protected boolean context = false; /** * enabled this component */ protected boolean enabled = true; /** * Maps to be used by the rules. */ protected Map<String,RewriteMap> maps = new ConcurrentHashMap<>(); /** * Maps configuration. */ protected ArrayList<String> mapsConfiguration = new ArrayList<>(); public RewriteValve() { super(true); } public boolean getEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override protected void initInternal() throws LifecycleException { super.initInternal(); containerLog = LogFactory.getLog(getContainer().getLogName() + ".rewrite"); } @Override protected void startInternal() throws LifecycleException { super.startInternal(); InputStream is = null; // Process configuration file for this valve if (getContainer() instanceof Context) { context = true; String webInfResourcePath = "/WEB-INF/" + resourcePath; is = ((Context) getContainer()).getServletContext().getResourceAsStream(webInfResourcePath); if (is == null) { if (containerLog.isInfoEnabled()) { containerLog.info(sm.getString("rewriteValve.noConfiguration", webInfResourcePath)); } } else { if (containerLog.isDebugEnabled()) { containerLog.debug(sm.getString("rewriteValve.readConfiguration", webInfResourcePath)); } } } else { String resourceName = Container.getConfigPath(getContainer(), resourcePath); try { ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getResource(resourceName); is = resource.getInputStream(); } catch (IOException ioe) { if (containerLog.isInfoEnabled()) { containerLog.info(sm.getString("rewriteValve.noConfiguration", resourceName), ioe); } } } if (is == null) { // Will use management operations to configure the valve dynamically return; } try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader reader = new BufferedReader(isr)) { parse(reader); } catch (IOException ioe) { containerLog.error(sm.getString("rewriteValve.closeError"), ioe); } finally { try { is.close(); } catch (IOException ioe) { containerLog.error(sm.getString("rewriteValve.closeError"), ioe); } } } public void setConfiguration(String configuration) throws Exception { if (containerLog == null) { containerLog = LogFactory.getLog(getContainer().getLogName() + ".rewrite"); } for (RewriteMap map : maps.values()) { if (map instanceof Lifecycle) { ((Lifecycle) map).stop(); } } maps.clear(); parse(new BufferedReader(new StringReader(configuration))); } public String getConfiguration() { StringBuilder buffer = new StringBuilder(); for (String mapConfiguration : mapsConfiguration) { buffer.append(mapConfiguration).append(" "); } if (!mapsConfiguration.isEmpty()) { buffer.append(" "); } for (RewriteRule rule : rules) { for (int j = 0; j < rule.getConditions().length; j++) { buffer.append(rule.getConditions()[j].toString()).append(" "); } buffer.append(rule.toString()).append(" ").append(" "); } return buffer.toString(); } protected void parse(BufferedReader reader) throws LifecycleException { List<RewriteRule> rules = new ArrayList<>(); List<RewriteCond> conditions = new ArrayList<>(); ArrayList<String> mapsConfiguration = new ArrayList<>(); while (true) { try { String line = reader.readLine(); if (line == null) { break; } Object result = parse(line); if (result instanceof RewriteRule rule) { if (containerLog.isTraceEnabled()) { containerLog.trace("Add rule with pattern " + rule.getPatternString() + " and substitution " + rule.getSubstitutionString()); } for (int i = (conditions.size() - 1); i > 0; i--) { if (conditions.get(i - 1).isOrnext()) { conditions.get(i).setOrnext(true); } } for (RewriteCond condition : conditions) { if (containerLog.isTraceEnabled()) { containerLog.trace("Add condition " + condition.getCondPattern() + " test " + condition.getTestString() + " to rule with pattern " + rule.getPatternString() + " and substitution " + rule.getSubstitutionString() + (condition.isOrnext() ? " [OR]" : "") + (condition.isNocase() ? " [NC]" : "")); } rule.addCondition(condition); } conditions.clear(); rules.add(rule); } else if (result instanceof RewriteCond) { conditions.add((RewriteCond) result); } else if (result instanceof Object[]) { String mapName = (String) ((Object[]) result)[0]; RewriteMap map = (RewriteMap) ((Object[]) result)[1]; maps.put(mapName, map); // Keep the original configuration line as it is not possible to get // the parameters back without an API change mapsConfiguration.add(line); if (map instanceof Lifecycle) { ((Lifecycle) map).start(); } } } catch (IOException ioe) { containerLog.error(sm.getString("rewriteValve.readError"), ioe); } } this.mapsConfiguration = mapsConfiguration; // Finish parsing the rules for (RewriteRule rule : rules) { rule.parse(maps); } this.rules = rules.toArray(new RewriteRule[0]); } @Override protected void stopInternal() throws LifecycleException { super.stopInternal(); for (RewriteMap map : maps.values()) { if (map instanceof Lifecycle) { ((Lifecycle) map).stop(); } } maps.clear(); rules = null; } @Override public void invoke(Request request, Response response) throws IOException, ServletException { if (!getEnabled() || rules == null || rules.length == 0) { getNext().invoke(request, response); return; } if (Boolean.TRUE.equals(invoked.get())) { try { getNext().invoke(request, response); } finally { invoked.set(null); } return; } try { Resolver resolver = new ResolverImpl(request, containerLog); invoked.set(Boolean.TRUE); // As long as MB isn't a char sequence or affiliated, this has to be converted to a string Charset uriCharset = request.getConnector().getURICharset(); String queryStringOriginalEncoded = request.getQueryString(); MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB(); urlMB.toChars(); CharSequence urlDecoded = urlMB.getCharChunk(); /* * The URL presented to the rewrite valve is the URL that is used for request mapping. That URL has been * processed to: remove path parameters; remove the query string; decode; and normalize the URL. It may * contain literal '%', '?' and/or ';' characters at this point. * * The re-write rules need to be able to process URLs with literal '?' characters and add query strings * without the two becoming confused. The re-write rules also need to be able to insert literal '%' * characters without them being confused with %nn encoding. * * To meet these requirement, the URL is processed as follows. * * Step 1. The URL is partially re-encoded by encodeForRewrite(). This method encodes any literal '%', ';' * and/or '?' characters in the URL using the standard %nn form. * * Step 2. The re-write processing runs with the provided re-write rules against the partially encoded URL. * If a re-write rule needs to insert a literal '%', ';' or '?', it must do so in %nn encoded form. * * Step 3. The URL (and query string if present) is re-encoded using the re-write specific encoders * (REWRITE_DEFAULT_ENCODER and REWRITE_QUERY_ENCODER) that behave the same was as the standard encoders * apart from '%' being treated as a safe character. This prevents double encoding of any '%' characters * present in the URL from steps 1 or 2. */ // Step 1. Encode URL for processing by the re-write rules. CharSequence urlRewriteEncoded = encodeForRewrite(urlDecoded); CharSequence host = request.getServerName(); boolean rewritten = false; boolean done = false; boolean qsa = false; boolean qsd = false; boolean valveSkip = false; // Step 2. Process the URL using the re-write rules. for (int i = 0; i < rules.length; i++) { RewriteRule rule = rules[i]; CharSequence test = (rule.isHost()) ? host : urlRewriteEncoded; CharSequence newtest = rule.evaluate(test, resolver); if (newtest != null && !Objects.equals(test.toString(), newtest.toString())) { if (containerLog.isTraceEnabled()) { containerLog.trace( "Rewrote " + test + " as " + newtest + " with rule pattern " + rule.getPatternString()); } if (rule.isHost()) { host = newtest; } else { urlRewriteEncoded = newtest; } rewritten = true; } // Check QSA before the final reply if (!qsa && newtest != null && rule.isQsappend()) { qsa = true; } if (!qsd && newtest != null && rule.isQsdiscard()) { qsd = true; } if (!valveSkip && newtest != null && rule.isValveSkip()) { valveSkip = true; } // Final reply // - forbidden if (rule.isForbidden() && newtest != null) { response.sendError(HttpServletResponse.SC_FORBIDDEN); done = true; break; } // - gone if (rule.isGone() && newtest != null) { response.sendError(HttpServletResponse.SC_GONE); done = true; break; } // - redirect (code) if (rule.isRedirect() && newtest != null) { // Append the query string to the url if there is one and it // hasn't been rewritten String urlStringRewriteEncoded = urlRewriteEncoded.toString(); int index = urlStringRewriteEncoded.indexOf('?'); String rewrittenQueryStringRewriteEncoded; if (index == -1) { rewrittenQueryStringRewriteEncoded = null; } else { rewrittenQueryStringRewriteEncoded = urlStringRewriteEncoded.substring(index + 1); urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, index); } // Step 3. Complete the 2nd stage to encoding. StringBuilder urlStringEncoded = new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset)); if (!qsd && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) { if (rewrittenQueryStringRewriteEncoded == null) { urlStringEncoded.append('?'); urlStringEncoded.append(queryStringOriginalEncoded); } else { if (qsa) { // if qsa is specified append the query urlStringEncoded.append('?'); urlStringEncoded.append( REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); urlStringEncoded.append('&'); urlStringEncoded.append(queryStringOriginalEncoded); } else if (index == urlStringEncoded.length() - 1) { // if the ? is the last character delete it, its only purpose was to // prevent the rewrite module from appending the query string urlStringEncoded.deleteCharAt(index); } else { urlStringEncoded.append('?'); urlStringEncoded.append( REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); } } } else if (rewrittenQueryStringRewriteEncoded != null) { urlStringEncoded.append('?'); urlStringEncoded .append(REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); } // Insert the context if // 1. this valve is associated with a context // 2. the url starts with a leading slash // 3. the url isn't absolute if (context && urlStringEncoded.charAt(0) == '/' && !UriUtil.hasScheme(urlStringEncoded)) { urlStringEncoded.insert(0, request.getContext().getEncodedPath()); } String redirectPath; if (rule.isNoescape()) { redirectPath = UDecoder.URLDecode(urlStringEncoded.toString(), uriCharset); } else { redirectPath = urlStringEncoded.toString(); } response.sendRedirect(response.encodeRedirectURL(redirectPath)); response.setStatus(rule.getRedirectCode()); done = true; break; } // Reply modification // - cookie if (rule.isCookie() && newtest != null) { Cookie cookie = new Cookie(rule.getCookieName(), rule.getCookieResult()); cookie.setDomain(rule.getCookieDomain()); cookie.setMaxAge(rule.getCookieLifetime()); cookie.setPath(rule.getCookiePath()); cookie.setSecure(rule.isCookieSecure()); cookie.setHttpOnly(rule.isCookieHttpOnly()); response.addCookie(cookie); } // - env (note: this sets a request attribute) if (rule.isEnv() && newtest != null) { for (int j = 0; j < rule.getEnvSize(); j++) { request.setAttribute(rule.getEnvName(j), rule.getEnvResult(j)); } } // - content type (note: this will not force the content type, use a filter // to do that) if (rule.isType() && newtest != null) { response.setContentType(rule.getTypeValue()); } // Control flow processing // - chain (skip remaining chained rules if this one does not match) if (rule.isChain() && newtest == null) { for (int j = i; j < rules.length; j++) { if (!rules[j].isChain()) { i = j; break; } } continue; } // - last (stop rewriting here) if (rule.isLast() && newtest != null) { break; } // - next (redo again) if (rule.isNext() && newtest != null) { i = 0; continue; } // - skip (n rules) if (newtest != null) { i += rule.getSkip(); } } if (rewritten) { if (!done) { // See if we need to replace the query string String urlStringRewriteEncoded = urlRewriteEncoded.toString(); String queryStringRewriteEncoded = null; int queryIndex = urlStringRewriteEncoded.indexOf('?'); if (queryIndex != -1) { queryStringRewriteEncoded = urlStringRewriteEncoded.substring(queryIndex + 1); urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, queryIndex); } // Parse path parameters from rewrite production and populate request path parameters urlStringRewriteEncoded = org.apache.catalina.util.RequestUtil.stripPathParams(urlStringRewriteEncoded, request); // Save the current context path before re-writing starts String contextPath = null; if (context) { contextPath = request.getContextPath(); } // Populated the encoded (i.e. undecoded) requestURI request.getCoyoteRequest().requestURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); CharChunk chunk = request.getCoyoteRequest().requestURI().getCharChunk(); if (context) { // This is neither decoded nor normalized chunk.append(contextPath); } // Step 3. Complete the 2nd stage to encoding. chunk.append(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset)); // Rewriting may have denormalized the URL and added encoded characters // Decode then normalize String urlStringRewriteDecoded = URLDecoder.decode(urlStringRewriteEncoded, uriCharset); urlStringRewriteDecoded = RequestUtil.normalize(urlStringRewriteDecoded); request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); chunk = request.getCoyoteRequest().decodedURI().getCharChunk(); if (context) { // This is decoded and normalized chunk.append(request.getServletContext().getContextPath()); } chunk.append(urlStringRewriteDecoded); // Set the new Query String if (queryStringRewriteEncoded == null) { // No new query string. Therefore the original is retained unless QSD is defined. if (qsd) { request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); } } else { // New query string. Therefore the original is dropped unless QSA is defined (and QSD is not). request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); chunk = request.getCoyoteRequest().queryString().getCharChunk(); chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset)); if (qsa && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) { chunk.append('&'); chunk.append(queryStringOriginalEncoded); } } // Set the new host if it changed if (!host.equals(request.getServerName())) { request.getCoyoteRequest().serverName().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); chunk = request.getCoyoteRequest().serverName().getCharChunk(); chunk.append(host.toString()); } request.getMappingData().recycle(); request.recycleSessionInfo(); // Reinvoke the whole request recursively Connector connector = request.getConnector(); try { if (!connector.getProtocolHandler().getAdapter().prepare(request.getCoyoteRequest(), response.getCoyoteResponse())) { return; } } catch (Exception e) { // This doesn't actually happen in the Catalina adapter implementation } Pipeline pipeline = connector.getService().getContainer().getPipeline(); request.setAsyncSupported(pipeline.isAsyncSupported()); pipeline.getFirst().invoke(request, response); } } else { Valve next = getNext(); if (valveSkip) { next = next.getNext(); if (next == null) { // Ignore and invoke the next valve normally next = getNext(); } } next.invoke(request, response); } } finally { invoked.set(null); } } /** * This factory method will parse a line formed like: * <p> * Example: {@code RewriteCond %{REMOTE_HOST} ^host1.* [OR]} * * @param line A line from the rewrite configuration * * @return The condition, rule or map resulting from parsing the line */ public static Object parse(String line) { QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(line); if (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("RewriteCond")) { // RewriteCond TestString CondPattern [Flags] RewriteCond condition = new RewriteCond(); if (tokenizer.countTokens() < 2) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidLine", line)); } condition.setTestString(tokenizer.nextToken()); condition.setCondPattern(tokenizer.nextToken()); if (tokenizer.hasMoreTokens()) { String flags = tokenizer.nextToken(); condition.setFlagsString(flags); if (flags.startsWith("[") && flags.endsWith("]")) { flags = flags.substring(1, flags.length() - 1); } StringTokenizer flagsTokenizer = new StringTokenizer(flags, ","); while (flagsTokenizer.hasMoreElements()) { parseCondFlag(line, condition, flagsTokenizer.nextToken()); } } return condition; } else if (token.equals("RewriteRule")) { // RewriteRule Pattern Substitution [Flags] RewriteRule rule = new RewriteRule(); if (tokenizer.countTokens() < 2) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidLine", line)); } rule.setPatternString(tokenizer.nextToken()); rule.setSubstitutionString(tokenizer.nextToken()); if (tokenizer.hasMoreTokens()) { String flags = tokenizer.nextToken(); rule.setFlagsString(flags); if (flags.startsWith("[") && flags.endsWith("]")) { flags = flags.substring(1, flags.length() - 1); } StringTokenizer flagsTokenizer = new StringTokenizer(flags, ","); while (flagsTokenizer.hasMoreElements()) { parseRuleFlag(line, rule, flagsTokenizer.nextToken()); } // If QSD and QSA are present, QSD always takes precedence if (rule.isQsdiscard()) { rule.setQsappend(false); } } return rule; } else if (token.equals("RewriteMap")) { // RewriteMap name rewriteMapClassName whateverOptionalParameterInWhateverFormat if (tokenizer.countTokens() < 2) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidLine", line)); } String name = tokenizer.nextToken(); String rewriteMapClassName = tokenizer.nextToken(); RewriteMap map = null; if (rewriteMapClassName.startsWith("int:")) { map = InternalRewriteMap.toMap(rewriteMapClassName.substring("int:".length())); } else if (rewriteMapClassName.startsWith("txt:")) { map = new RandomizedTextRewriteMap(rewriteMapClassName.substring("txt:".length()), false); } else if (rewriteMapClassName.startsWith("rnd:")) { map = new RandomizedTextRewriteMap(rewriteMapClassName.substring("rnd:".length()), true); } else if (rewriteMapClassName.startsWith("prg:")) { // https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#prg // Not worth implementing further since this is a simpler CGI // piping stdin/stdout from an external native process // Instead assume a class and use the RewriteMap interface rewriteMapClassName = rewriteMapClassName.substring("prg:".length()); } else if (rewriteMapClassName.startsWith("dbm:")) { // FIXME: https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#dbm // Probably too specific to HTTP Server to implement } else if (rewriteMapClassName.startsWith("dbd:") || rewriteMapClassName.startsWith("fastdbd:")) { // FIXME: https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#dbd } if (map == null) { try { map = (RewriteMap) (Class.forName(rewriteMapClassName).getConstructor().newInstance()); } catch (Exception e) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidMapClassName", line)); } } if (tokenizer.hasMoreTokens()) { if (tokenizer.countTokens() == 1) { map.setParameters(tokenizer.nextToken()); } else { List<String> params = new ArrayList<>(); while (tokenizer.hasMoreTokens()) { params.add(tokenizer.nextToken()); } map.setParameters(params.toArray(new String[0])); } } return new Object[] { name, map }; } else if (token.startsWith("#")) { // it's a comment, ignore it } else { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidLine", line)); } } return null; } /** * Parser for RewriteCond flags. * * @param line The configuration line being parsed * @param condition The current condition * @param flag The flag */ protected static void parseCondFlag(String line, RewriteCond condition, String flag) { if (flag.equals("NC") || flag.equals("nocase")) { condition.setNocase(true); } else if (flag.equals("OR") || flag.equals("ornext")) { condition.setOrnext(true); } else { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag)); } } /** * Parser for RewriteRule flags. * * @param line The configuration line being parsed * @param rule The current rule * @param flag The flag */ protected static void parseRuleFlag(String line, RewriteRule rule, String flag) { if (flag.equals("B")) { rule.setEscapeBackReferences(true); } else if (flag.equals("chain") || flag.equals("C")) { rule.setChain(true); } else if (flag.startsWith("cookie=") || flag.startsWith("CO=")) { rule.setCookie(true); if (flag.startsWith("cookie")) { flag = flag.substring("cookie=".length()); } else if (flag.startsWith("CO=")) { flag = flag.substring("CO=".length()); } StringTokenizer tokenizer = new StringTokenizer(flag, ":"); if (tokenizer.countTokens() < 2) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag)); } rule.setCookieName(tokenizer.nextToken()); rule.setCookieValue(tokenizer.nextToken()); if (tokenizer.hasMoreTokens()) { rule.setCookieDomain(tokenizer.nextToken()); } if (tokenizer.hasMoreTokens()) { try { rule.setCookieLifetime(Integer.parseInt(tokenizer.nextToken())); } catch (NumberFormatException e) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag), e); } } if (tokenizer.hasMoreTokens()) { rule.setCookiePath(tokenizer.nextToken()); } if (tokenizer.hasMoreTokens()) { rule.setCookieSecure(Boolean.parseBoolean(tokenizer.nextToken())); } if (tokenizer.hasMoreTokens()) { rule.setCookieHttpOnly(Boolean.parseBoolean(tokenizer.nextToken())); } } else if (flag.startsWith("env=") || flag.startsWith("E=")) { rule.setEnv(true); if (flag.startsWith("env=")) { flag = flag.substring("env=".length()); } else if (flag.startsWith("E=")) { flag = flag.substring("E=".length()); } int pos = flag.indexOf(':'); if (pos == -1 || (pos + 1) == flag.length()) { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag)); } rule.addEnvName(flag.substring(0, pos)); rule.addEnvValue(flag.substring(pos + 1)); } else if (flag.startsWith("forbidden") || flag.startsWith("F")) { rule.setForbidden(true); } else if (flag.startsWith("gone") || flag.startsWith("G")) { rule.setGone(true); } else if (flag.startsWith("host") || flag.startsWith("H")) { rule.setHost(true); } else if (flag.startsWith("last") || flag.startsWith("L")) { rule.setLast(true); } else if (flag.startsWith("nocase") || flag.startsWith("NC")) { rule.setNocase(true); } else if (flag.startsWith("noescape") || flag.startsWith("NE")) { rule.setNoescape(true); } else if (flag.startsWith("next") || flag.startsWith("N")) { rule.setNext(true); // Note: Proxy is not supported as Tomcat does not have proxy // capabilities } else if (flag.startsWith("qsappend") || flag.startsWith("QSA")) { rule.setQsappend(true); } else if (flag.startsWith("qsdiscard") || flag.startsWith("QSD")) { rule.setQsdiscard(true); } else if (flag.startsWith("redirect") || flag.startsWith("R")) { rule.setRedirect(true); int redirectCode = HttpServletResponse.SC_FOUND; if (flag.startsWith("redirect=") || flag.startsWith("R=")) { if (flag.startsWith("redirect=")) { flag = flag.substring("redirect=".length()); } else if (flag.startsWith("R=")) { flag = flag.substring("R=".length()); } redirectCode = switch (flag) { case "temp" -> HttpServletResponse.SC_FOUND; case "permanent" -> HttpServletResponse.SC_MOVED_PERMANENTLY; case "seeother" -> HttpServletResponse.SC_SEE_OTHER; default -> Integer.parseInt(flag); }; } rule.setRedirectCode(redirectCode); } else if (flag.startsWith("skip") || flag.startsWith("S")) { if (flag.startsWith("skip=")) { flag = flag.substring("skip=".length()); } else if (flag.startsWith("S=")) { flag = flag.substring("S=".length()); } rule.setSkip(Integer.parseInt(flag)); } else if (flag.startsWith("type") || flag.startsWith("T")) { if (flag.startsWith("type=")) { flag = flag.substring("type=".length()); } else if (flag.startsWith("T=")) { flag = flag.substring("T=".length()); } rule.setType(true); rule.setTypeValue(flag); } else if (flag.startsWith("valveSkip") || flag.startsWith("VS")) { rule.setValveSkip(true); } else { throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag)); } } private CharSequence encodeForRewrite(CharSequence input) { StringBuilder result = null; int pos = 0; int mark = 0; while (pos < input.length()) { char c = input.charAt(pos); if (c == '%' || c == ';' || c == '?') { if (result == null) { result = new StringBuilder((int) (input.length() * 1.1)); } result.append(input.subSequence(mark, pos)); result.append('%'); result.append(Character.forDigit((c >> 4) & 0xF, 16)); result.append(Character.forDigit(c & 0xF, 16)); mark = pos + 1; } pos++; } if (result != null) { result.append(input.subSequence(mark, input.length())); return result; } else { return input; } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
3.51
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9
https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS 65 65
https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#prg 698 698
https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#dbm 704 704
https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html#dbd 707 707