tch); else if (($match = $this->scanner->scan(self::MATCH_RULE_COMPARISON_END)) !== null) return $this->createToken(self::T_RULE_COMPARISON_END, $match); else if (($match = $this->scanner->scan(self::MATCH_ASSIGNMENT)) !== null) return $this->createToken(self::T_ASSIGNMENT, $match); else if (($match = $this->scanner->scan(self::MATCH_OPEN_BRACKET)) !== null) return $this->createToken(self::T_OPEN_BRACKET, $match); else if (($match = $this->scanner->scan(self::MATCH_CLOSE_BRACKET)) !== null) return $this->createToken(self::T_CLOSE_BRACKET, $match); else if (($match = $this->scanner->scan(self::MATCH_SINGLE_LINE_COMMENT)) !== null) return $this->createToken(self::T_SINGLE_LINE_COMMENT, $match); else if (($match = $this->scanner->scan(self::MATCH_MULTIPLE_LINE_COMMENT)) !== null) return $this->createToken(self::T_MULTIPLE_LINE_COMMENT, $match); else { $e = new wfWAFParserSyntaxError(sprintf('Invalid character "%s" found on line %d, column %d', $this->scanner->scanChar(), $this->scanner->getLine(), $this->scanner->getColumn())); $e->setParseLine($this->scanner->getLine()); $e->setParseColumn($this->scanner->getColumn()); throw $e; } } return false; } /** * @param $type * @param $value * @return wfWAFLexerToken */ protected function createToken($type, $value) { return new wfWAFLexerToken($type, $value, $this->scanner->getLine(), $this->scanner->getColumn()); } /** * @return string */ public function getRules() { return $this->rules; } /** * @param string $rules */ public function setRules($rules) { $this->rules = rtrim($rules); } } /** * */ class wfWAFLexerToken { private $type; private $value; private $line; private $column; /** * wfWAFRuleToken constructor. * * @param $type * @param $value * @param $line * @param $column */ public function __construct($type, $value, $line, $column) { $this->setType($type); $this->setValue($value); $this->setLine($line); $this->setColumn($column); } /** * @return string */ public function getLowerCaseValue() { return wfWAFUtils::strtolower($this->getValue()); } /** * @return string */ public function getUpperCaseValue() { return wfWAFUtils::strtoupper($this->getValue()); } /** * @return mixed */ public function getType() { return $this->type; } /** * @param mixed $type */ public function setType($type) { $this->type = $type; } /** * @return mixed */ public function getValue() { return $this->value; } /** * @param mixed $value */ public function setValue($value) { $this->value = $value; } /** * @return mixed */ public function getLine() { return $this->line; } /** * @param mixed $line */ public function setLine($line) { $this->line = $line; } /** * @return mixed */ public function getColumn() { return $this->column; } /** * @param mixed $column */ public function setColumn($column) { $this->column = $column; } } class wfWAFParserSyntaxError extends wfWAFException { private $parseLine; private $parseColumn; private $token; /** * @return mixed */ public function getToken() { return $this->token; } /** * @param mixed $token */ public function setToken($token) { $this->token = $token; } /** * @return mixed */ public function getParseLine() { return $this->parseLine; } /** * @param mixed $parseLine */ public function setParseLine($parseLine) { $this->parseLine = $parseLine; } /** * @return mixed */ public function getParseColumn() { return $this->parseColumn; } /** * @param mixed $parseColumn */ public function setParseColumn($parseColumn) { $this->parseColumn = $parseColumn; } } class wfWAFBaseParser { protected $tokens; protected $index; /** @var wfWAFLexerInterface */ protected $lexer; protected $checkpoint=0; public function __construct($lexer) { $this->lexer = $lexer; } /** * @param wfWAFLexerToken $token * @param mixed $type * @return bool */ protected function isTokenOfType($token, $type) { if (is_array($type)) { return $token && in_array($token->getType(), $type); } return $token && $token->getType() === $type; } /** * @param wfWAFLexerToken $token * @param int $type * @param string $message * @throws wfWAFParserSyntaxError */ protected function expectTokenTypeEquals($token, $type, $message = 'Wordfence WAF Syntax Error: Unexpected %s found on line %d, column %d. Expected %s.') { if ($token->getType() !== $type) { $this->triggerSyntaxError($token, sprintf($message, $token->getType(), $token->getLine(), $token->getColumn(), $type)); } } /** * @param wfWAFLexerToken $token * @param array $types * @param string $message * @throws wfWAFParserSyntaxError */ protected function expectTokenTypeInArray($token, $types, $message = 'Wordfence WAF Syntax Error: Unexpected %s found on line %d, column %d') { if (!in_array($token->getType(), $types)) { $this->triggerSyntaxError($token, sprintf($message, $token->getType(), $token->getLine(), $token->getColumn())); } } /** * @param wfWAFLexerToken $token * @param string $message * @throws wfWAFParserSyntaxError */ protected function triggerSyntaxError($token, $message = 'Wordfence WAF Syntax Error: Unexpected %s %s found on line %d, column %d') { $e = new wfWAFParserSyntaxError(sprintf($message, $token->getType(), $token->getValue(), $token->getLine(), $token->getColumn())); $e->setToken($token); $e->setParseLine($token->getLine()); $e->setParseColumn($token->getColumn()); throw $e; } /** * @return wfWAFLexerToken */ protected function currentToken() { return $this->getToken($this->index); } /** * @return bool|wfWAFLexerToken */ protected function nextToken() { $this->index++; return $this->getToken($this->index); } /** * @param string $message * @return wfWAFLexerToken * @throws wfWAFParserSyntaxError */ protected function expectNextToken($message = 'Expected statement') { $this->index++; if ($token = $this->getToken($this->index)) { return $token; } throw new wfWAFParserSyntaxError($message); } /** * @param int $index * @return mixed */ protected function getToken($index) { if (is_array($this->tokens) && array_key_exists($index, $this->tokens)) { return $this->tokens[$index]; } if ($token = $this->getLexer()->nextToken()) { $this->tokens[$index] = $token; return $this->tokens[$index]; } return false; } /** * @return wfWAFLexerInterface */ public function getLexer() { return $this->lexer; } /** * @param wfWAFLexerInterface $lexer */ public function setLexer($lexer) { $this->lexer = $lexer; } /** * @return mixed */ public function getTokens() { return $this->tokens; } /** * @param mixed $tokens */ public function setTokens($tokens) { $this->tokens = $tokens; } protected function setCheckpoint() { $this->checkpoint=$this->index; } protected function reset() { $this->index=$this->checkpoint; } } /** * */ class wfWAFStringScanner { private $string; private $remainingStringCache; private $length; private $pointer; private $remainingStringCachePointer; private $prevPointer; private $match; private $captures; /** * wfWAFStringScanner constructor. * @param $string */ public function __construct($string = null) { if (is_string($string)) { $this->setString($string); } } /** * @param $regex * @return mixed */ public function scan($regex) { $remaining = $this->getRemainingString(); if ($this->regexMatch($regex, $remaining, $matches)) { $matchLen = wfWAFUtils::strlen($matches[0]); if ($matchLen > 0 && wfWAFUtils::strpos($remaining, $matches[0]) === 0) { return $this->setState($matches, $this->getPointer() + $matchLen, $this->getPointer()); } } return $this->setState(); } /** * @param $regex * @return int|null */ public function skip($regex) { return $this->scan($regex) ? wfWAFUtils::strlen($this->getMatch()) : null; } /** * @return mixed */ public function scanChar() { return $this->scan('/./s'); } /** * @param string $regex * @return mixed */ public function check($regex) { $remaining = $this->getRemainingString(); if ($this->regexMatch($regex, $remaining, $matches)) { $matchLen = wfWAFUtils::strlen($matches[0]); if ($matchLen > 0 && wfWAFUtils::strpos($remaining, $matches[0]) === 0) { return $this->setState($matches); } } return $this->setState(); } /** * @param string $regex * @param string $remaining * @param $matches * @return int */ public function regexMatch($regex, $remaining, &$matches) { // $startTime = microtime(true); $result = preg_match($regex, $remaining, $matches); // printf("%s took %f seconds\n", $regex, microtime(true) - $startTime); return $result; } /** * @return bool */ public function eos() { return $this->getPointer() === $this->getLength(); } /** * @return string */ public function getRemainingString() { $pointer = $this->getPointer(); if ($pointer === $this->remainingStringCachePointer && is_string($this->remainingStringCache)) { return $this->remainingStringCache; } $this->remainingStringCache = wfWAFUtils::substr($this->getString(), $pointer); $this->remainingStringCachePointer = $pointer; return $this->remainingStringCache; } /** * @return $this */ public function reset() { $this->remainingStringCache = false; $this->setState(array(), 0, 0); return $this; } /** * The current line of the scanned string. * * @return int */ public function getLine() { if ($this->getPointer() + 1 > $this->getLength()) { return wfWAFUtils::substr_count($this->getString(), "\n") + 1; } return wfWAFUtils::substr_count($this->getString(), "\n", 0, $this->getPointer() + 1) + 1; } /** * The current column of the line of the scanned string. * * @return int */ public function getColumn() { return $this->getPointer() - ((int) wfWAFUtils::strrpos(wfWAFUtils::substr($this->getString(), 0, $this->getPointer() + 1), "\n")) + 1; } /** * @param array $matches * @param int|null $pointer * @param int|null $prevPointer * @return mixed */ protected function setState($matches = array(), $pointer = null, $prevPointer = null) { if ($pointer !== null) { $this->setPointer($pointer); } if ($prevPointer !== null) { $this->setPrevPointer($prevPointer); } if (is_array($matches)) { $this->setCaptures(array_slice($matches, 1)); if (count($matches) > 0) { $this->setMatch($matches[0]); } else { $this->setMatch(null); } } else { $this->setMatch(null); } return $this->getMatch(); } /** * @return string */ public function getString() { return $this->string; } /** * @param string $string * @throws InvalidArgumentException */ public function setString($string) { if (!is_string($string)) { throw new InvalidArgumentException(sprintf('String expected, got [%s]', gettype($string))); } $this->setLength(wfWAFUtils::strlen($string)); $this->string = $string; $this->reset(); } /** * @return int */ public function getLength() { return $this->length; } /** * @param int $length */ protected function setLength($length) { $this->length = $length; } /** * @param int $length */ public function advancePointer($length) { $this->setPointer($this->getPointer() + $length); } /** * @return int */ public function getPointer() { return $this->pointer; } /** * @param int $pointer */ protected function setPointer($pointer) { $this->pointer = $pointer; } /** * @return int */ public function getPrevPointer() { return $this->prevPointer; } /** * @param int $prevPointer */ protected function setPrevPointer($prevPointer) { $this->prevPointer = $prevPointer; } /** * @return mixed */ public function getMatch() { return $this->match; } /** * @param mixed $match */ protected function setMatch($match) { $this->match = $match; } /** * @param null $index * @return mixed */ public function getCaptures($index = null) { if (is_numeric($index)) { return isset($this->captures[$index]) ? $this->captures[$index] : null; } return $this->captures; } /** * @param mixed $captures */ protected function setCaptures($captures) { $this->captures = $captures; } } }