public function getResultIterator($path) { if ((Environment::isWindows() || Environment::isLinuxWithWine()) !== true) { $this->log(['FxCop requires Windows or Wine to execute', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } $compatibleVersions = ['Microsoft Fxcop 10.0', 'Microsoft Fxcop 1.36', 'Microsoft Fxcop 1.35']; $pluginDir = (new \Yasca\Core\IteratorBuilder())->from([__DIR__])->concat((new \Yasca\Core\IteratorBuilder())->from($compatibleVersions)->select(static function ($versionDir) { return __DIR__ . '/' . $versionDir; }))->concat((new \Yasca\Core\IteratorBuilder())->from(['ProgramFiles', 'ProgramFiles(x86)', 'ProgramW6432'])->where(static function ($specialDir) { return isset($_ENV[$specialDir]); })->select(static function ($specialDir) { return $_ENV[$specialDir]; })->selectMany(static function ($dir) use($compatibleVersions) { return (new \Yasca\Core\IteratorBuilder())->from($compatibleVersions)->select(static function ($versionDir) use($dir) { return "{$dir}/{$versionDir}"; }); }))->where(static function ($dir) { return (new \SplFileInfo("{$dir}/FxCopCmd.exe"))->isFile(); })->firstOrNull(); if ($pluginDir === null) { $this->log(['FxCop cannot be found. To enable the FxCop plugin, ' . 'install it from Microsoft in the default location ' . ' or copy FxCop to the ./Yasca/Plugins/FxCop directory.', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } try { $process = new Process((Environment::isLinuxWithWine() === true ? 'wine ' : '') . '"' . $pluginDir . '/FxCopCmd.exe" ' . '/ignoreinvalidtargets ' . '/searchgac ' . '/rule:"' . $pluginDir . '/Rules/SecurityRules.dll" ' . '/consolexsl:"' . __DIR__ . '/yasca.xsl" ' . '/quiet ' . (new \Yasca\Core\IteratorBuilder())->from(new \RecursiveDirectoryIterator($path))->whereRegex('`(?i)\\.(' . (new \Yasca\Core\IteratorBuilder())->from($this->getSupportedFileTypes())->select(static function ($element) { return \preg_quote($element, '`'); })->join('|') . ')$`u')->select(static function ($current) { return " /file:\"{$current}\""; })->join('')); } catch (ProcessStartException $e) { $this->log(['FxCop failed to start', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } $this->log(['FxCop launched', \Yasca\Logs\Level::INFO]); return $process->continueWith(function ($async) { list($stdout, $stderr) = $async->result(); $this->log(['FxCop completed', \Yasca\Logs\Level::INFO]); $dom = new \DOMDocument(); try { $success = $dom->loadXML($stdout); } catch (\ErrorException $e) { $success = false; } if ($success !== true) { if ($stdout === '') { $this->log(['FxCop did not return any data', \Yasca\Logs\Level::ERROR]); } else { $this->log(['FxCop did not return valid XML', \Yasca\Logs\Level::ERROR]); $this->log(["FxCop returned {$stdout}", \Yasca\Logs\Level::ERROR]); } return Async::fromResult(new \EmptyIterator()); } return (new \Yasca\Core\IteratorBuilder())->from($dom->getElementsByTagName('result'))->select(static function ($result) { return (new \Yasca\Result())->setOptions(['pluginName' => 'FxCop', 'severity' => "{$result->getAttribute('severity')}", 'category' => "{$result->getAttribute('category')}", 'filename' => "{$result->getAttribute('filename')}", 'references' => ["{$result->getAttribute('reference')}" => 'MSDN'], 'message' => "{$result->getAttribute('message')}", 'description' => "{$result->getAttribute('description')}"]); })->toFunctionPipe()->pipe([Async::_class, 'fromResult']); }); }
public function getResultIterator($path) { if (Environment::hasAtLeastJavaVersion(5) !== true) { $this->log(['FindBugs requires JRE 1.5 or later.', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } try { $process = new Process('"' . __DIR__ . '/bin/findbugs' . (Environment::isWindows() ? '.bat' : '') . '"' . ' -home "' . __DIR__ . '" ' . ' -include "' . __DIR__ . '/filter.xml" ' . '-textui -xml:withMessages -xargs -quiet'); } catch (ProcessStartException $e) { $this->log(['FindBugs failed to start', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } $this->log(['FindBugs launched', \Yasca\Logs\Level::INFO]); (new \Yasca\Core\FunctionPipe())->wrap($path)->pipe([Operators::_class, '_new'], '\\RecursiveDirectoryIterator')->toIteratorBuilder()->where(function ($fileinfo) { return $this->supportsExtension($fileinfo->getExtension()); })->select(static function ($fileinfo, $filepath) { return "{$filepath}\n"; })->forAll([$process, 'writeToStdin']); $process->closeStdin(); return $process->continueWith(function ($async) use($path) { list($stdout, $stderr) = $async->result(); $this->log(['FindBugs completed', \Yasca\Logs\Level::INFO]); $dom = new \DOMDocument(); try { $success = $dom->loadXML($stdout); } catch (\ErrorException $e) { $success = false; } if ($success !== true) { if ($stdout === '') { $this->log(['FindBugs did not return any data', \Yasca\Logs\Level::ERROR]); } else { $this->log(['FindBugs did not return valid XML', \Yasca\Logs\Level::ERROR]); $this->log(["FindBugs returned {$stdout}", \Yasca\Logs\Level::ERROR]); } return Async::fromResult(new \EmptyIterator()); } $bugPatterns = (new \Yasca\Core\IteratorBuilder())->from($dom->getElementsByTagName('BugPattern'))->selectKeys(static function ($patternNode) { return ["{$pattern->getElementsByTagName('Details')->item(0)->nodeValue}", "{$pattern->getAttribute('type')}"]; })->toArray(true); return (new \Yasca\Core\IteratorBuilder())->from($dom->getElementsByTagName('BugInstance'))->select(static function ($bugInstance) use(&$bugPatterns, $path) { $type = $bugInstance->getAttribute('type'); $sourceLine = $bugInstance->getElementsByTagName('SourceLine')->item(0); $shortMessage = $bugInstance->getElementsByTagName('ShortMessage')->item(0)->nodeValue; return (new \Yasca\Result())->setOptions(['pluginName' => 'FindBugs', 'severity' => "{$bugInstance->getAttribute('priority')}", 'category' => (new \Yasca\Core\FunctionPipe())->wrap($bugInstance->getAttribute('category'))->pipeLast('\\str_replace', '_', ' ')->pipe('\\strtolower')->pipe('\\ucwords')->unwrap(), 'lineNumber' => "{$sourceLine->getAttribute('start')}", 'filename' => "{$path}/{$sourceLine->getAttribute('sourcepath')}", 'references' => ['http://findbugs.sourceforge.net/bugDescriptions.html#' . \urlencode($type) => 'FindBugs Bug Description'], 'message' => "{$shortMessage}", 'description' => <<<EOT {$shortMessage} {$bugPatterns[$type]} EOT ]); })->toFunctionPipe()->pipe([Async::_class, 'fromResult']); }); }
public function getResultIterator($path) { if (Environment::hasAtLeastJavaVersion(4) !== true) { $this->log(['PMD requires JRE 1.4 or later.', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } try { $process = new Process('java -cp "' . (new \Yasca\Core\FunctionPipe())->wrap(__DIR__)->pipe([Operators::_class, '_new'], '\\FilesystemIterator')->toIteratorBuilder()->select(static function ($u, $key) { return $key; })->whereRegex('`\\.jar$`ui')->join(PATH_SEPARATOR) . '" net.sourceforge.pmd.PMD "' . $path . '"' . ' xml' . ' "' . __DIR__ . '/yasca-rules.xml"'); // ' "' . __DIR__ . '/yasca-rules.xml"' //); } catch (ProcessStartException $e) { $this->log(['PMD failed to start', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } $this->log(['PMD launched', \Yasca\Logs\Level::INFO]); return $process->continueWith(function ($async) { list($stdout, $stderr) = $async->result(); $this->log(['PMD completed', \Yasca\Logs\Level::INFO]); //$this->log([$stdout, \Yasca\Logs\Level::ERROR]); $dom = new \DOMDocument(); try { $success = $dom->loadXML($stdout); } catch (\ErrorException $e) { $success = false; } if ($success !== true) { if ($stdout === '') { $this->log(['PMD did not return any data', \Yasca\Logs\Level::ERROR]); $this->log([$stderr, \Yasca\Logs\Level::ERROR]); } else { $this->log(['PMD did not return valid XML', \Yasca\Logs\Level::ERROR]); $this->log(["PMD returned {$stdout}", \Yasca\Logs\Level::ERROR]); } return Async::fromResult(new \EmptyIterator()); } return (new \Yasca\Core\IteratorBuilder())->from($dom->getElementsByTagName('file'))->selectMany(static function ($fileNode) { return (new \Yasca\Core\IteratorBuilder())->from($fileNode->getElementsByTagName('violation'))->select(static function ($violationNode) use($fileNode) { return (new \Yasca\Result())->setOptions(['pluginName' => 'PMD', 'filename' => "{$fileNode->getAttribute('name')}", 'lineNumber' => "{$violationNode->getAttribute('beginline')}", 'category' => "{$violationNode->getAttribute('rule')}", 'severity' => "{$violationNode->getAttribute('priority')}", 'description' => "", 'message' => "", 'references' => ["{$violationNode->getAttribute('externalInfoUrl')}" => 'PMD Reference']]); }); })->toFunctionPipe()->pipe([Async::_class, 'fromResult']); }); }
public function getResultIterator($filepath) { if (Environment::hasAtLeastJavaVersion(6) !== true) { if ($this->firstRun === true) { $this->log(['Pixy requires JRE 1.6 or later.', \Yasca\Logs\Level::ERROR]); } $this->firstRun = false; return new \EmptyIterator(); } try { $process = new Process('"' . __DIR__ . '/pixy' . (Environment::isWindows() ? '.bat' : '') . '"' . ' "' . $filepath . '"'); } catch (ProcessStartException $e) { if ($this->firstRun === true) { $this->log(['Unable to start Pixy', \Yasca\Logs\Level::ERROR]); } $this->firstRun = false; return new \EmptyIterator(); } $this->log(['Pixy launched', \Yasca\Logs\Level::DEBUG]); return $process->continueWith(function ($async) { list($stdout, $stderr) = $async->result(); $this->log(['Pixy completed', \Yasca\Logs\Level::DEBUG]); $matches = []; \preg_match_all(<<<'EOT' `(*ANY)(?xim) ^ (?<analysis> XSS | SQL | File ) \s Analysis \s BEGIN \R (^ .* (?<! Analysis \s BEGIN | \d ) \R)* (?<results> (^ - \d* .* \: \d+ $ \R?)+ ) `u EOT , $stdout, $matches, PREG_SET_ORDER); return (new \Yasca\Core\IteratorBuilder())->from($matches)->selectKeys(static function ($analysisMatch) { return [$analysisMatch['results'], $analysisMatch['analysis']]; })->whereRegex(<<<'EOT' `(*ANY)(?xim) ^ - \d* (?<filename> .* ) : (?<lineNumber> \d+ ) $. `u EOT , \RegexIterator::ALL_MATCHES, 0, PREG_SET_ORDER)->selectMany(function ($results, $analysis) { if ($analysis === 'XSS') { $result = $this->newXssResult(); } elseif ($analysis === 'SQL') { $result = $this->newSqliResult(); } else { $result = (new \Yasca\Result())->setOptions(['description' => 'Generic File Vulnerability']); } return (new \Yasca\Core\IteratorBuilder())->from($results)->select(static function ($match) use($result) { $r = clone $result; return $r->setOptions(['pluginName' => 'Pixy', 'lineNumber' => "{$match['lineNumber']}", 'filename' => "{$match['filename']}", 'message' => "{$match['filename']}"]); }); })->toFunctionPipe()->pipe([Async::_class, 'fromResult']); }); }
public function getResultIterator($path) { if (Environment::isWindows() !== true) { $this->log(['The copy of CppCheck included with Yasca requires Windows', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } try { $process = new Process('"' . __DIR__ . '/cppcheck" ' . '--quiet ' . '--enable=all ' . '--inline-suppr ' . '--xml ' . '"' . $path . '"'); } catch (ProcessStartException $e) { $this->log(['CppCheck failed to start', \Yasca\Logs\Level::ERROR]); return new \EmptyIterator(); } $this->log(['CppCheck launched', \Yasca\Logs\Level::INFO]); return $process->continueWith(function ($async) { list($stdout, $stderr) = $async->result(); $this->log(['CppCheck completed', \Yasca\Logs\Level::INFO]); $regex = <<<'EOT' `No C or C\+\+ source files found\.`u EOT; if (\preg_match($regex, $stderr)) { $this->log(['CppCheck did not find any C or C++ source files', \Yasca\Logs\Level::ERROR]); return Async::fromResult(new \EmptyIterator()); } $dom = new \DOMDocument(); try { $success = $dom->loadXML($stderr); } catch (\ErrorException $e) { $success = false; } if ($success !== true) { if ($stderr === '') { $this->log(['CppCheck did not return any data', \Yasca\Logs\Level::ERROR]); } else { $this->log(['CppCheck did not return valid XML', \Yasca\Logs\Level::ERROR]); $this->log(["CppCheck returned {$stderr}", \Yasca\Logs\Level::ERROR]); } return Async::fromResult(new \EmptyIterator()); } return (new \Yasca\Core\IteratorBuilder())->from($dom->getElementsByTagName('error'))->select(static function ($errorNode) { return (new \Yasca\Result())->setOptions(['pluginName' => 'CppCheck', 'category' => "{$errorNode->getAttribute('id')}", 'lineNumber' => "{$errorNode->getAttribute('line')}", 'filename' => "{$errorNode->getAttribute('file')}", 'message' => "{$errorNode->getAttribute('msg')}", 'description' => "{$errorNode->getAttribute('msg')}", 'references' => ['http://sourceforge.net/projects/cppcheck/' => 'CppCheck Home Page'], 'severity' => (new \Yasca\Core\FunctionPipe())->wrap($errorNode->getAttribute('severity'))->pipe(static function ($cppcheckSeverity) { //http://cppcheck.sourceforge.net/devinfo/doxyoutput/classSeverity.html if ($cppcheckSeverity === 'error') { return 2; } elseif ($cppcheckSeverity === 'warning') { return 3; } elseif ($cppcheckSeverity === 'portability' || $cppcheckSeverity === 'performance' || $cppcheckSeverity === 'style' || $cppcheckSeverity === 'portability') { return 4; } else { //$cppcheckSeverity === 'debug' //$cppcheckSeverity === 'information' //$cppcheckSeverity === 'none' return 5; } })->unwrap()]); })->where(static function ($result) { $category = $result->category; if ($category === 'toomanyconfigs' || $category === 'syntaxError' || $category === 'cppcheckError') { return false; } else { return true; } })->toFunctionPipe()->pipe([Async::_class, 'fromResult']); }); }