/*
 * 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.
 */
package org.owasp.security.logging.mask;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import static org.hamcrest.CoreMatchers.is;

import org.junit.After;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;

import static org.mockito.Mockito.verify;

import org.mockito.runners.MockitoJUnitRunner;
import org.owasp.security.logging.SecurityMarkers;
import org.slf4j.LoggerFactory;

/**
 *
 * @author August Detlefsen [augustd@codemagi.com]
 */
@RunWith(MockitoJUnitRunner.class)
public class MaskingConverterTest {

	LoggerContext loggerContext = (LoggerContext) LoggerFactory
			.getILoggerFactory();

	Logger LOGGER = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

	PatternLayoutEncoder encoder;

	@Mock
	private RollingFileAppender<ILoggingEvent> mockAppender = new RollingFileAppender<ILoggingEvent>();

	// Captor is genericised with ch.qos.logback.classic.spi.LoggingEvent
	@Captor
	private ArgumentCaptor<LoggingEvent> captorLoggingEvent;

	@Before
	public void setUp() {
		PatternLayout.defaultConverterMap.put("mask",
				MaskingConverter.class.getName());

		encoder = new PatternLayoutEncoder();
		encoder.setContext(loggerContext);
		encoder.setPattern("%-4relative [%thread] %-5level %logger{35} - %mask%n");
		encoder.start();

		mockAppender.setContext(loggerContext);
		mockAppender.setEncoder(encoder);
		mockAppender.start();

		LOGGER.addAppender(mockAppender);
	}

	@After
	public void teardown() {
		LOGGER.detachAppender(mockAppender);
	}

	@Test
	public void test() {
		String userid = "myId";
		String password = "secret";
		LOGGER.info(SecurityMarkers.CONFIDENTIAL, "userid={}, password='{}'",
				userid, password);

		// Now verify our logging interactions
		verify(mockAppender).doAppend(captorLoggingEvent.capture());

		// Get the logging event from the captor
		final LoggingEvent loggingEvent = captorLoggingEvent.getValue();

		// Check log level is correct
		assertThat(loggingEvent.getLevel(), is(Level.INFO));

		// Check the message being logged is correct
		String layoutMessage = encoder.getLayout().doLayout(loggingEvent);
		assertTrue(layoutMessage.contains("userid="
				+ MaskingConverter.MASKED_PASSWORD + ", password='"
				+ MaskingConverter.MASKED_PASSWORD + "'"));
		assertFalse(layoutMessage.contains("secret"));
	}

}

@@ -1518,16 +1518,41 @@
 Factory;
+%0Aimport org.slf4j.Marker;
 %0A%0A/**%0A *
@@ -3442,10 +3442,933 @@
 ));%0A%09%7D%0A%0A
+    /**%0A     * Test that masking works for combinations of markers and not just%0A     * SecurityMarkers.CONFIDENTIAL%0A     *%0A     * @see https://github.com/javabeanz/owasp-security-logging/issues/19%0A     */%0A    @Test%0A    public void markerTest() %7B%0A        Marker multiMarker = SecurityMarkers.getMarker(SecurityMarkers.CONFIDENTIAL, SecurityMarkers.SECURITY_FAILURE);%0A%0A        String ssn = %22123-45-6789%22;%0A        LOGGER.info(multiMarker, %22ssn=%7B%7D%22, ssn);%0A%0A        // Now verify our logging interactions%0A        verify(mockAppender).doAppend(captorLoggingEvent.capture());%0A%0A        // Get the logging event from the captor%0A        final LoggingEvent loggingEvent = captorLoggingEvent.getValue();%0A%0A        // Check the message being logged is correct%0A        String layoutMessage = encoder.getLayout().doLayout(loggingEvent);%0A        assertTrue(layoutMessage.contains(%22ssn=%22 + MaskingConverter.MASKED_PASSWORD));%0A    %7D%0A        %0A
 %7D%0A

Test masking with multi-markers