SecurityWrappedHttpServletResponse.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2026 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.openspcoop2.web.lib.mvc.security;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;

import org.openspcoop2.web.lib.mvc.security.exception.ValidationException;
import org.slf4j.Logger;

/**
 * SecurityWrappedHttpServletResponse
 * 
 * @author Giuliano Pintori (pintori@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class SecurityWrappedHttpServletResponse extends HttpServletResponseWrapper implements HttpServletResponse {

	private Logger log;
	private Validatore validator;

	public SecurityWrappedHttpServletResponse(HttpServletResponse httpServletResponse, Logger log) {
		 super(httpServletResponse);
		this.log = log;
		this.validator = Validatore.getInstance();

	}
	
    private HttpServletResponse getHttpServletResponse() {
        return (HttpServletResponse)super.getResponse();
    }
	
	@Override
	public void flushBuffer() throws IOException {
		this.getHttpServletResponse().flushBuffer();
	}

	@Override
	public int getBufferSize() {
		return this.getHttpServletResponse().getBufferSize();
	}

	@Override
	public String getCharacterEncoding() {
		return this.getHttpServletResponse().getCharacterEncoding();
	}

	@Override
	public String getContentType() {
		return this.getHttpServletResponse().getContentType();
	}

	@Override
	public Locale getLocale() {
		return this.getHttpServletResponse().getLocale();
	}

	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		return this.getHttpServletResponse().getOutputStream();
	}

	@Override
	public PrintWriter getWriter() throws IOException {
		return this.getHttpServletResponse().getWriter();
	}

	@Override
	public boolean isCommitted() {
		return this.getHttpServletResponse().isCommitted();
	}

	@Override
	public void reset() {
		this.getHttpServletResponse().reset();
	}

	@Override
	public void resetBuffer() {
		this.getHttpServletResponse().resetBuffer();
	}

	@Override
	public void setBufferSize(int arg0) {
		this.getHttpServletResponse().setBufferSize(arg0);
	}

	@Override
	public void setCharacterEncoding(String arg0) {
		this.getHttpServletResponse().setCharacterEncoding(arg0);
	}

	@Override
	public void setContentLength(int arg0) {
		this.getHttpServletResponse().setContentLength(arg0);
	}

	@Override
	public void setContentType(String arg0) {
		this.getHttpServletResponse().setContentType(arg0);
	}

	@Override
	public void setLocale(Locale arg0) {
		this.getHttpServletResponse().setLocale(arg0);
	}

	@Override
	public void addCookie(Cookie cookie) {
		this.getHttpServletResponse().addCookie(cookie);
	}

	@Override
	public void addDateHeader(String arg0, long arg1) {
		this.getHttpServletResponse().addDateHeader(arg0, arg1);
	}

	@Override
	public void addHeader(String arg0, String arg1) {
		String strippedValue = SecurityWrappedHttpServletResponse.stripControls(arg1);
		String safeValue = null;

		try {
			safeValue = this.validator.validate("Il valore dell'Header [" + arg0+ "]:["+arg1+"]", strippedValue, null, false, Costanti.PATTERN_RESPONSE_HTTP_HEADER_VALUE);
		} catch (ValidationException e) {
			this.log.warn("Impossibile impostare un header con il valore errato ["+arg0+"]: " + e.getMessage(), e);
		}

		boolean validValue = SecurityWrappedHttpServletResponse.notNullOrEmpty(safeValue, true);

		if (validValue) {
			this.getHttpServletResponse().addHeader(arg0, safeValue);
		}
	}

	@Override
	public void addIntHeader(String arg0, int arg1) {
		this.getHttpServletResponse().addIntHeader(arg0, arg1);
	}

	@Override
	public boolean containsHeader(String arg0) {
		return this.getHttpServletResponse().containsHeader(arg0);
	}

	@Override
	public String encodeRedirectURL(String arg0) {
		return this.getHttpServletResponse().encodeRedirectURL(arg0);
	}

	// jakarta api 5
	public String encodeRedirectUrl(String arg0) {
		return this.getHttpServletResponse().encodeRedirectURL(arg0);
	}

	@Override
	public String encodeURL(String arg0) {
		return this.getHttpServletResponse().encodeURL(arg0);
	}

	// jakarta api 5
	public String encodeUrl(String arg0) {
		return this.getHttpServletResponse().encodeURL(arg0);
	}

	@Override
	public void sendError(int arg0) throws IOException {
		this.getHttpServletResponse().sendError(arg0);
	}

	@Override
	public void sendError(int arg0, String arg1) throws IOException {
		this.getHttpServletResponse().sendError(arg0, arg1);
	}

	@Override
	public void sendRedirect(String arg0) throws IOException {
		this.getHttpServletResponse().sendRedirect(arg0);
	}

	@Override
	public void setDateHeader(String arg0, long arg1) {
		this.getHttpServletResponse().setDateHeader(arg0, arg1);
	}

	@Override
	public void setHeader(String arg0, String arg1) {
		String strippedValue = SecurityWrappedHttpServletResponse.stripControls(arg1);
		String safeValue = null;
		try {
			safeValue = this.validator.validate("Il valore dell'Header [" + arg0+ "]:["+arg1+"]", strippedValue, null, false, Costanti.PATTERN_RESPONSE_HTTP_HEADER_VALUE);
		} catch (ValidationException e) {
			this.log.warn("Impossibile impostare un header con il valore errato ["+arg0+"]: " + e.getMessage(), e);
		}

		boolean validValue = SecurityWrappedHttpServletResponse.notNullOrEmpty(safeValue, true);

		if (validValue) {
			this.getHttpServletResponse().setHeader(arg0, safeValue);
		}
	}

	@Override
	public void setIntHeader(String arg0, int arg1) {
		this.getHttpServletResponse().setIntHeader(arg0, arg1);
	}

	@Override
	public void setStatus(int arg0) {
		this.getHttpServletResponse().setStatus(arg0);
	}

	// jakarta api 5
	public void setStatus(int arg0, String arg1) {
		if(arg1!=null) {
			// nop
		}
		this.getHttpServletResponse().setStatus(arg0);
	}

	// v3
	
	@Override
	public void setContentLengthLong(long arg0) {
		this.getHttpServletResponse().setContentLengthLong(arg0);
	}

	@Override
	public String getHeader(String arg0) {
		String value = this.getHttpServletResponse().getHeader(arg0);
		if(value != null) {
			try {
				return this.validator.validate("Il valore dell'Header [" + arg0+ "]:["+value+"]", value, null, true, Costanti.PATTERN_RESPONSE_HTTP_HEADER_VALUE);
			} catch (ValidationException e) {
				this.log.warn("Errore di validazione: "+ e.getMessage(),e);
				return "";
			}
		}
		return null;
	}

	@Override
	public Collection<String> getHeaderNames() {
		return this.getHttpServletResponse().getHeaderNames();
	}

	@Override
	public Collection<String> getHeaders(String arg0) {
		List<String> v = new ArrayList<>();
		Collection<String> headers = this.getHttpServletResponse().getHeaders(arg0);
		List<String> lst = new ArrayList<>();
		if(headers != null && !headers.isEmpty())
			lst.addAll(headers);

		for (String value : lst) {
			try {
				v.add(this.validator.validate("Il valore dell'Header [" + arg0+ "]:["+value+"]", value, null, true, Costanti.PATTERN_REQUEST_HTTP_HEADER_VALUE));
			} catch (ValidationException e) {
				this.log.warn("Errore di validazione: "+ e.getMessage(),e);
			}
		}
		return v;

	}

	@Override
	public int getStatus() {
		return this.getHttpServletResponse().getStatus();
	}
	
	/**
	 * Removes all unprintable characters from a string
	 * and replaces with a space.
	 * @param input
	 * @return the stripped value
	 */
	public static String stripControls( String input ) {
		StringBuilder sb = new StringBuilder();
		for ( int i=0; i<input.length(); i++ ) {
			char c = input.charAt( i );
			if ( c > 0x20 && c < 0x7f ) {
				sb.append( c );
			} else {
				sb.append( ' ' );
			}
		}
		return sb.toString();
	}

	public static boolean notNullOrEmpty(String str, boolean trim) {
		if ( trim ) {
			return !( str == null || str.trim().equals("") );
		} else {
			return !( str == null || str.equals("") );
		}
	}
}