2020import java .net .InetAddress ;
2121import java .net .URLDecoder ;
2222import java .net .UnknownHostException ;
23+ import java .util .ArrayList ;
2324import java .util .Arrays ;
2425import java .util .HashMap ;
2526import java .util .HashSet ;
@@ -85,6 +86,8 @@ public class ApiServlet extends HttpServlet {
8586 private static final Logger ACCESSLOGGER = LogManager .getLogger ("apiserver." + ApiServlet .class .getName ());
8687 private static final String REPLACEMENT = "_" ;
8788 private static final String LOGGER_REPLACEMENTS = "[\n \r \t ]" ;
89+ private static final Pattern SENSITIVE_QUERY_PARAMETER = Pattern .compile (
90+ ".*(?:password|privatekey|accesskey|secretkey)|sessionkey|secretcode|token|userdata" , Pattern .DOTALL );
8891 private static final Pattern GET_REQUEST_COMMANDS = Pattern .compile ("^(get|list|query|find)(\\ w+)+$" );
8992 private static final HashSet <String > GET_REQUEST_COMMANDS_LIST = new HashSet <>(Set .of ("isaccountallowedtocreateofferingswithtags" ,
9093 "readyforshutdown" , "cloudianisenabled" , "quotabalance" , "quotasummary" , "quotatarifflist" , "quotaisenabled" , "quotastatement" , "verifyoauthcodeandgetuser" ));
@@ -100,6 +103,7 @@ public class ApiServlet extends HttpServlet {
100103 "updaterolepermission" ,
101104 "updateprojectrolepermission" ,
102105 "createstoragepool" ,
106+ "addobjectstoragepool" ,
103107 "addhost" ,
104108 "updatehostpassword" ,
105109 "addcluster" ,
@@ -172,7 +176,7 @@ void utf8Fixup(final HttpServletRequest req, final Map<String, Object[]> params)
172176 String value = decodeUtf8 (paramTokens [1 ]);
173177 params .put (name , new String [] {value });
174178 } else {
175- LOGGER .debug ("Invalid parameter in URL found. param: " + param );
179+ LOGGER .debug ("Invalid parameter in URL found (missing equals sign)." );
176180 }
177181 }
178182 }
@@ -199,8 +203,8 @@ public void run() {
199203 private void checkSingleQueryParameterValue (Map <String , String []> params ) {
200204 params .forEach ((k , v ) -> {
201205 if (v .length > 1 ) {
202- String message = String .format ("Query parameter '%s' has multiple values %s . Only the last value will be respected." +
203- "It is advised to pass only a single parameter" , k , Arrays . toString ( v ) );
206+ String message = String .format ("Query parameter '%s' has %d values. Only the last value will be respected. " +
207+ "It is advised to pass only a single parameter" , saveLogString ( k ), v . length );
204208 LOGGER .warn (message );
205209 }
206210 });
@@ -237,17 +241,15 @@ void processRequestInContext(final HttpServletRequest req, final HttpServletResp
237241
238242 // logging the request start and end in management log for easy debugging
239243 String reqStr = "" ;
240- String cleanQueryString = StringUtils . cleanString ( req .getQueryString ());
244+ String cleanQueryString = getCleanQueryString ( command , req .getQueryString (), reqParams );
241245 if (LOGGER .isDebugEnabled ()) {
242246 reqStr = auditTrailSb .toString () + " " + cleanQueryString ;
243247 if (req .getMethod ().equalsIgnoreCase ("POST" ) && org .apache .commons .lang3 .StringUtils .isNotBlank (command )) {
244- if (! POST_REQUESTS_TO_DISABLE_LOGGING . contains (command . toLowerCase ()) && ! reqParams . containsKey ( ApiConstants . USER_DATA )) {
248+ if (shouldLogRequestParameters (command , reqParams )) {
245249 String cleanParamsString = getCleanParamsString (reqParams );
246250 if (org .apache .commons .lang3 .StringUtils .isNotBlank (cleanParamsString )) {
247251 reqStr += "\n " + cleanParamsString ;
248252 }
249- } else {
250- reqStr += " " + command ;
251253 }
252254 }
253255 LOGGER .debug ("===START=== " + reqStr );
@@ -580,7 +582,7 @@ protected void setClientAddressForConsoleEndpointAccess(String command, Map<Stri
580582
581583 @ Nullable
582584 private String saveLogString (String stringToLog ) {
583- return stringToLog == null ? null : stringToLog .replace (LOGGER_REPLACEMENTS , REPLACEMENT );
585+ return stringToLog == null ? null : stringToLog .replaceAll (LOGGER_REPLACEMENTS , REPLACEMENT );
584586 }
585587
586588 /**
@@ -779,4 +781,52 @@ private String getCleanParamsString(Map<String, String[]> reqParams) {
779781
780782 return cleanParamsString .toString ();
781783 }
784+
785+ protected boolean shouldLogRequestParameters (String command , Map <String , String []> reqParams ) {
786+ return (org .apache .commons .lang3 .StringUtils .isBlank (command )
787+ || !POST_REQUESTS_TO_DISABLE_LOGGING .contains (command .toLowerCase (java .util .Locale .ROOT )))
788+ && !reqParams .containsKey (ApiConstants .USER_DATA );
789+ }
790+
791+ protected String getCleanQueryString (String command , String queryString , Map <String , String []> reqParams ) {
792+ boolean sensitiveRequest = !shouldLogRequestParameters (command , reqParams );
793+ boolean includeCommand = sensitiveRequest && org .apache .commons .lang3 .StringUtils .isNotBlank (command );
794+ if (org .apache .commons .lang3 .StringUtils .isEmpty (queryString )) {
795+ return includeCommand ? "command=" + saveLogString (command ) : "" ;
796+ }
797+
798+ List <String > cleanParameters = new ArrayList <>();
799+ try {
800+ // Split before decoding so an encoded ampersand remains part of its original value.
801+ // Read the query only: reqParams also contains the POST body.
802+ for (String parameter : queryString .split ("&" , -1 )) {
803+ int separator = parameter .indexOf ('=' );
804+ if (separator < 0 ) {
805+ continue ;
806+ }
807+ String name = decodeUtf8 (parameter .substring (0 , separator ));
808+ if (ApiConstants .COMMAND .equals (name )) {
809+ includeCommand = false ;
810+ }
811+ String lowerName = name .toLowerCase (java .util .Locale .ROOT );
812+ if (SENSITIVE_QUERY_PARAMETER .matcher (lowerName ).matches ()) {
813+ continue ;
814+ }
815+ // Sensitive requests can carry credentials in indexed maps. Omit those entries
816+ // rather than trust descriptors that may be in the POST body or use different case.
817+ if (sensitiveRequest && (name .indexOf ('[' ) >= 0 || ApiConstants .DETAILS .equals (lowerName ))) {
818+ continue ;
819+ }
820+ cleanParameters .add (parameter );
821+ }
822+ } catch (IllegalArgumentException e ) {
823+ // Formatting must not expose an undecodable query or change request processing.
824+ return includeCommand ? "command=" + saveLogString (command ) : "" ;
825+ }
826+ if (includeCommand ) {
827+ cleanParameters .add (0 , "command=" + saveLogString (command ));
828+ }
829+ // Preserve the existing cleaning of nested URLs and JSON in the retained query fields.
830+ return StringUtils .cleanString (String .join ("&" , cleanParameters ));
831+ }
782832}
0 commit comments