File size: 6,562 Bytes
7929f62 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | <?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Diagnostics\Diagnostic;
use Piwik\Common;
use Piwik\Config;
use Piwik\Http;
use Piwik\Piwik;
use Piwik\SettingsPiwik;
use Piwik\Translation\Translator;
abstract class AbstractPrivateDirectories implements Diagnostic
{
protected $privatePaths = [];
protected $accessiblePaths = []; // used like a set, but hashtable used underneath anyway, so map simpler php way
protected $labelKey = 'Diagnostics_RequiredPrivateDirectories';
/**
* @var Translator
*/
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function execute()
{
if (!SettingsPiwik::isMatomoInstalled()) {
return [];
}
$label = $this->translator->translate($this->labelKey);
$baseUrl = SettingsPiwik::getPiwikUrl();
if (!Common::stringEndsWith($baseUrl, '/')) {
$baseUrl .= '/';
}
$manualCheck = $this->translator->translate('Diagnostics_PrivateDirectoryManualCheck');
$testUrls = [];
foreach ($this->privatePaths as $path) {
if (!file_exists($path)) {
continue;
}
$testUrls[$path] = $baseUrl . $path;
}
$isInternetEnabled = SettingsPiwik::isInternetEnabled();
if (!$isInternetEnabled) {
$testUrlsList = $this->getUrlList($testUrls);
$unknown = $this->translator->translate('Diagnostics_PrivateDirectoryInternetDisabled') . ' ' . $manualCheck
. $testUrlsList;
$results[] = DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $unknown);
return $results;
}
$result = new DiagnosticResult($label);
if (Config::getInstance()->General['enable_required_directories_diagnostic'] == 0) {
$result->addItem(
new DiagnosticResultItem(
DiagnosticResult::STATUS_WARNING,
$this->translator->translate('Diagnostics_EnableRequiredDirectoriesDiagnostic')
)
);
return [$result];
}
$atLeastOneIsAccessible = $this->computeAccessiblePaths($result, $baseUrl, $testUrls);
if ($atLeastOneIsAccessible) {
$this->addError($result);
} else {
$result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_OK, $this->translator->translate('Diagnostics_AllPrivateDirectoriesAreInaccessible')));
}
return [$result];
}
private function getUrlList(array $testUrls)
{
$testUrlsList = '';
foreach ($testUrls as $testUrl) {
$testUrlsList .= '<br/>' . Common::sanitizeInputValue($testUrl);
}
return $testUrlsList;
}
protected function isAccessible(DiagnosticResult $result, $testUrl, $publicIfResponseEquals, $publicIfResponseContains)
{
try {
$response = Http::sendHttpRequest($testUrl, $timeout = 2, null, null, null, false, false, true);
$status = $response['status'];
if ($status >= 400 && $status < 500) {
return false;
} elseif ($status >= 300 && $status < 400) {
// follow the redirect
$response = Http::sendHttpRequest($testUrl, $timeout = 5, null, null, 5, false, false, true);
$isResolvedRedirectProtected = $response['status'] >= 400 && $response['status'] < 500;
if ($isResolvedRedirectProtected) {
// eg someone redirect from http to https or the other way around
return false;
}
// we check for content if possible as they may redirect these files eg to /home or something else
if (!$publicIfResponseContains || !$publicIfResponseEquals) {
// it may or may not be an issue depending where they redirect to
// TODO ideally we make this more clear maybe?
$result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_WARNING, $testUrl));
return true;
}
if (trim($response['data']) === $publicIfResponseEquals) {
// we assume it is publicly accessible because either the exact expected content is returned or because we don't check for content match
$result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_ERROR, $testUrl));
return true;
} elseif (strpos($response['data'], $publicIfResponseContains) !== false) {
// we assume it is publicly accessible because a unique content is included in the response or because we don't check for content contains
$result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_ERROR, $testUrl));
return true;
}
// in other cases we assume it's not publicly accessible because we didn't get any expected output in the response
// so it seems like they redirect eg to the homepage or another page
} else {
// we assume the file is accessible publicly
$result->addItem(new DiagnosticResultItem(DiagnosticResult::STATUS_ERROR, $testUrl));
return true;
}
} catch (\Exception $e) {
$error = $e->getMessage();
$result->addItem(
new DiagnosticResultItem(
DiagnosticResult::STATUS_WARNING,
Piwik::translate(
'Diagnostics_PrivateDirectoryCantCheckUrl',
[Common::sanitizeInputValue($testUrl), Common::sanitizeInputValue($error)]
)
)
);
}
return false;
}
protected function computeAccessiblePaths(DiagnosticResult &$result, $baseUrl, array $testUrls): bool
{
$atLeastOneIsAccessible = false;
foreach ($testUrls as $path => $testUrl) {
if ($this->isAccessible($result, $testUrl, '', '')) {
$atLeastOneIsAccessible = true;
}
}
return $atLeastOneIsAccessible;
}
abstract protected function addError(DiagnosticResult &$result);
}
|