VYPR
Moderate severityNVD Advisory· Published Apr 17, 2009· Updated Apr 23, 2026

CVE-2009-0038

CVE-2009-0038

Description

Multiple cross-site scripting (XSS) vulnerabilities in the web administration console in Apache Geronimo Application Server 2.1 through 2.1.3 allow remote attackers to inject arbitrary web script or HTML via the (1) name, (2) ip, (3) username, or (4) description parameter to console/portal/Server/Monitoring; or (5) the PATH_INFO to the default URI under console/portal/.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.geronimo.plugins:consoleMaven
>= 2.1.0, < 2.1.42.1.4

Affected products

4
  • Apache/Geronimo4 versions
    cpe:2.3:a:apache:geronimo:2.1:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:apache:geronimo:2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:geronimo:2.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:geronimo:2.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:geronimo:2.1.3:*:*:*:*:*:*:*

Patches

1
aa0c2c26dde8

GERONIMO-4597 Validate Web Admin Console input - fixes for CVE-2008-5518, CVE-2009-0038, and CVE-2009-0039

https://github.com/apache/geronimoJoseph Alan BohnMar 25, 2009via ghsa
30 files changed · +1447 72
  • framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/repository/AbstractRepository.java+4 11 modified
    @@ -23,6 +23,8 @@
     import java.net.MalformedURLException;
     import java.net.URL;
     import java.net.URLClassLoader;
    +import java.util.ArrayList;
    +import java.util.Arrays;
     import java.util.Enumeration;
     import java.util.HashMap;
     import java.util.LinkedHashSet;
    @@ -38,6 +40,7 @@
     
     import org.apache.commons.logging.Log;
     import org.apache.commons.logging.LogFactory;
    +import org.apache.geronimo.kernel.util.InputUtils;
     import org.apache.geronimo.kernel.util.XmlUtil;
     import org.w3c.dom.Document;
     import org.w3c.dom.Element;
    @@ -158,17 +161,7 @@ public void setTypeHandler(String type, ArtifactTypeHandler handler) {
         public void copyToRepository(File source, Artifact destination, FileWriteMonitor monitor) throws IOException {
     
             // ensure there are no illegal chars in destination elements
    -        Matcher groupMatcher = ILLEGAL_CHARS.matcher(destination.getGroupId());
    -        Matcher artifactMatcher = ILLEGAL_CHARS.matcher(destination.getArtifactId());
    -        Matcher versionMatcher = ILLEGAL_CHARS.matcher(destination.getVersion().toString());
    -        Matcher typeMatcher = ILLEGAL_CHARS.matcher(destination.getType());
    -        if (groupMatcher.find() || 
    -            artifactMatcher.find() ||
    -            versionMatcher.find() ||
    -            typeMatcher.find())
    -        {
    -            throw new IllegalArgumentException("Artifact  "+destination+" contains illegal characters, .. ( ) < > , ; : / \\ \' \" ");
    -        }
    +        InputUtils.validateSafeInput(new ArrayList(Arrays.asList(destination.getGroupId(), destination.getArtifactId(), destination.getVersion().toString(), destination.getType())));
     
             if(!destination.isResolved()) {
                 throw new IllegalArgumentException("Artifact "+destination+" is not fully resolved");
    
  • framework/modules/geronimo-kernel/src/main/java/org/apache/geronimo/kernel/util/InputUtils.java+55 0 added
    @@ -0,0 +1,55 @@
    +/**
    + *  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.geronimo.kernel.util;
    +
    +// import java.io.IOException;
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.regex.Matcher;
    +import java.util.regex.Pattern;
    +
    +import org.apache.commons.logging.Log;
    +import org.apache.commons.logging.LogFactory;
    +
    +/**
    + * Utility functions related to Input validation.
    + *
    + * @version $Rev$ $Date$
    + */
    +public class InputUtils {
    +    private static final Log log = LogFactory.getLog(InputUtils.class);
    +
    +    private static final Pattern ILLEGAL_CHARS = Pattern.compile("[\\.]{2}|[<>:\\\\/\"\'\\|]");
    +
    +    public final static void validateSafeInput(String input) {
    +        // look for illegal chars in input
    +        if (input != null) {
    +            Matcher inputMatcher = ILLEGAL_CHARS.matcher(input);
    +            if (inputMatcher.find()) 
    +            {
    +                log.warn("Illegal characters detected in input" + input);
    +                throw new IllegalArgumentException("input  "+input+" contains illegal characters: .. < > : / \\ \' \" | ");
    +            }
    +        }
    +    }
    +
    +    public final static void validateSafeInput(ArrayList<String> inputs) {
    +        for (String input : inputs) {
    +            validateSafeInput(input);
    +        }
    +    }
    +}
    
  • framework/modules/geronimo-security/src/main/java/org/apache/geronimo/security/keystore/FileKeystoreManager.java+5 0 modified
    @@ -58,6 +58,7 @@
     import org.apache.geronimo.kernel.config.ConfigurationUtil;
     import org.apache.geronimo.kernel.config.EditableConfigurationManager;
     import org.apache.geronimo.kernel.config.InvalidConfigException;
    +import org.apache.geronimo.kernel.util.InputUtils;
     import org.apache.geronimo.management.geronimo.KeyIsLocked;
     import org.apache.geronimo.management.geronimo.KeystoreException;
     import org.apache.geronimo.management.geronimo.KeystoreInstance;
    @@ -367,6 +368,10 @@ public SSLContext createSSLContext(String provider, String protocol, String algo
         }
     
         public KeystoreInstance createKeystore(String name, char[] password, String keystoreType) throws KeystoreException {
    +
    +        // ensure there are no illegal chars in DB name
    +        InputUtils.validateSafeInput(name);
    +
             File test = new File(directory, name);
             if(test.exists()) {
                 throw new IllegalArgumentException("Keystore already exists "+test.getAbsolutePath()+"!");
    
  • plugins/ca-helper/geronimo-ca-helper/pom.xml+6 0 modified
    @@ -38,6 +38,12 @@
         </description>
     
         <dependencies>
    +        <dependency>
    +            <groupId>org.apache.geronimo.plugins</groupId>
    +            <artifactId>console-filter</artifactId>
    +            <version>${version}</version>
    +        </dependency>
    +
             <dependency>
                 <groupId>org.apache.geronimo.framework</groupId>
                 <artifactId>geronimo-kernel</artifactId>
    
  • plugins/ca-helper/geronimo-ca-helper/src/main/webapp/WEB-INF/web.xml+14 0 modified
    @@ -23,6 +23,20 @@
         <description>
             CA Helper
         </description>
    +
    +    <!-- XSS/XSRF filter -->
    +    <filter>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <filter-class>org.apache.geronimo.console.filter.XSSXSRFFilter</filter-class>
    +    </filter>
    +    <filter-mapping>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <url-pattern>/*</url-pattern>
    +    </filter-mapping>
    +    <listener>
    +        <listener-class>org.apache.geronimo.console.filter.XSSXSRFFilter</listener-class>
    +    </listener>
    +
         <servlet>
             <display-name>CertificateRequestServlet</display-name>
             <servlet-name>CertificateRequestServlet</servlet-name>
    
  • plugins/console/console-base-portlets/src/main/webapp/WEB-INF/view/keystore/createKeystore.jsp+4 0 modified
    @@ -29,8 +29,12 @@ var <portlet:namespace/>formName = "<portlet:namespace/>KeystoreForm";
     var <portlet:namespace/>requiredFields = new Array("filename", "password");
     var <portlet:namespace/>passwordFields = new Array("password");
     function <portlet:namespace/>validateForm(){
    +    var illegalChars= /[\.]{2}|[()<>,;:\\/"'\|]/ ;
         if(!textElementsNotEmpty(<portlet:namespace/>formName, <portlet:namespace/>requiredFields)) {
             return false;
    +    } else if (document.forms[<portlet:namespace/>formName].filename.value.match(illegalChars)) {
    +        alert("Keystore name contains illegal characters");
    +        return false;
         }
         if(!passwordElementsConfirm(<portlet:namespace/>formName, <portlet:namespace/>passwordFields)) {
             return false;
    
  • plugins/console/console-base-portlets/src/main/webapp/WEB-INF/view/repository/normal.jsp+9 9 modified
    @@ -26,19 +26,19 @@
     <c:set var="reslist" value="${requestScope['org.apache.geronimo.console.repo.list']}"/>
     
     <style type="text/css">  
    -	div.Hidden {
    -	display: none;
    -	}
    -	
    -	div.Shown {
    -	display: block;
    -	font-size: 10px;
    -	}
    +    div.Hidden {
    +        display: none;
    +    }
    +
    +    div.Shown {
    +        display: block;
    +        font-size: 10px;
    +    }
     </style>  
     
     <script language="JavaScript">
     function <portlet:namespace/>validateForm() {
    -   var illegalChars= /[\.]{2}|[()<>,;:\\/"']/ ;
    +   var illegalChars= /[\.]{2}|[()<>,;:\\/"'\|]/ ;
        if (! (document.<portlet:namespace/>fileSelect.local.value 
           && document.<portlet:namespace/>fileSelect.group.value 
           && document.<portlet:namespace/>fileSelect.artifact.value 
    
  • plugins/console/console-filter/LICENSE.txt+203 0 added
    @@ -0,0 +1,203 @@
    +
    +                                 Apache License
    +                           Version 2.0, January 2004
    +                        http://www.apache.org/licenses/
    +
    +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    +
    +   1. Definitions.
    +
    +      "License" shall mean the terms and conditions for use, reproduction,
    +      and distribution as defined by Sections 1 through 9 of this document.
    +
    +      "Licensor" shall mean the copyright owner or entity authorized by
    +      the copyright owner that is granting the License.
    +
    +      "Legal Entity" shall mean the union of the acting entity and all
    +      other entities that control, are controlled by, or are under common
    +      control with that entity. For the purposes of this definition,
    +      "control" means (i) the power, direct or indirect, to cause the
    +      direction or management of such entity, whether by contract or
    +      otherwise, or (ii) ownership of fifty percent (50%) or more of the
    +      outstanding shares, or (iii) beneficial ownership of such entity.
    +
    +      "You" (or "Your") shall mean an individual or Legal Entity
    +      exercising permissions granted by this License.
    +
    +      "Source" form shall mean the preferred form for making modifications,
    +      including but not limited to software source code, documentation
    +      source, and configuration files.
    +
    +      "Object" form shall mean any form resulting from mechanical
    +      transformation or translation of a Source form, including but
    +      not limited to compiled object code, generated documentation,
    +      and conversions to other media types.
    +
    +      "Work" shall mean the work of authorship, whether in Source or
    +      Object form, made available under the License, as indicated by a
    +      copyright notice that is included in or attached to the work
    +      (an example is provided in the Appendix below).
    +
    +      "Derivative Works" shall mean any work, whether in Source or Object
    +      form, that is based on (or derived from) the Work and for which the
    +      editorial revisions, annotations, elaborations, or other modifications
    +      represent, as a whole, an original work of authorship. For the purposes
    +      of this License, Derivative Works shall not include works that remain
    +      separable from, or merely link (or bind by name) to the interfaces of,
    +      the Work and Derivative Works thereof.
    +
    +      "Contribution" shall mean any work of authorship, including
    +      the original version of the Work and any modifications or additions
    +      to that Work or Derivative Works thereof, that is intentionally
    +      submitted to Licensor for inclusion in the Work by the copyright owner
    +      or by an individual or Legal Entity authorized to submit on behalf of
    +      the copyright owner. For the purposes of this definition, "submitted"
    +      means any form of electronic, verbal, or written communication sent
    +      to the Licensor or its representatives, including but not limited to
    +      communication on electronic mailing lists, source code control systems,
    +      and issue tracking systems that are managed by, or on behalf of, the
    +      Licensor for the purpose of discussing and improving the Work, but
    +      excluding communication that is conspicuously marked or otherwise
    +      designated in writing by the copyright owner as "Not a Contribution."
    +
    +      "Contributor" shall mean Licensor and any individual or Legal Entity
    +      on behalf of whom a Contribution has been received by Licensor and
    +      subsequently incorporated within the Work.
    +
    +   2. Grant of Copyright License. Subject to the terms and conditions of
    +      this License, each Contributor hereby grants to You a perpetual,
    +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +      copyright license to reproduce, prepare Derivative Works of,
    +      publicly display, publicly perform, sublicense, and distribute the
    +      Work and such Derivative Works in Source or Object form.
    +
    +   3. Grant of Patent License. Subject to the terms and conditions of
    +      this License, each Contributor hereby grants to You a perpetual,
    +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    +      (except as stated in this section) patent license to make, have made,
    +      use, offer to sell, sell, import, and otherwise transfer the Work,
    +      where such license applies only to those patent claims licensable
    +      by such Contributor that are necessarily infringed by their
    +      Contribution(s) alone or by combination of their Contribution(s)
    +      with the Work to which such Contribution(s) was submitted. If You
    +      institute patent litigation against any entity (including a
    +      cross-claim or counterclaim in a lawsuit) alleging that the Work
    +      or a Contribution incorporated within the Work constitutes direct
    +      or contributory patent infringement, then any patent licenses
    +      granted to You under this License for that Work shall terminate
    +      as of the date such litigation is filed.
    +
    +   4. Redistribution. You may reproduce and distribute copies of the
    +      Work or Derivative Works thereof in any medium, with or without
    +      modifications, and in Source or Object form, provided that You
    +      meet the following conditions:
    +
    +      (a) You must give any other recipients of the Work or
    +          Derivative Works a copy of this License; and
    +
    +      (b) You must cause any modified files to carry prominent notices
    +          stating that You changed the files; and
    +
    +      (c) You must retain, in the Source form of any Derivative Works
    +          that You distribute, all copyright, patent, trademark, and
    +          attribution notices from the Source form of the Work,
    +          excluding those notices that do not pertain to any part of
    +          the Derivative Works; and
    +
    +      (d) If the Work includes a "NOTICE" text file as part of its
    +          distribution, then any Derivative Works that You distribute must
    +          include a readable copy of the attribution notices contained
    +          within such NOTICE file, excluding those notices that do not
    +          pertain to any part of the Derivative Works, in at least one
    +          of the following places: within a NOTICE text file distributed
    +          as part of the Derivative Works; within the Source form or
    +          documentation, if provided along with the Derivative Works; or,
    +          within a display generated by the Derivative Works, if and
    +          wherever such third-party notices normally appear. The contents
    +          of the NOTICE file are for informational purposes only and
    +          do not modify the License. You may add Your own attribution
    +          notices within Derivative Works that You distribute, alongside
    +          or as an addendum to the NOTICE text from the Work, provided
    +          that such additional attribution notices cannot be construed
    +          as modifying the License.
    +
    +      You may add Your own copyright statement to Your modifications and
    +      may provide additional or different license terms and conditions
    +      for use, reproduction, or distribution of Your modifications, or
    +      for any such Derivative Works as a whole, provided Your use,
    +      reproduction, and distribution of the Work otherwise complies with
    +      the conditions stated in this License.
    +
    +   5. Submission of Contributions. Unless You explicitly state otherwise,
    +      any Contribution intentionally submitted for inclusion in the Work
    +      by You to the Licensor shall be under the terms and conditions of
    +      this License, without any additional terms or conditions.
    +      Notwithstanding the above, nothing herein shall supersede or modify
    +      the terms of any separate license agreement you may have executed
    +      with Licensor regarding such Contributions.
    +
    +   6. Trademarks. This License does not grant permission to use the trade
    +      names, trademarks, service marks, or product names of the Licensor,
    +      except as required for reasonable and customary use in describing the
    +      origin of the Work and reproducing the content of the NOTICE file.
    +
    +   7. Disclaimer of Warranty. Unless required by applicable law or
    +      agreed to in writing, Licensor provides the Work (and each
    +      Contributor provides its Contributions) on an "AS IS" BASIS,
    +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    +      implied, including, without limitation, any warranties or conditions
    +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    +      PARTICULAR PURPOSE. You are solely responsible for determining the
    +      appropriateness of using or redistributing the Work and assume any
    +      risks associated with Your exercise of permissions under this License.
    +
    +   8. Limitation of Liability. In no event and under no legal theory,
    +      whether in tort (including negligence), contract, or otherwise,
    +      unless required by applicable law (such as deliberate and grossly
    +      negligent acts) or agreed to in writing, shall any Contributor be
    +      liable to You for damages, including any direct, indirect, special,
    +      incidental, or consequential damages of any character arising as a
    +      result of this License or out of the use or inability to use the
    +      Work (including but not limited to damages for loss of goodwill,
    +      work stoppage, computer failure or malfunction, or any and all
    +      other commercial damages or losses), even if such Contributor
    +      has been advised of the possibility of such damages.
    +
    +   9. Accepting Warranty or Additional Liability. While redistributing
    +      the Work or Derivative Works thereof, You may choose to offer,
    +      and charge a fee for, acceptance of support, warranty, indemnity,
    +      or other liability obligations and/or rights consistent with this
    +      License. However, in accepting such obligations, You may act only
    +      on Your own behalf and on Your sole responsibility, not on behalf
    +      of any other Contributor, and only if You agree to indemnify,
    +      defend, and hold each Contributor harmless for any liability
    +      incurred by, or claims asserted against, such Contributor by reason
    +      of your accepting any such warranty or additional liability.
    +
    +   END OF TERMS AND CONDITIONS
    +
    +   APPENDIX: How to apply the Apache License to your work.
    +
    +      To apply the Apache License to your work, attach the following
    +      boilerplate notice, with the fields enclosed by brackets "[]"
    +      replaced with your own identifying information. (Don't include
    +      the brackets!)  The text should be enclosed in the appropriate
    +      comment syntax for the file format. We also recommend that a
    +      file or class name and description of purpose be included on the
    +      same "printed page" as the copyright notice for easier
    +      identification within third-party archives.
    +
    +   Copyright [yyyy] [name of copyright owner]
    +
    +   Licensed 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.
    +
    
  • plugins/console/console-filter/NOTICE.txt+11 0 added
    @@ -0,0 +1,11 @@
    +Apache Geronimo 
    +Copyright 2003-2009 The Apache Software Foundation
    +
    +This product includes software developed by
    +The Apache Software Foundation (http://www.apache.org/).
    +
    +Portions of the Web Console were orginally developed by
    +International Business Machines Corporation and are
    +licensed to the Apache Software Foundation under the
    +"Software Grant and Corporate Contribution License Agreement",
    +informally known as the "IBM Console CLA".
    
  • plugins/console/console-filter/pom.xml+42 0 added
    @@ -0,0 +1,42 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!--
    +    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.
    +-->
    +
    +<!-- $Rev$ $Date$ -->
    +
    +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    +
    +    <modelVersion>4.0.0</modelVersion>
    +
    +    <parent>
    +        <groupId>org.apache.geronimo.plugins</groupId>
    +        <artifactId>console</artifactId>
    +        <version>2.1.4-SNAPSHOT</version>
    +    </parent>
    +
    +    <artifactId>console-filter</artifactId>
    +    <name>Geronimo Plugins, Console :: XSSXSRF Filter</name>
    +
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.apache.geronimo.specs</groupId>
    +            <artifactId>geronimo-servlet_2.5_spec</artifactId>
    +            <scope>provided</scope>
    +        </dependency>
    +    </dependencies>
    +
    +</project>
    
  • plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/FilterResponseWrapper.java+182 0 added
    @@ -0,0 +1,182 @@
    +/*
    + * 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.geronimo.console.filter;
    +
    +import java.io.ByteArrayOutputStream;
    +import java.io.IOException;
    +import java.io.OutputStream;
    +import java.io.PrintWriter;
    +
    +import javax.servlet.ServletOutputStream;
    +import javax.servlet.ServletResponse;
    +import javax.servlet.http.HttpServletResponse;
    +import javax.servlet.http.HttpServletResponseWrapper;
    +
    +/**
    + * Implementation of a HttpServletResponseWrapper to allow us to edit the
    + * response content from the filter chain/servlet before committing it to
    + * the ServletResponse.
    + *
    + * @version $Rev$ $Date$
    + */
    +public final class FilterResponseWrapper extends HttpServletResponseWrapper {
    +    private ByteArrayOutputStream output = null;
    +    private ResponseOutputStream stream = null;
    +    private PrintWriter writer = null;
    +
    +    /**
    +     * Default constructor which creates a new HttpServletResponseWrapper in
    +     * place of the default HttpServletResponse, so we can manipulate the
    +     * stream content before committing as a response to the client.
    +     * @param response
    +     */
    +    public FilterResponseWrapper(HttpServletResponse response) {
    +        super(response);
    +        reset();
    +    }
    +
    +    /**
    +     * Gets the current stream content as bytes for easy manipulation.
    +     * @return
    +     * @throws IOException
    +     */
    +    public byte[] getOutput() throws IOException {
    +        flushBuffer();
    +        return output.toByteArray();
    +    }
    +
    +    /**
    +     * Replaces the existing stream content with the updated bytes supplied.
    +     * @param bytes
    +     * @throws IOException
    +     */
    +    public void setOutput(byte[] bytes) throws IOException {
    +        reset();
    +        stream.write(bytes);
    +    }
    +
    +    /**
    +     * Replaces the existing stream content with the updated String supplied.
    +     * @param s
    +     * @throws IOException
    +     */
    +    public void setOutput(String s) throws IOException {
    +        setOutput(s.getBytes());
    +    }
    +
    +    /**
    +     * Write the manipulated stream content out as the ServletResponse
    +     * to the client.
    +     * @throws IOException
    +     */
    +    public void writeOutput() throws IOException {
    +        byte[] content = getOutput();
    +        ServletResponse response = getResponse();
    +        OutputStream os = response.getOutputStream();
    +        response.setContentLength(content.length);
    +        // only write the stream if there is actually something to write
    +        if (content.length > 0) {
    +            os.write(content);
    +        }
    +        os.close();
    +    }
    +
    +    //----- Required method overrides for javax.servlet.ServletResponseWrapper -----
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#flushBuffer()
    +     */
    +    @Override
    +    public void flushBuffer() throws IOException {
    +        writer.flush();
    +        stream.flush();
    +        output.flush();
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#getOutputStream()
    +     */
    +    @Override
    +    public ServletOutputStream getOutputStream() throws IOException {
    +        return stream;
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#getResponse()
    +     */
    +    @Override
    +    public ServletResponse getResponse() {
    +        return super.getResponse();
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#getWriter()
    +     */
    +    @Override
    +    public PrintWriter getWriter() throws IOException {
    +        return writer;
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#isCommitted()
    +     */
    +    @Override
    +    public boolean isCommitted() {
    +        return(output.size() > 0);
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#reset()
    +     */
    +    @Override
    +    public void reset() {
    +        if (this.writer != null) {
    +            this.writer.close();
    +            this.writer = null;
    +        }
    +        if (this.stream != null) {
    +            try {
    +                this.stream.close();
    +            }
    +            catch (IOException e) {
    +                // ignore
    +            }
    +            this.stream = null;
    +        }
    +        if (this.output != null) {
    +            try {
    +                this.output.close();
    +            }
    +            catch (IOException e) {
    +                // ignore
    +            }
    +            this.output = null;
    +        }
    +        this.output = new ByteArrayOutputStream();
    +        this.stream = new ResponseOutputStream(output);
    +        this.writer = new PrintWriter(stream);
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.ServletResponseWrapper#resetBuffer()
    +     */
    +    @Override
    +    public void resetBuffer() {
    +        reset();
    +    }
    +
    +}
    
  • plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/ResponseOutputStream.java+75 0 added
    @@ -0,0 +1,75 @@
    +/*
    + * 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.geronimo.console.filter;
    +
    +import java.io.IOException;
    +import java.io.OutputStream;
    +
    +import javax.servlet.ServletOutputStream;
    +
    +/**
    + * Implementation of a ServletOutputStream, so we can manipulate the stream
    + * before committing it in a ServletResponse.
    + *
    + * @version $Rev$ $Date$
    + */
    +public final class ResponseOutputStream extends ServletOutputStream {
    +
    +    private OutputStream stream = null;
    +
    +    /**
    +     * Default constructor for our wrappered ServletResponse stream.
    +     * @param os
    +     */
    +    public ResponseOutputStream(OutputStream os) {
    +        stream = os;
    +    }
    +
    +    //----- Required method overrides for java.io.OutputStream -----
    +
    +    /* (non-Javadoc)
    +     * @see java.io.OutputStream#close()
    +     */
    +    @Override
    +    public void close() throws IOException {
    +        stream.close();
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see java.io.OutputStream#flush()
    +     */
    +    @Override
    +    public void flush() throws IOException {
    +        stream.flush();
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see java.io.OutputStream#write(byte[])
    +     */
    +    @Override
    +    public void write(byte[] b) throws IOException {
    +        stream.write(b);
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see java.io.OutputStream#write(int)
    +     */
    +    @Override
    +    public void write(int b) throws IOException {
    +        stream.write(b);
    +    }
    +}
    
  • plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java+277 0 added
    @@ -0,0 +1,277 @@
    +/*
    + * 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.geronimo.console.filter;
    +
    +import org.apache.commons.logging.Log;
    +import org.apache.commons.logging.LogFactory;
    +
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.util.Collections;
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.Random;
    +import java.util.regex.Pattern;
    +
    +import javax.servlet.http.HttpServletRequest;
    +import javax.servlet.http.HttpSession;
    +import javax.servlet.http.HttpSessionEvent;
    +
    +/**
    + * Simple XSRF protection via injecting a hidden unique session token into forms
    + * via JavaScript, which can then be used on the form submit by comparing
    + * against the expected uniqueId based on the HttpSession id.
    + * 
    + * See the following for more explanation of XSRF and how adding a unique token
    + * in each request can block attackers (no code was used from these sources):
    + *    http://www.cgisecurity.com/csrf-faq.html
    + *    http://shiflett.org/articles/cross-site-request-forgeries
    + * 
    + * @version $Rev$ $Date$
    + */
    +public class XSRFHandler
    +{
    +    private static final Log log = LogFactory.getLog(XSRFHandler.class);
    +    private static final String XSRF_UNIQUEID = "formId";
    +    private static final String XSRF_JS_FILENAME = "/XSRF.js";
    +    private final static String XSRF_JS_UNIQUEID = "<%XSRF_UNIQUEID%>";
    +    private final static String SEARCH_PATTERN = "(?i)</body>";
    +    private static final Pattern regexPattern = Pattern.compile(SEARCH_PATTERN);
    +
    +    private Map<String, String> sessionMap = Collections.synchronizedMap(new HashMap<String, String>());
    +    private String xsrfJS;
    +
    +    private Random random = new Random();
    +
    +    /**
    +     * Default constructor
    +     */
    +    public XSRFHandler() {
    +        xsrfJS = getFile(XSRF_JS_FILENAME);
    +        log.debug("loaded xsrf file");
    +    }
    +
    +    //----- Session handler routines -----
    +
    +    /**
    +     * Determines if the HttpServletRequest should be blocked due to 
    +     * a potential XSRF attack.  Only requests with a QueryString or
    +     * POST parameters are checked to verify they contain a unique
    +     * session token that we added via JavaScript on the original response.
    +     * @param hreq
    +     * @return String if the session was invalid or null if OK
    +     */
    +    public boolean isInvalidSession(HttpServletRequest hreq) {
    +        HttpSession hses = hreq.getSession(true);
    +        String uniqueId = getSession(hses);
    +
    +        if (hses.isNew() || (uniqueId == null)) {
    +            // New client session, so create and add our uniqueId
    +            uniqueId = createSession(hses.getId());
    +            hses.setAttribute(XSRF_UNIQUEID, uniqueId);
    +            log.info("Created session for uid=" + hreq.getRemoteUser() + " with sessionId=" + hses.getId() + ", uniqueId=" + uniqueId);
    +            return false;
    +        }
    +
    +        String sesId = (String)hses.getAttribute(XSRF_UNIQUEID);
    +        String reqId = (String)hreq.getParameter(XSRF_UNIQUEID);
    +        if ((hreq.getQueryString() != null) || (hreq.getParameterNames().hasMoreElements())) {
    +            log.debug("XSRF checking requestURI=" + hreq.getRequestURI());
    +            // only check if this is a form GET/POST
    +            if (sesId == null) {
    +                // Request did not contain the expected session param
    +                log.warn("Blocked due to missing HttpSession data.");
    +                return true;
    +            }
    +            else if (reqId == null) {
    +                // Request did not contain the expected session param
    +                log.warn("Blocked due to missing HttpServletRequest parameter.");
    +                return true;                
    +            }
    +            else if (!reqId.equals(uniqueId)) {
    +                // The unique Ids didn't match
    +                log.warn("Blocked due to invalid HttpServletRequest parameter.");
    +                // TODO - Should we invalidate the session?
    +                return true;
    +            }
    +            else {
    +                // Unique Ids matched, so let the request thru
    +                log.debug("Validated sessionId=" + hses.getId() + ", uniqueId=" + uniqueId + ", requestURI=" + hreq.getRequestURI());
    +            }
    +        }
    +        else {
    +            log.debug("Skipped check due to no QueryString or ParameterNames for requestURI=" + hreq.getRequestURI());
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * When HttpSessions are invalidated, remove them form our map
    +     * @param hse
    +     */
    +    public void destroySession(HttpSessionEvent hse) {
    +        String sesId = hse.getSession().getId();
    +        log.info("Removed destroyed sessionId=" + sesId);
    +        removeSession(sesId);
    +    }
    +
    +    /**
    +     * Allow cleanup of our session map on filter exit
    +     */
    +    public void clearSessions() {
    +        // clear out our session map
    +        log.debug("Cleaning out sessionMap");
    +        sessionMap.clear();
    +    }
    +
    +    /**
    +     * Create and return a uniqueId for the given HttpSession id
    +     * @param sesId
    +     * @return String holding the unique token, else null if there was no HttpSession
    +     */
    +    private String createSession(String sesId) {
    +        String uniqueId = null;
    +        if (sesId != null) {
    +            uniqueId = String.valueOf(random.nextLong());
    +            sessionMap.put(sesId, uniqueId);        
    +        }
    +        return uniqueId;
    +    }
    +
    +    /**
    +     * Get the uniqueId for the given HttpServletRequest.getSession()
    +     * @param hreq
    +     * @return String holding the unique token for this session, else null
    +     */
    +    private String getSession(HttpServletRequest hreq) {
    +        HttpSession hses = hreq.getSession(false);
    +        if (hses != null) {
    +            return sessionMap.get(hses.getId());
    +        }
    +        else {
    +            return null;
    +        }
    +    }
    +
    +    /**
    +     * Get the uniqueId for the given HttpSession id
    +     * @param hses
    +     * @return String holding the unique token for this session, else null
    +     */
    +    private String getSession(HttpSession hses) {
    +        if (hses != null) {
    +            return sessionMap.get(hses.getId());
    +        }
    +        else {
    +            return null;
    +        }
    +    }
    +
    +    /**
    +     * Remove the given HttpSession id from our session map
    +     * @param sesId
    +     */
    +    private void removeSession(String sesId) {
    +        if (sesId != null) {
    +            sessionMap.remove(sesId);        
    +        }
    +    }
    +
    +    //----- Response handler routines -----
    +
    +    /**
    +     * Main response handler, which appends our XSRF JavaScript with the
    +     * unique session token to any HTML response content that includes a
    +     * form tag.
    +     * @param hreq
    +     * @param hres
    +     */
    +    public void updateResponse(HttpServletRequest hreq, FilterResponseWrapper hres) throws IOException {
    +        // get the JavaScript file we're going to append to it
    +        String updatedXsrfJS;
    +        String uniqueId = getSession(hreq);
    +        if (xsrfJS == null) {
    +            log.error("No JavaScript to append to the response!");
    +        }
    +        else if (uniqueId == null) {
    +            // this should only happen for user logout or session timeout, so ignore
    +            log.debug("HttpSession is null!");
    +        }
    +        else {
    +            String cType = hres.getContentType();
    +            if (cType != null) {
    +                // only update the content if it is HTML
    +                if (cType.toLowerCase().indexOf("html") != -1) {
    +                    // get the response content
    +                    String content = new String(hres.getOutput());
    +                    // update the JavaScript with the uniqueId for this session
    +                    updatedXsrfJS = xsrfJS.replace(XSRF_JS_UNIQUEID, uniqueId);
    +                    // update the response to contain the JS fragment
    +                    content = regexPattern.matcher(content).replaceAll(updatedXsrfJS);
    +                    log.info("Updated HTML content with XSRF JavaScript for requestURI=" + hreq.getRequestURI());
    +                    //log.debug("Updated content =" + content);
    +                    // update the ResponseOutputStream content
    +                    hres.setOutput(content);                                    
    +                }
    +                else {
    +                    // we don't want to try updating non-HTML content with our JavaScript
    +                    log.debug("Not updating requestURI=" + hreq.getRequestURI() + " due to ContentType = " + cType);
    +                }
    +            }
    +            else {
    +                // no ContentType provided, so ignore this content
    +                log.debug("Not updating requestURI=" +  hreq.getRequestURI() + " due to NO ContentType");
    +            }
    +        }
    +        // write out our updated HttpServletResponse
    +        hres.writeOutput();
    +    }
    +
    +    /**
    +     * Helper function to retrieve our JavaScript from the classpath.
    +     * @param filename
    +     * @return String containing the JavaScript content, else null
    +     */
    +    private String getFile(String filename) {
    +        StringBuffer sb = new StringBuffer();
    +        InputStream is = getClass().getResourceAsStream(filename);
    +        if (is != null) {
    +            try {
    +                int i = 0;
    +                while ((i = is.read()) > 0) {
    +                    sb.append((char) i);
    +                }
    +            }
    +            catch (IOException ioe) {
    +                log.error("Could not read resource=" + filename, ioe);
    +            }
    +            finally {
    +                try {
    +                    is.close();
    +                }
    +                catch (IOException ioe) {
    +                }
    +            }
    +        }
    +        else {
    +            log.error("Could not load required resource=" + filename);
    +            return null;
    +        }
    +        return sb.toString();
    +    }
    +
    +}
    
  • plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSHandler.java+250 0 added
    @@ -0,0 +1,250 @@
    +/*
    + * 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.geronimo.console.filter;
    +
    +import org.apache.commons.logging.Log;
    +import org.apache.commons.logging.LogFactory;
    +
    +import java.io.UnsupportedEncodingException;
    +import java.net.URLDecoder;
    +import java.util.Enumeration;
    +
    +import javax.servlet.http.HttpServletRequest;
    +
    +
    +/**
    + * Heavily modified code from Apache JetSpeed v2.1.3 -
    + *     jetspeed-2.1.3/src/components/portal/src/java/org/apache/jetspeed/engine/servlet/XXSUrlAttackFilter.java
    + *
    + * Simple XSS Url attack protection blocking access whenever the request url
    + * contains a &lt; or &quot; character.
    + * Modified to include basic XSS POST parameter protection and logging.
    + * 
    + * @version $Rev$ $Date$
    + */
    +public class XSSHandler {
    +    private static final Log log = LogFactory.getLog(XSSHandler.class);
    +
    +    /**
    +     * Default constructor
    +     */
    +    public XSSHandler() {
    +    }
    +
    +    /**
    +     * Block simple XSS attacks in GET request URIs
    +     * @param hreq
    +     * @return true if we find %lt; or &quot; in the URI or Query string, otherwise false
    +     */
    +    public boolean isInvalidURI(HttpServletRequest hreq) {
    +        if (isInvalidString(hreq.getRequestURI()) ||
    +            isInvalidString(hreq.getQueryString())) {
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Block simple XSS attacks in POST parameters
    +     * Note: Portlet/webapp should perform more complex field validation
    +     * @param hreq
    +     * @return true if any session params were invalid, otherwise false
    +     */
    +    public boolean isInvalidParameters(HttpServletRequest hreq) {
    +
    +        for (Enumeration<String> e = hreq.getParameterNames(); e.hasMoreElements();) {
    +            String name = e.nextElement();
    +            String name2 = name.trim().toLowerCase();
    +            if (name2.startsWith("noxss")) {
    +                log.debug("Skipping specially marked paramter=" + name);
    +            }
    +            else if ((name2.startsWith("minxss")) || (name2.indexOf("password") != -1) || (name2.indexOf("xml") != -1) || (name2.indexOf("sql") != -1)) {
    +                // perform a "minimal" but more CPU intensive set of checks on
    +                // these parameter value(s) which can allow &lt; and &quot; usage
    +                String[] vals = hreq.getParameterValues(name);
    +                for (String value : vals) {
    +                    if (isInvalidParam(value)) {
    +                        // should be safe to log the uri, as we've already run isInvalidURI() on it
    +                        log.warn("Blocking request due to known XSS content in parameter=" + name + " for uri=" + hreq.getRequestURI());
    +                        return true;
    +                    }
    +                }
    +            }
    +            else {
    +                String[] vals = hreq.getParameterValues(name);
    +                for (String value : vals) {
    +                    if (isInvalidString(value)) {
    +                        // should be safe to log the uri, as we've already run isInvalidURI() on it
    +                        log.warn("Blocking request due to potential XSS content in parameter=" + name + " for uri=" + hreq.getRequestURI());
    +                        return true;
    +                    }
    +                }
    +            }
    +
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Searches the given string for any &lt; or &quot; instances
    +     * @param value
    +     * @return true if we find &lt; or &quot; anywhere in the string, otherwise false
    +     */
    +    private boolean isInvalidString(String value) {
    +        if (value != null) {
    +            try {
    +                String s = URLDecoder.decode(value, "UTF-8").toLowerCase();
    +                if ((s.indexOf('<') != -1) || (s.indexOf('"') != -1)) {
    +                    return true;
    +                }
    +            }
    +            catch (UnsupportedEncodingException uee) {
    +                // should never happen
    +                log.error("URLDecoder.decode(UTF8) failed.", uee);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * More limited version of the isInvalidString() method, in which we only
    +     * check for: <script, <img, <iframe, <div and style= tags in the string.
    +     * @param value
    +     * @return true if we find:
    +     *      1) <script, <img, <iframe or <div or
    +     *      2) style= anywhere in the string
    +     *      else false
    +     */
    +    private boolean isInvalidParam(String value) {
    +        if (value != null) {
    +            try {
    +                String s = URLDecoder.decode(value, "UTF-8").toLowerCase();
    +                int offset = s.indexOf('<');
    +                while (offset != -1) {
    +                    // increment past the "<"
    +                    offset++;
    +                    // if we found a start tag in the param, lets dig deeper...
    +                    if (containsScript(s, offset) || containsImg(s, offset) ||
    +                        containsIframe(s, offset) || containsDiv(s, offset)) {
    +                        // we found a hit
    +                        return true;
    +                    }
    +                    else {
    +                        // look for another set of tags in the string
    +                        offset = s.indexOf('<', offset);
    +                    }
    +                }
    +                // also need to check for style= usage
    +                return(containsStyle(s));
    +            }
    +            catch (UnsupportedEncodingException uee) {
    +                // should never happen
    +                log.error("URLDecoder.decode(UTF8) failed.", uee);
    +            }
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Check for script tag at start of a URLDecoder.decode().toLowerCase() String
    +     * @param value
    +     * @param index
    +     * @return true if string starts with "script", else false
    +     */
    +    private boolean containsScript(String value, int index) {
    +        int offset = index;
    +        if ((value.charAt(offset) == 's') &&
    +            (value.charAt(++offset) == 'c') &&
    +            (value.charAt(++offset) == 'r') &&
    +            (value.charAt(++offset) == 'i') &&
    +            (value.charAt(++offset) == 'p') &&
    +            (value.charAt(++offset) == 't')) {
    +            log.debug("Found a '<script' tag in a HttpServletRequest parameter.");
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Check for img tag at start of a URLDecoder.decode().toLowerCase() String
    +     * @param value
    +     * @param index
    +     * @return true if string starts with "img", else false
    +     */
    +    private boolean containsImg(String value, int index) {
    +        int offset = index;
    +        if ((value.charAt(offset) == 'i') &&
    +            (value.charAt(++offset) == 'm') &&
    +            (value.charAt(++offset) == 'g')) {
    +            log.debug("Found a '<img' tag in a HttpServletRequest parameter.");
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Check for iframe tag at start of a URLDecoder.decode().toLowerCase() String
    +     * @param value
    +     * @param index
    +     * @return true if string starts with "iframe", else false
    +     */
    +    private boolean containsIframe(String value, int index) {
    +        int offset = index;
    +        if ((value.charAt(offset) == 'i') &&
    +            (value.charAt(++offset) == 'f') &&
    +            (value.charAt(++offset) == 'r') &&
    +            (value.charAt(++offset) == 'a') &&
    +            (value.charAt(++offset) == 'm') &&
    +            (value.charAt(++offset) == 'e')) {
    +            log.debug("Found a '<iframe' tag in a HttpServletRequest parameter.");
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Check for div tag at start of a URLDecoder.decode().toLowerCase() String
    +     * @param value
    +     * @param index
    +     * @return true if string starts with "div", else false
    +     */
    +    private boolean containsDiv(String value, int index) {
    +        int offset = index;
    +        if ((value.charAt(offset) == 'd') &&
    +            (value.charAt(++offset) == 'i') &&
    +            (value.charAt(++offset) == 'v')) {
    +            log.debug("Found a '<div' tag in a HttpServletRequest parameter.");
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Check for style= tag anywhere in a URLDecoder.decode().toLowerCase() String
    +     * @param value
    +     * @return true if string contains "style=", else false
    +     */
    +    private boolean containsStyle(String value) {
    +        String style = "style=";
    +        if (value.indexOf(style) != -1) {
    +            log.debug("Found a 'style=' tag in a HttpServletRequest parameter.");
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +}
    
  • plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java+153 0 added
    @@ -0,0 +1,153 @@
    +/*
    + * 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.geronimo.console.filter;
    +
    +import org.apache.commons.logging.Log;
    +import org.apache.commons.logging.LogFactory;
    +
    +import java.io.IOException;
    +
    +import javax.servlet.Filter;
    +import javax.servlet.FilterChain;
    +import javax.servlet.FilterConfig;
    +import javax.servlet.ServletException;
    +import javax.servlet.ServletRequest;
    +import javax.servlet.ServletResponse;
    +import javax.servlet.http.HttpServletRequest;
    +import javax.servlet.http.HttpServletResponse;
    +import javax.servlet.http.HttpSessionEvent;
    +import javax.servlet.http.HttpSessionListener;
    +
    +/**
    + * WebApp protection against XSS and XSRF attacks.
    + * 
    + * Simple XSS Url attack protection blocking access whenever the request url
    + *  contains a &lt; or &quot; character in XSSHandler, was adapted from
    + *  Apache JetSpeed v2.1.3 -
    + *      jetspeed-2.1.3/src/components/portal/src/java/org/apache/jetspeed/engine/servlet/XXSUrlAttackFilter.java
    + *  Modified to include basic XSS POST parameter protection and logging.
    + *  
    + * Simple XSRF protection via unique session token was added by XSRFHandler.
    + * 
    + * @version $Rev$ $Date$
    + */
    +public class XSSXSRFFilter implements Filter, HttpSessionListener
    +{
    +    private static final Log log = LogFactory.getLog(XSSXSRFFilter.class);
    +    private XSSHandler xss = new XSSHandler();
    +    private XSRFHandler xsrf = new XSRFHandler();
    +    private boolean enableXSS = true;
    +    private boolean enableXSRF = true;
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
    +     */
    +    public void init(FilterConfig config) throws ServletException {
    +        log.debug("init() called");
    +        String parmEnableXSS = config.getInitParameter("enableXSS");
    +        String parmEnableXSRF = config.getInitParameter("enableXSRF");
    +        if ((parmEnableXSS != null) && (parmEnableXSS.equals("false"))) {
    +            this.enableXSS = false;
    +        }
    +        if ((parmEnableXSRF != null) && (parmEnableXSRF.equals("false"))) {
    +            this.enableXSRF = false;
    +        }
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)
    +     */
    +    public void sessionCreated(HttpSessionEvent hse) {
    +        log.debug("sessionCreated() called for sesId=" + hse.getSession().getId());
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
    +     */
    +    public void sessionDestroyed(HttpSessionEvent hse) {
    +        // when sessions are invalidated, remove them form our map
    +        log.debug("sessionDestroyed() called for sesId=" + hse.getSession().getId());
    +        xsrf.destroySession(hse);
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
    +     */
    +    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    +        if ((request instanceof HttpServletRequest) &&
    +            (response instanceof HttpServletResponse)) {
    +            HttpServletRequest hreq = (HttpServletRequest)request;
    +            hreq.setCharacterEncoding("UTF-8");
    +            String errStr = null;
    +            //--------------------------------------------------------------
    +            // Check the URI and QueryString for simple XSS attacks
    +            // Validate any FORM submission with our XSRF protection code
    +            //--------------------------------------------------------------
    +            // check the URI/Params first, as they get logged during the XSRF checks
    +            if (enableXSS && xss.isInvalidURI(hreq)) {
    +                // Block simple XSS attacks in GET request URIs
    +                errStr = new String("XSSXSRFFilter blocked HttpServletRequest due to invalid URI content.");
    +            }
    +            else if (enableXSS && xss.isInvalidParameters(hreq)) {
    +                // Block simple XSS attacks in POST parameters
    +                errStr = new String("XSSXSRFFilter blocked HttpServletRequest due to invalid POST content.");
    +            }
    +            else if (enableXSRF && xsrf.isInvalidSession(hreq)) {
    +                // Block simple XSRF attacks on our forms
    +                errStr = new String("XSSXSRFFilter blocked HttpServletRequest due to invalid FORM content.");   
    +            }
    +            // if we found a problem, return a HTTP 400 error code and message
    +            if (errStr != null) {
    +                log.error(errStr);
    +                // create an error response with our message
    +                ((HttpServletResponse)response).sendError(HttpServletResponse.SC_BAD_REQUEST, errStr);
    +                // Shouldn't forward to next filter after response committed
    +                return;
    +            }
    +            //-----------------------------------------------
    +            // Call other filters and eventually the Servlet
    +            //-----------------------------------------------
    +            FilterResponseWrapper whres = new FilterResponseWrapper((HttpServletResponse)response);
    +            chain.doFilter(hreq, whres);
    +
    +            //-------------------------------------------------------------------
    +            // Update and commit the response with our XSRF FORM protection code
    +            //-------------------------------------------------------------------
    +            xsrf.updateResponse(hreq, whres);
    +        }
    +        else {
    +            log.debug("Request not HttpServletRequest and/or Response not HttpServletResponse");
    +            log.debug("Request: " + request);
    +            log.debug("Response: " + response);
    +
    +            //-----------------------------------------------
    +            // Call other filters and eventually the Servlet
    +            //-----------------------------------------------
    +            chain.doFilter(request, response);
    +        }
    +
    +    }
    +
    +    /* (non-Javadoc)
    +     * @see javax.servlet.Filter#destroy()
    +     */
    +    public void destroy() {
    +        log.debug("destroy() called");
    +        // clear out our session map
    +        xsrf.clearSessions();
    +    }
    +}
    
  • plugins/console/console-filter/src/main/resources/XSRF.js+71 0 added
    @@ -0,0 +1,71 @@
    +<script language="JavaScript">
    +var formID = '<%XSRF_UNIQUEID%>';
    +function updateLinks() {
    +    var elements = document.all ? document.all : document.getElementsByTagName('*');
    +    var len = elements.length;
    +    for (var i=0; i<len; i++) {
    +        var element = elements[i];      
    +        updateLink(element, 'src');
    +        updateLink(element, 'href');
    +//        updateOnclickLink(element);
    +    }
    +}
    +function updateForms() {
    +   var forms = document.getElementsByTagName('form');
    +   for (i=0; i<forms.length; i++) {
    +       var input = document.createElement('input');
    +       if (document.all) {
    +          input.type = 'hidden';
    +          input.name = 'formId';
    +          input.value = formID;
    +       } else if (document.getElementById) {
    +          input.setAttribute('type', 'hidden');
    +          input.setAttribute('name', 'formId');
    +          input.setAttribute('value', formID);
    +       }
    +       forms[i].appendChild(input);
    +   }
    +}
    +function updateLink(element, attr) {
    +    var link = element.getAttribute(attr);
    +    if ((link != null) && (link != '') && isURL(link)) {
    +        var i = link.indexOf('?');
    +        // add formId only if other attributes are present in link
    +        if (i != -1) {
    +            link = link + '&formId=' + formID;
    +            // Note: we cannot use setAttribute due to IE issues so we are using element.*=
    +            if (attr.substring(0,3) == 'src') {
    +                element.src=link;
    +            }
    +            else {
    +                element.href=link;
    +            }
    +        }
    +    }
    +}
    +function updateOnclickLink(element) {
    +    var link = element.getAttribute('onclick');
    +    if ((link != null) && (link != '')) {
    +        var start = link.indexOf('/');
    +        if (start != -1) {
    +            var end = link.indexOf('?',start);
    +            if (end != -1) {
    +                var newlink = link.substring(0,end+1) + 'formId=' + formID + '&' + link.substring(end+1);
    +                var new_onclick = function() { eval(newlink); };
    +                element.onclick=new_onclick;
    +            }
    +        }
    +    }
    +    return false;
    +}
    +function isURL(link) {
    +    var rc = 0;
    +    if (link.substring(0, 4) == 'http' || link.substring(0, 1) == '/') {
    +        rc = 1;
    +    }
    +    return rc;
    +}
    +updateLinks();
    +updateForms();
    +</script>
    +</body>
    \ No newline at end of file
    
  • plugins/console/console-portal-driver/pom.xml+6 0 modified
    @@ -33,6 +33,12 @@
         <packaging>war</packaging>
     
         <dependencies>
    +        <dependency>
    +            <groupId>org.apache.geronimo.plugins</groupId>
    +            <artifactId>console-filter</artifactId>
    +            <version>${version}</version>
    +        </dependency>
    +
             <!-- for jspc maven plugin -->
             <dependency>
                 <groupId>org.apache.geronimo.framework</groupId>
    
  • plugins/console/console-portal-driver/src/main/webapp/WEB-INF/web.xml+13 0 modified
    @@ -31,6 +31,19 @@ limitations under the License.
         <param-value>/WEB-INF/pluto-portal-driver-services-config.xml</param-value>
       </context-param>
     
    +  <!-- XSS/XSRF filter -->
    +  <filter>
    +    <filter-name>XSSXSRFFilter</filter-name>
    +    <filter-class>org.apache.geronimo.console.filter.XSSXSRFFilter</filter-class>
    +  </filter>
    +  <filter-mapping>
    +    <filter-name>XSSXSRFFilter</filter-name>
    +    <url-pattern>/*</url-pattern>
    +  </filter-mapping>
    +  <listener>
    +    <listener-class>org.apache.geronimo.console.filter.XSSXSRFFilter</listener-class>
    +  </listener>
    +
       <listener>
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
       </listener>
    
  • plugins/console/pom.xml+1 0 modified
    @@ -48,6 +48,7 @@
             <module>geronimo-converter</module>
             <module>console-core</module>
             <module>console-base-portlets</module>
    +        <module>console-filter</module>
             <module>console-portal-driver</module>
             <module>console-ear</module>
             <module>console-tomcat</module>
    
  • plugins/monitoring/mconsole-war/pom.xml+7 0 modified
    @@ -37,6 +37,13 @@
         <description>Geronimo Monitorin Console :: WEB Module</description>
     
         <dependencies>
    +
    +        <dependency>
    +            <groupId>org.apache.geronimo.plugins</groupId>
    +            <artifactId>console-filter</artifactId>
    +            <version>${version}</version>
    +        </dependency>
    +
             <dependency>
                 <groupId>org.apache.geronimo.framework</groupId>
                 <artifactId>geronimo-management</artifactId>
    
  • plugins/monitoring/mconsole-war/src/main/java/org/apache/geronimo/monitoring/console/MonitoringPortlet.java+4 4 modified
    @@ -510,7 +510,7 @@ private void updateView(ActionRequest actionRequest,
             DBManager DBase = new DBManager();
             Connection con = DBase.getConnection();
             String name = actionRequest.getParameter("name");
    -        String description = actionRequest.getParameter("description");
    +        String description = actionRequest.getParameter("minxss_description");
             String[] graphsArray = actionRequest.getParameterValues("graph_ids");
             if (graphsArray == null) {
                 graphsArray = new String[0];
    @@ -553,7 +553,7 @@ private void addView(ActionRequest actionRequest,
             DBManager DBase = new DBManager();
             Connection con = DBase.getConnection();
             String name = actionRequest.getParameter("name");
    -        String description = actionRequest.getParameter("description");
    +        String description = actionRequest.getParameter("minxss_description");
             String[] graphsArray = actionRequest.getParameterValues("graph_ids");
             if (graphsArray == null) {
                 graphsArray = new String[0];
    @@ -797,7 +797,7 @@ private void addGraph(ActionRequest actionRequest,
             DBManager DBase = new DBManager();
             Connection con = DBase.getConnection();
             String name = actionRequest.getParameter("name");
    -        String description = actionRequest.getParameter("description");
    +        String description = actionRequest.getParameter("minxss_description");
             String server_id = actionRequest.getParameter("server_id");
             String xlabel = actionRequest.getParameter("xlabel");
             String ylabel = actionRequest.getParameter("ylabel");
    @@ -870,7 +870,7 @@ private void updateGraph(ActionRequest actionRequest,
             actionResponse.setRenderParameter("graph_id", graph_id);
     
             String name = actionRequest.getParameter("name");
    -        String description = actionRequest.getParameter("description");
    +        String description = actionRequest.getParameter("minxss_description");
             String server_id = actionRequest.getParameter("server_id");
             String xlabel = actionRequest.getParameter("xlabel");
             String ylabel = actionRequest.getParameter("ylabel");
    
  • plugins/monitoring/mconsole-war/src/main/webapp/WEB-INF/view/monitoringAddGraph.jsp+1 1 modified
    @@ -459,7 +459,7 @@ if (!message.equals("")) {
     				<td><fmt:message key="monitor.common.desc"/>:</td>
     				<td>&nbsp;</td>
     				<td align="right"><textarea rows="5" cols="50"
    -					name="description"></textarea></td>
    +					name="minxss_description"></textarea></td>
     				<td></td>
     			</tr>
     			<tr>
    
  • plugins/monitoring/mconsole-war/src/main/webapp/WEB-INF/view/monitoringAddView.jsp+2 2 modified
    @@ -56,7 +56,7 @@ document.getElementById(x).style.display='';
     }
     function validate() {
        if (! (document.addView.name.value 
    -      && document.addView.description.value  ))
    +      && document.addView.minxss_description.value  ))
        {
           alert("Name and Description are required fields");
           return false;
    @@ -100,7 +100,7 @@ function openNewWindow(theURL,winName,features) {
         <tr>
           <td><fmt:message key="monitor.common.desc"/>:</td>
           <td>&nbsp;</td>
    -      <td align="right"><textarea rows="5" cols="50" name="description"></textarea></td>
    +      <td align="right"><textarea rows="5" cols="50" name="minxss_description"></textarea></td>
         </tr>
         <tr>
           <td><fmt:message key="monitor.common.graph"/>:</td>
    
  • plugins/monitoring/mconsole-war/src/main/webapp/WEB-INF/view/monitoringEditGraph.jsp+1 1 modified
    @@ -508,7 +508,7 @@ function addOption(selectbox, value, text )
         <tr>
           <td><fmt:message key="monitor.common.desc"/>:</td>
           <td>&nbsp;</td>
    -      <td align="right"><textarea rows="5" cols="50" name="description"><%=description%></textarea></td>
    +      <td align="right"><textarea rows="5" cols="50" name="minxss_description"><%=description%></textarea></td>
           <td></td>
         </tr>
         <tr>
    
  • plugins/monitoring/mconsole-war/src/main/webapp/WEB-INF/view/monitoringEditView.jsp+2 2 modified
    @@ -73,7 +73,7 @@ document.getElementById(x).style.display='';
     }
     function validate() {
        if (! (document.editView.name.value 
    -      && document.editView.description.value  ))
    +      && document.editView.minxss_description.value  ))
        {
           alert("Name and Description are required fields");
           return false;
    @@ -128,7 +128,7 @@ function openNewWindow(theURL,winName,features) {
         <tr>
           <td><fmt:message key="monitor.common.desc"/>:</td>
           <td>&nbsp;</td>
    -      <td align="right"><textarea rows="5" cols="50" name="description"><%=description%></textarea></td>
    +      <td align="right"><textarea rows="5" cols="50" name="minxss_description"><%=description%></textarea></td>
         </tr>
         <tr>
           <td valign="top"><fmt:message key="monitor.common.graph"/>:</td>
    
  • plugins/monitoring/mconsole-war/src/main/webapp/WEB-INF/web.xml+17 0 modified
    @@ -19,6 +19,23 @@
     <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     
    +    <!-- XSS/XSRF filter -->
    +    <filter>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <filter-class>org.apache.geronimo.console.filter.XSSXSRFFilter</filter-class>
    +            <init-param>
    +                <param-name>enableXSRF</param-name>
    +                <param-value>false</param-value>
    +            </init-param>
    +    </filter>
    +    <filter-mapping>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <url-pattern>/*</url-pattern>
    +    </filter-mapping>
    +    <listener>
    +        <listener-class>org.apache.geronimo.console.filter.XSSXSRFFilter</listener-class>
    +    </listener>
    +
         <servlet>
             <servlet-name>monitoring</servlet-name>
             <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
    
  • plugins/system-database/sysdb-portlets/src/main/java/org/apache/geronimo/console/internaldb/RunSQLHelper.java+6 0 modified
    @@ -25,6 +25,8 @@
     import java.sql.SQLException;
     import java.sql.Statement;
     
    +import org.apache.geronimo.kernel.util.InputUtils;
    +
     public class RunSQLHelper {
     
         private final static Log log = LogFactory.getLog(RunSQLHelper.class);
    @@ -46,6 +48,10 @@ public class RunSQLHelper {
         private static final String BAK_PREFIX = "BAK_";
     
         public String createDB(String dbName) {
    +
    +        // ensure there are no illegal chars in DB name
    +        InputUtils.validateSafeInput(dbName);
    +
             String result = DB_CREATED_MSG + ": " + dbName;
     
             Connection conn = null;
    
  • plugins/system-database/sysdb-portlets/src/main/webapp/WEB-INF/view/internaldb/runSQLNormal.jsp+8 1 modified
    @@ -28,9 +28,16 @@ var <portlet:namespace/>requiredFields = new Array("createDB");
     var <portlet:namespace/>requiredFields2 = new Array("sqlStmts");
     
     function <portlet:namespace/>validateForm1(){
    +    var illegalChars= /[\.]{2}|[()<>,;:\\/"'\|]/ ;
         var action = document.forms[<portlet:namespace/>formName].elements['action'];
         action.value="Create";
    -    return textElementsNotEmpty(<portlet:namespace/>formName, <portlet:namespace/>requiredFields);
    +    if (!textElementsNotEmpty(<portlet:namespace/>formName, <portlet:namespace/>requiredFields)) 
    +    {
    +        return false;
    +    } else if (document.forms[<portlet:namespace/>formName].createDB.value.match(illegalChars)) {
    +        alert("Database name contains illegal characters");
    +        return false;
    +    }
     }
     function <portlet:namespace/>validateForm2(){
         var action = document.forms[<portlet:namespace/>formName].elements['action'];
    
  • plugins/welcome/geronimo-welcome/pom.xml+6 0 modified
    @@ -39,6 +39,12 @@
         </description>
     
         <dependencies>
    +        <dependency>
    +            <groupId>org.apache.geronimo.plugins</groupId>
    +            <artifactId>console-filter</artifactId>
    +            <version>${version}</version>
    +        </dependency>
    +
             <dependency>
                 <groupId>org.apache.geronimo.framework</groupId>
                 <artifactId>geronimo-plugin</artifactId>
    
  • plugins/welcome/geronimo-welcome/src/main/webapp/WEB-INF/web.xml+12 41 modified
    @@ -26,46 +26,17 @@
             Welcome to Geronimo
         </description>
     
    -    <!--<servlet>-->
    -        <!--<servlet-name>jsp_sample_installer</servlet-name>-->
    -        <!--<servlet-class>org.apache.geronimo.welcome.AbsentSampleServlet</servlet-class>-->
    -        <!--<init-param>-->
    -            <!--<param-name>moduleId</param-name>-->
    -            <!--<param-value>org.apache.geronimo.samples/jsp-examples-SERVER//car</param-value>-->
    -        <!--</init-param>-->
    -    <!--</servlet>-->
    -
    -    <!--<servlet>-->
    -        <!--<servlet-name>servlet_sample_installer</servlet-name>-->
    -        <!--<servlet-class>org.apache.geronimo.welcome.AbsentSampleServlet</servlet-class>-->
    -        <!--<init-param>-->
    -            <!--<param-name>moduleId</param-name>-->
    -            <!--<param-value>org.apache.geronimo.samples/servlet-examples-SERVER//car</param-value>-->
    -        <!--</init-param>-->
    -    <!--</servlet>-->
    -
    -    <!--<servlet>-->
    -        <!--<servlet-name>ldap_sample_installer</servlet-name>-->
    -        <!--<servlet-class>org.apache.geronimo.welcome.AbsentSampleServlet</servlet-class>-->
    -        <!--<init-param>-->
    -            <!--<param-name>moduleId</param-name>-->
    -            <!--<param-value>org.apache.geronimo.samples/ldap-sample-app-SERVER//car</param-value>-->
    -        <!--</init-param>-->
    -    <!--</servlet>-->
    -
    -    <!--<servlet-mapping>-->
    -        <!--<servlet-name>jsp_sample_installer</servlet-name>-->
    -        <!--<url-pattern>/jsp-examples/*</url-pattern>-->
    -    <!--</servlet-mapping>-->
    -
    -    <!--<servlet-mapping>-->
    -        <!--<servlet-name>servlet_sample_installer</servlet-name>-->
    -        <!--<url-pattern>/servlets-examples/*</url-pattern>-->
    -    <!--</servlet-mapping>-->
    -
    -    <!---<servlet-mapping>-->
    -        <!--<servlet-name>ldap_sample_installer</servlet-name>-->
    -        <!--<url-pattern>/ldap-demo/*</url-pattern>-->
    -    <!--</servlet-mapping>-->
    +    <!-- XSS/XSRF filter -->
    +    <filter>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <filter-class>org.apache.geronimo.console.filter.XSSXSRFFilter</filter-class>
    +    </filter>
    +    <filter-mapping>
    +        <filter-name>XSSXSRFFilter</filter-name>
    +        <url-pattern>/*</url-pattern>
    +    </filter-mapping>
    +    <listener>
    +        <listener-class>org.apache.geronimo.console.filter.XSSXSRFFilter</listener-class>
    +    </listener>
     
     </web-app>
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

13

News mentions

0

No linked articles in our index yet.