/** * @param array $failedParams * @param string $highlightParamFormat * @param string $highlightMatchFormat * @return string */ public function highlightFailedParams($failedParams = array(), $highlightParamFormat = '[param]%s[/param]', $highlightMatchFormat = '[match]%s[/match]') { $highlights = array(); // Cap at 50kb $maxRequestLen = 1024 * 50; $this->highlightParamFormat = $highlightParamFormat; $this->highlightMatchFormat = $highlightMatchFormat; if (is_array($failedParams)) { foreach ($failedParams as $paramKey => $categories) { foreach ($categories as $categoryKey => $failedRules) { foreach ($failedRules as $failedRule) { $rule = $failedRule['rule']; /** @var wfWAFRuleComparisonFailure $failedComparison */ $failedComparison = $failedRule['failedComparison']; $action = $failedRule['action']; $paramKey = $failedComparison->getParamKey(); if (preg_match('/request\\.([a-z0-9]+)(?:\\[(.*?)\\](.*?))?$/i', $paramKey, $matches)) { $global = $matches[1]; if (method_exists('wfWAFRequestInterface', "get" . ucfirst($global))) { $highlight = array('match' => $failedComparison->getMatches()); if (isset($matches[2])) { $highlight['param'] = "{$matches['2']}{$matches['3']}"; } $highlights[$global][] = $highlight; } } } } } } $uri = $this->getURI(); $queryStringPos = wfWAFUtils::strpos($uri, '?'); if ($queryStringPos !== false) { $uri = wfWAFUtils::substr($uri, 0, $queryStringPos); } $queryString = $this->getQueryString(); if ($queryString) { $uri .= '?' . http_build_query($queryString); } if (!empty($highlights['queryString'])) { foreach ($highlights['queryString'] as $matches) { if (!empty($matches['param'])) { $this->highlightMatches = $matches['match']; $uri = preg_replace_callback('/(&|\\?|^)(' . preg_quote(urlencode($matches['param']), '/') . ')=(.*?)(&|$)/', array($this, 'highlightParam'), $uri); } } } if (!empty($highlights['uri'])) { foreach ($highlights['uri'] as $matches) { if ($matches) { } } $uri = sprintf($highlightParamFormat, $uri); } $request = "{$this->getMethod()} {$uri} HTTP/1.1\n"; $hasAuth = false; $auth = $this->getAuth(); if (is_array($this->getHeaders())) { foreach ($this->getHeaders() as $header => $value) { switch (wfWAFUtils::strtolower($header)) { case 'cookie': // TODO: Hook up highlights to cookies $cookies = ''; foreach ($this->getCookies() as $cookieName => $cookieValue) { $cookies .= $cookieName . '=' . urlencode($cookieValue) . '; '; } $request .= 'Cookie: ' . trim($cookies) . "\n"; break; case 'host': $request .= 'Host: ' . $this->getHost() . "\n"; break; case 'authorization': $hasAuth = true; if ($auth) { $request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n"; } break; default: $request .= $header . ': ' . $value . "\n"; break; } } } if (!$hasAuth && $auth) { $request .= 'Authorization: Basic ' . base64_encode($auth['user'] . ':' . $auth['password']) . "\n"; } $body = $this->getBody(); $contentType = $this->getHeaders('Content-Type'); if (is_array($body)) { if (wfWAFUtils::stripos($contentType, 'application/x-www-form-urlencoded') === 0) { $body = http_build_query($body); if (!empty($highlights['body'])) { foreach ($highlights['body'] as $matches) { if (!empty($matches['param'])) { $this->highlightMatches = $matches['match']; $body = preg_replace_callback('/(&|^)(' . preg_quote(urlencode($matches['param']), '/') . ')=(.*?)(&|$)/', array($this, 'highlightParam'), $body); } } } } else { if (preg_match('/^multipart\\/form\\-data; boundary=(.*?)$/i', $contentType, $boundaryMatches)) { $boundary = $boundaryMatches[1]; $bodyArray = array(); foreach ($body as $key => $value) { $bodyArray = array_merge($bodyArray, $this->reduceBodyParameter($key, $value)); } $body = ''; foreach ($bodyArray as $param => $value) { if (!empty($highlights['body'])) { foreach ($highlights['body'] as $matches) { if (!empty($matches['param']) && $matches['param'] === $param) { $value = sprintf($this->highlightParamFormat, $value); if (is_array($matches['match'][0])) { $replace = array(); foreach ($matches['match'][0] as $key => $match) { $replace[$match] = sprintf($this->highlightMatchFormat, $match); } if ($replace) { $value = str_replace(array_keys($replace), $replace, $value); } } else { // preg_match $value = str_replace($matches['match'][0], sprintf($this->highlightMatchFormat, $matches['match'][0]), $value); } break; } } } $body .= <<<FORM --{$boundary} Content-Disposition: form-data; name="{$param}" {$value} FORM; } foreach ($this->getFiles() as $param => $file) { $name = array_key_exists('name', $file) ? $file['name'] : ''; if (is_array($name)) { continue; // TODO: implement files as arrays } $mime = array_key_exists('type', $file) ? $file['type'] : ''; $value = ''; $lenToRead = $maxRequestLen - (wfWAFUtils::strlen($request) + wfWAFUtils::strlen($body) + 1); if (array_key_exists('content', $file)) { $value = $file['content']; } else { if ($lenToRead > 0 && file_exists($file['tmp_name'])) { $handle = fopen($file['tmp_name'], 'r'); $value = fread($handle, $lenToRead); fclose($handle); } } if (!empty($highlights['fileNames'])) { foreach ($highlights['fileNames'] as $matches) { if (!empty($matches['param']) && $matches['param'] === $param) { $name = sprintf($this->highlightParamFormat, $name); $name = str_replace($matches['match'][0], sprintf($this->highlightMatchFormat, $matches['match'][0]), $name); break; } } } $body .= <<<FORM --{$boundary} Content-Disposition: form-data; name="{$param}"; filename="{$name}" Content-Type: {$mime} Expires: 0 {$value} FORM; } if ($body) { $body .= "--{$boundary}--\n"; } } } } if (!is_string($body)) { $body = ''; } $request .= "\n" . $body; if (wfWAFUtils::strlen($request) > $maxRequestLen) { $request = wfWAFUtils::substr($request, 0, $maxRequestLen); } return $request; }