Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added property to omit event type information in logs #813

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

RodolfoAndre
Copy link

This PR adds the property "Logger.OmitEventTypeInLogs" that will make event type information not be prepended in log messages when set to true (default false).

Copy link
Contributor

@kwwall kwwall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RodolfoAndre - Please kindly address the code review comments. You may want to wait until @jeremiahjstacey adds his comments as he is the author and expert of the revised ESAPI logging.

*/
public LogPrefixAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) {
public LogPrefixAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName, boolean omitEventTypeInLogs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot change the signatures of any public or protected methods as that could potentially break someone's code. Instead, On line 39, just initialize this there instead of in the CTOR. You can do it it a static initializer block if you need to catch exceptions. The default value for omitEventTypeInLogs must be false in case the property value Logger.OmitEventTypeInLogs is not found in ESAPI.properties at all, which will not be uncommon because based on our experience, many ESAPI users never seem to update their ESAPI.properties file with the latest version. But that is the only way we can make this work.

An alternative--one that I don't like as much, but may find acceptable assuming that @jeremiahjstacey is okay with it is to add a new CTOR rather than just replacing this one and then have this existing CTOR, just invoke:

    this(logUserInfo, logClientInfo, logServerIp, logApplicaionName, appname, false);

That at least, can't break existing code, although it does at a bit of complexity to this class.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I have changed back. I have a question where should I get the OmitEventTypeInLogs property?

I could create a static variable in Slf4JLogFactory and use the static method to get this property and create a setOmitEventTypeInLogs method in LogPrefixAppender to set the value;
I can do that when the LogPrefixAppender instanced in Slf4JLogFactory static method. So I would get the property and create a setOmitEventTypeInLogs method in LogPrefixAppender to set the value.

Which approach do you think would be better or do you have a suggestion?

Copy link
Contributor

@kwwall kwwall Nov 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESAPI generally makes the assumption (except when running JUnit tests) that the value of a a specific property provided in ESAPI.properties doesn't change in the middle of your application, so as a result, it's generally fine to assume one a property is set, you can store its value in a static class variable.

That said, what I was thinking in the context of LogPrefixAppender was rather than changing the CTOR (or adding a new one), change your declaration of:

    private final boolean omitEventTypeInLogs;

to

    import static org.owasp.esapi.PropNames.OMIT_EVENT_TYPE_IN_LOGS;
    ...
    private static boolean omitEventTypeInLogs;

    static {
        try {
            omitEventTypeInLogs =
                ESAPI.securityConfiguration().getBooleanProp( OMIT_EVENT_TYPE_IN_LOGS );
        } catch (Exception ex) {    // Usually this will be ConfigurationException
            omitEventTypeInLogs = false;
        }
    }

Not sure if that is what you were asking though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! thank you. With this solution most of the comments bellow are fulfilled. I will send another patch shortly.

}

@Override
public String appendTo(String logName, EventType eventType, String message) {
if (omitEventTypeInLogs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look correct to me. This would also skip over all the other parameters collected in lines 78-81 (of your new code) . I may be missing something, but I think that around line 78, this should be changed to something like this:

    String eventTypeMsg = ( omitEventTypeInLogs ? "" : eventTypeSupplier.get().trim() );

I think doing this they way you have it drops a lot more than just the Logger.EVENT_TYPE.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was thinking in a pespective that everything inside the [] was related to event type. So I have made the change you suggested and then it will check if the prefix is empty, if so it will return just the message without the prefix, otherwise it will add the prefix in the message.

@@ -79,7 +80,8 @@ public class JavaLogFactory implements LogFactory {
boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(LOG_APPLICATION_NAME);
String appName = ESAPI.securityConfiguration().getStringProp(APPLICATION_NAME);
boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(LOG_SERVER_IP);
JAVA_LOG_APPENDER = createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName);
boolean omitEventTypeInLogs = ESAPI.securityConfiguration().getBooleanProp(OMIT_EVENT_TYPE_IN_LOGS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reasons noted in a comment in the LogPrefixAppender CTOR that you modifed, there is a (probably good) possibility that this property will not be found at all in the ESAPI.properties file, so you need to be prepared to catch ConfigurationException here and set omitEventTypeInlogs to false should that happen. Because we can't let a ConfigurationException propagate back to the caller if Logger.OmitEventTypeInLogs is not found. That would result in breaking people's code and as a result is just not acceptable.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:( Catching a runtime exception to avoid breaking feels dirty to me, but I do agree it has the unfavorable result of breaking implementations when new properties are required.

It is worth noting that there are actually 2 cases that the Exception may occur:

  1. the value is not set
  2. the value is not of the expected type (cannot be converted to a boolean)

In the second case, catching this exception may result in hiding a problem with a property value. I do think it is a less likely scenario, but still worth noting.

 @Override
    public Boolean getBooleanProp(String propertyName) throws ConfigurationException {
        try {
            return esapiPropertyManager.getBooleanProp(propertyName);
        } catch (ConfigurationException ex) {
            String property = properties.getProperty( propertyName );
            if ( property == null ) {
                throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " not found in ESAPI.properties");
            }
            if ( property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes" ) ) {
                return true;
            }
            if ( property.equalsIgnoreCase("false") || property.equalsIgnoreCase( "no" ) ) {
                return false;
            }
            throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " has incorrect " +
                    "type");
        }
    }

Copy link
Contributor

@kwwall kwwall Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeremiahjstacey - True, but the more common error is just going to be that people don't update their ESAPI.properties file regularly, so the property would be missing and I don't want ESAPI to start failing (and thus breaking code) because of that. We could have the ConfigurationException logged via logSpecial(), but I'd prefer dirty over adding a new CTOR and all the other ugliness that brings into ESAPI code. We can also note it in a comment there why this particular ugly choice is made over a different ugly choice.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can incorporate the check of property value as you mentitoned and ignore it if the property is not present. What do you think?

if (property != null) { if (property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes")) { omitEventTypeInLogs = true; } else if (property.equalsIgnoreCase("false") || property.equalsIgnoreCase("no")) { omitEventTypeInLogs = false; } else if (property != null) { throw new ConfigurationException("SecurityConfiguration for " + OMIT_EVENT_TYPE_IN_LOGS + " has incorrect " + "type"); } }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code I posted is the default implementation of getBooleanProp from the DefaultSecurityProvider. I just wanted to identify the alternative case that is being swallowed.

It is known and is still preferred to the probability of breaking an environment due to a missing property.

@RodolfoAndre You do not need to make any additional changes for this as far as I am concerned.

/*package*/ static LogAppender createLogAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) {
return new LogPrefixAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName);
/*package*/ static LogAppender createLogAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName, boolean omitEventTypeInLogs) {
return new LogPrefixAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName, omitEventTypeInLogs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May wish to change this, depending @jeremiahjstacey's take on my comments on the LogPrefixAppender CTOR. But at even if this remains the same here, we can't simple remove the old CTOR as the CTOR is public and thus off limits. (Once we start using Java 9 or later, we can better control this with Java modules, but until then, no changes to signatures of public or protected methods unless it is absolutely required to address a vulnerability.)

@@ -69,7 +71,8 @@ public class Slf4JLogFactory implements LogFactory {
boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(LOG_APPLICATION_NAME);
String appName = ESAPI.securityConfiguration().getStringProp(APPLICATION_NAME);
boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(LOG_SERVER_IP);
SLF4J_LOG_APPENDER = createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName);
boolean omitEventTypeInLogs = ESAPI.securityConfiguration().getBooleanProp(OMIT_EVENT_TYPE_IN_LOGS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as for JavaLogFactory class... for reasons noted in a comment in the LogPrefixAppender CTOR that you modifed, there is a (probably good) possibility that this property will not be found at all in the ESAPI.properties file, so you need to be prepared to catch ConfigurationException here and set omitEventTypeInlogs to false should that happen. Because we can't let a ConfigurationException propagate back to the caller if Logger.OmitEventTypeInLogs is not found. That would result in breaking people's code and as a result is just not acceptable.

@@ -63,7 +64,7 @@ public void testCtrArgTruePassthroughToDelegates() throws Exception {
whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy);
whenNew(ServerInfoSupplier.class).withArguments(testLoggerName).thenReturn(sisSpy);

LogPrefixAppender lpa = new LogPrefixAppender(true, true,true,true, testApplicationName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeremiahjstacey - I think we need to keep both tests, do we not?

@@ -84,7 +85,7 @@ public void testCtrArgFalsePassthroughToDelegates() throws Exception {
whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy);
whenNew(ServerInfoSupplier.class).withArguments(testLoggerName).thenReturn(sisSpy);

LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null);
LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto previous comment.

@@ -102,51 +103,61 @@ public void testDelegateCtrArgs() throws Exception {
whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy);
whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy);

LogPrefixAppender lpa = new LogPrefixAppender(true, true,true,true, testApplicationName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, same comment. I think it would just be best to copy the existing test(s) and then make a new test.

Also, you can now see why I don't like the whole idea of changing the CTOR.

@@ -171,23 +171,25 @@ public void checkScrubberWithoutEncoding() throws Exception {
*/
@Test
public void checkPassthroughAppenderConstruct() throws Exception {
LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, true, "");
LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, true, "", false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeremiahjstacey - Ditto here. I will trust your code review here. No further comments from me in this file.

@kwwall
Copy link
Contributor

kwwall commented Nov 21, 2023

@jeremiahjstacey - Please code review this when you have a moment. I have already made a lot of comments, but I'm not sure I understand the JUnit tests well enough unless I really dive into the gritty details of how they work. Note this addresses GitHub Issue #811.

…ppender, updated the prefix building and improved the tests
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there are also ESAPI.properties files in the test scope that should also be updated
https://github.com/ESAPI/esapi-java-legacy/blob/develop/src/test/resources/esapi/ESAPI.properties

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely correct. I thought at the time that I originally reviewed this, he had updated the src/test/resources/esapi/ESAPI.properties file to add that new property. Maybe he undid it in the last commit or maybe I just was imagining it, but yeah, it needs to be there too.

Comment on lines 110 to 115
if (logPrefix.toString().trim().isEmpty()) {
// if there isn't any log prefix we just send back the message without touching it
return message;
}

return String.format(RESULT_FORMAT, logPrefix.toString().trim(), message);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would feel better about this if it was a single return as a ternary:

String prefix = logPrefix.toString().trim();
return prefix.isEmpty() ? message : String.format(RESULT_FORMAT, prefix, message);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with that.

Comment on lines 190 to 191
// Using reflection API to set omitEventTypeInLogs field in LogPrefixAppender.
setOmitEventTypeInLogsFieldUsingReflection(lpa, omitEventTypeInLogs);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my previous comments about making the variable a member instance, we will not need reflection here.

System.setProperty(PropNames.OMIT_EVENT_TYPE_IN_LOGS, omitEventTypeInLogs);

 //Since everything is mocked these booleans don't much matter aside from the later verifies
 LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except, setting this is a System property still won't make that property get picked up by ESAPI.securityConfiguration().getBooleanProp().

@RodolfoAndre - We have a "trick" to make ESAPI properties work with JUnit tests so we can simulate various values that @jeremiahjstacey may have forgotten about. We use the org.owasp.esapi.SecurityConfigurationWrapper class for that. Rather than trying to describe how to use that for JUnit tests, I will instead refer you to
https://github.com/ESAPI/esapi-java-legacy/blob/develop/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java
as a typical example of how we use it. If after looking at that, you still can't figure it out, then let me know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial context is invalid for removing reflection utility, but if we're able to do that I think it would be a good thing.
Thank you for the context @kwwall

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kwwall I could change the property just once. After the first initalization the class won't get the property again. So I can't have test for true and false in the same class.

It will work if I change the test to another file or create a static class inside this one just for testing the flag. Do you have any idea what may I do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RodolfoAndre - The way I handled it was to create 2 separate JUnit classes, each of which tested one of the possible values. For example, for the property Validator.HtmlValidationAction, which can have the value "clean" or "throw", I respectively created:

(although for the latter, it looks as though I may have botched line 53, although I'm not sure it matters since it is set correctly in the @Before setup method on line 84).

But that explains the general approach of how I tested both variations. There's probably some other way to do it, but that worked. It's also why the JUnit file are named as they are. HTH.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. So I've added the new file with the tests and reverted the other one just adding a test to check if it is working with the flag off. It is in the new commits that I've sent.

@kwwall
Copy link
Contributor

kwwall commented May 24, 2024

@RodolfoAndre - There are requested changes here. Any chance you can make them before Sunday, 2024/05/26 as I'm considering doing a release this long holiday weekend. (See Discussion #834.) If not, I will leave this PR unmerged and it will have to wait until a future release. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants