AEM 6.5: ESAPI Support

How to implement ESAPI logger in AEM 65 to avoid CRLF issues

Author : TharunDate      :  Aug, 2021

Overview:

The context for why use the ESAPI logger. Using the ESAPI logger we can avoid some of the security logger issues like Carriage return /r or newline /n. ESAPI (The OWASP Enterprise Security API) is a free, open-source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI libraries are designed to make it easier for programmers to retrofit security into existing applications. The ESAPI libraries also serve as a solid foundation for new development.

With AEM 6.5 from version 6.5.6+ onwards, AEM supports the ESAPI logging with the dependency to work perfectly OOTB without custom coding or exposing the resource files but it does not expose the dependent interface class as those are declared as private. so I would walk through how to include the dependency and make it work with AEM.

With AEM 6.3.3 or less we had to create a module within our project and had to pass the resource file for Esapi class as the JVM parameter at the start of the AEM application since we had less support with 6.3 dependencies. with the upgrade to 65 were not able to solve the issue of our custom solution with retrofit to 65 for the obvious reason we customized it ..! ugh dam-it. So took a step back by making it simple, I checked by decompiling the bundle the sling logger has the dependency for esapi and i can leverage it now since it supports esapi logger OOTB. 

Implementation Details:

we have included the dependency of the apache.sling.xss dependency to implement the ESAPI

      <dependency>

            <groupId>commons-fileupload</groupId>

            <artifactId>commons-fileupload</artifactId>

        </dependency>

        <dependency>

            <groupId>org.apache.sling</groupId>

            <artifactId>org.apache.sling.xss</artifactId>

            <scope>provided</scope>

        </dependency>

        <dependency>

            <groupId>org.apache.commons</groupId>

            <artifactId>commons-collections4</artifactId>

            <version>4.4</version>

        </dependency>

remove the slf4j dependency and use the org.owasp.esapi as below

            <dependency>

                <groupId>org.slf4j</groupId>

                <artifactId>slf4j-simple</artifactId>

                <version>1.7.6</version>

            <dependency>

                <groupId>org.owasp.esapi</groupId>

                <artifactId>esapi</artifactId>

                <version>2.2.3.1</version>


Since the apache sling XSS does not expose the dependencies and declared as private it. I have exposed those dependency in my code base. 

<Export-Package>
com.kryptonsquare.app.kpp.util.*, bsh, nu.xom, org.owasp.esapi.*, org.owasp.validator.html.*,  org.owasp.validator.css, org.apache.xerces.*, org.apache.xml.serialize, org.apache.xml.resolver,

org.apache.xml.resolver.readers, org.cyberneko.html.*, org.w3c.css.sac, org.apache.batik.css.parser, 

org.apache.batik.*, org.apache.xmlgraphics.java2d.color, org.w3c.dom.svg, org.apache.commons.beanutils,

org.apache.commons.beanutils.converters,org.apache.commons.beanutils.expression, org.apache.commons.configuration.*,org.apache.commons.digester,org.apache.commons.digester.substitution, org.apache.commons.digester.xmlrules,org.apache.commons.jexl2,org.apache.commons.jxpath, org.apache.commons.jxpath.ri,org.apache.commons.jxpath.ri.compiler,org.apache.commons.jxpath.ri.model, org.apache.commons.vfs2,org.apache.commons.vfs2.provider,org.apache.xml.resolver.helpers, org.apache.xml.resolver.tools, sun.io

</Export-Package>

add the ESAPI properties to the resources folder. with the code base in the maven project . here is sample properties file  
ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory

In the class and that wraps the methods for the ESAPI  with the class dependency

package com.kryptonsquare.app.kp.util.ESAPI;


import org.owasp.esapi.ESAPI;

import org.owasp.esapi.Logger;


public final class ESAPILogger {

    private static final String NULL = "null";

    private static final String ERR_MSG = "There was an error creating the log message!";


    private Class clazz;


    private ESAPILogger() { }


    private ESAPILogger(Class clazz) {

        this.clazz = clazz;

    }


    public static ESAPILogger getLogger(Class clazz) {

        return new ESAPILogger(clazz);

    }


    public boolean isTraceEnabled() {

        return ESAPI.getLogger(clazz).isTraceEnabled();

    }


    public void trace(String msg) {

        ESAPI.getLogger(clazz).trace(Logger.EVENT_SUCCESS, msg);

    }


    public void trace(String format, Object arg) {

        try {

            if (arg == null) {

                arg = NULL;

            }

            ESAPI.getLogger(clazz).trace(Logger.EVENT_SUCCESS, (format.contains("{}")) ? format.replace("{}", arg.toString()) : format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void trace(String format, Object arg1, Object arg2) {

        try {

            String message = handleTwoArguments(format, arg1, arg2);

            ESAPI.getLogger(clazz).trace(Logger.EVENT_SUCCESS, message);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void trace(String format, Object... arguments) {

        try {

            format = handleMultipleArguments(format, arguments);

            ESAPI.getLogger(clazz).trace(Logger.EVENT_SUCCESS, format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void trace(String msg, Throwable t) {

        ESAPI.getLogger(clazz).trace(Logger.EVENT_FAILURE, msg, t);

    }


    public boolean isDebugEnabled() {

        return ESAPI.getLogger(clazz).isDebugEnabled();

    }


    public void debug(String msg) {

        ESAPI.getLogger(clazz).debug(Logger.EVENT_SUCCESS, msg);

    }


    public void debug(String format, Object arg) {

        try {

            if (arg == null) {

                arg = NULL;

            }

            ESAPI.getLogger(clazz).debug(Logger.EVENT_SUCCESS, (format.contains("{}")) ? format.replace("{}", arg.toString()) : format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void debug(String format, Object arg1, Object arg2) {

        try {

            String message = handleTwoArguments(format, arg1, arg2);

            ESAPI.getLogger(clazz).debug(Logger.EVENT_SUCCESS, message);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void debug(String format, Object... arguments) {

        try {

            format = handleMultipleArguments(format, arguments);

            ESAPI.getLogger(clazz).debug(Logger.EVENT_SUCCESS, format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void debug(String msg, Throwable t) {

        ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, msg, t);

    }


    public boolean isInfoEnabled() {

        return ESAPI.getLogger(clazz).isInfoEnabled();

    }


    public void info(String msg) {

        ESAPI.getLogger(clazz).info(Logger.EVENT_SUCCESS, msg);

    }


    public void info(String format, Object arg) {

        try {

            if (arg == null) {

                arg = NULL;

            }

            ESAPI.getLogger(clazz).info(Logger.EVENT_SUCCESS, (format.contains("{}")) ? format.replace("{}", arg.toString()) : format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void info(String format, Object arg1, Object arg2) {

        try {

            String message = handleTwoArguments(format, arg1, arg2);

            ESAPI.getLogger(clazz).info(Logger.EVENT_SUCCESS, message);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void info(String format, Object... arguments) {

        try {

            format = handleMultipleArguments(format, arguments);

            ESAPI.getLogger(clazz).info(Logger.EVENT_SUCCESS, format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void info(String msg, Throwable t) {

        ESAPI.getLogger(clazz).info(Logger.EVENT_FAILURE, msg, t);

    }


    public boolean isWarnEnabled() {

        return ESAPI.getLogger(clazz).isWarningEnabled();

    }


    public void warn(String msg) {

        ESAPI.getLogger(clazz).warning(Logger.EVENT_UNSPECIFIED, msg);

    }


    public void warn(String format, Object arg) {

        try {

            if (arg == null) {

                arg = NULL;

            }

            ESAPI.getLogger(clazz).warning(Logger.EVENT_UNSPECIFIED,

                    (format.contains("{}")) ? format.replace("{}", arg.toString()) : format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void warn(String format, Object arg1, Object arg2) {

        try {

            String message = handleTwoArguments(format, arg1, arg2);

            ESAPI.getLogger(clazz).warning(Logger.EVENT_UNSPECIFIED, message);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void warn(String format, Object... arguments) {

        try {

            format = handleMultipleArguments(format, arguments);

            ESAPI.getLogger(clazz).warning(Logger.EVENT_UNSPECIFIED, format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void warn(String msg, Throwable t) {

        ESAPI.getLogger(clazz).warning(Logger.EVENT_FAILURE, msg, t);

    }


    public boolean isErrorEnabled() {

        return ESAPI.getLogger(clazz).isErrorEnabled();

    }


    public void error(String msg) {

        ESAPI.getLogger(clazz).error(Logger.EVENT_FAILURE, msg);

    }


    public void error(String format, Object arg) {

        try {

            if (arg == null) {

                arg = NULL;

            }

            ESAPI.getLogger(clazz).error(Logger.EVENT_FAILURE, (format.contains("{}")) ? format.replace("{}", arg.toString()) : format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void error(String format, Object arg1, Object arg2) {

        try {

            String message = handleTwoArguments(format, arg1, arg2);

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, message);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void error(String format, Object... arguments) {

        try {

            format = handleMultipleArguments(format, arguments);

            ESAPI.getLogger(clazz).error(Logger.EVENT_FAILURE, format);

        } catch (Exception e) {

            ESAPI.getLogger(clazz).debug(Logger.EVENT_FAILURE, ERR_MSG, e);

        }

    }


    public void error(String msg, Throwable t) {

        ESAPI.getLogger(clazz).error(Logger.EVENT_FAILURE, msg, t);

    }


    private String handleTwoArguments(String format, Object arg1, Object arg2) {

        if (arg1 == null) {

            arg1 = NULL;

        }

        if (arg2 == null) {

            arg2 = NULL;

        }

        String message = (format.contains("{}"))

                ? format.replaceFirst("\\{}", arg1.toString()).replaceFirst("\\{}", arg2.toString())

                : format;

        return message;

    }


    private String handleMultipleArguments(String format, Object... arguments) {

        int counter = 0;

        while (format.contains("{}") && counter < arguments.length ) {

            format = format.replaceFirst("\\{}", arguments[counter] == null ? NULL : arguments[counter].toString());

            counter++;

        }

        return format;

    }

}



About Author:

Tharun Mittapalli is an Adobe certified multi-solution architect with experience of over a decade working in digital solutions e-commerce solutions. His expertise in Retail, health & financial domains completed dozens of projects from scratch.