Skip to content

Commit 30cfd8f

Browse files
Merge pull request #29 from matomo-org/harden-url-check
Adds code to harden URL check, disallow ips
2 parents 0f3c17f + 90cab4d commit 30cfd8f

6 files changed

Lines changed: 87 additions & 3 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.codex

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Changelog
22

3+
5.0.6 - 2025-04-27
4+
- Added code to harden the URL check for Microsoft Teams
5+
36
5.0.5 - 2026-03-30
47
- Fixed exception when module, action and idsite is not defined when setting js variables
58

MicrosoftTeams.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public function validateReportParameters(&$parameters, $reportType)
175175
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookRequiredErrorMessage'));
176176
} elseif (!UrlHelper::isLookLikeUrl($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER])) {
177177
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookInvalidErrorMessage'));
178-
} elseif (filter_var(parse_url($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER], PHP_URL_HOST), FILTER_VALIDATE_IP)) {
178+
} elseif ($this->isIpHost(parse_url($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER], PHP_URL_HOST))) {
179179
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookInvalidErrorMessage'));
180180
}
181181

@@ -410,14 +410,25 @@ public function validateCustomAlertReportParameters($parameters, $alertMedium)
410410
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookRequiredErrorMessage'));
411411
} elseif (!UrlHelper::isLookLikeUrl($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER])) {
412412
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookInvalidErrorMessage'));
413-
} elseif (filter_var(parse_url($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER], PHP_URL_HOST), FILTER_VALIDATE_IP)) {
413+
} elseif ($this->isIpHost(parse_url($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER], PHP_URL_HOST))) {
414414
throw new \Exception(Piwik::translate('MicrosoftTeams_IncomingWebhookInvalidErrorMessage'));
415415
}
416416

417417
$parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER] = htmlspecialchars_decode($parameters[self::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER]);
418418
}
419419
}
420420

421+
private function isIpHost(?string $host): bool
422+
{
423+
if (empty($host)) {
424+
return false;
425+
}
426+
427+
$host = trim($host, '[]');
428+
429+
return filter_var($host, FILTER_VALIDATE_IP) !== false || ctype_digit($host);
430+
}
431+
421432
/**
422433
*
423434
* Code to send CustomAlerts via MicrosoftTeams

plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "MicrosoftTeams",
33
"description": "Send Matomo reports and alerts to Microsoft Team channels, keeping your team informed and ready to act in real time.",
4-
"version": "5.0.5",
4+
"version": "5.0.6",
55
"theme": false,
66
"require": {
77
"matomo": ">=5.7.0-alpha,<6.0.0-b1"

tests/Integration/CustomAlertsApiTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public function testAddAlertShouldThrowExceptionIfInvalidMicrosoftTeamsWebhookUr
5757
$this->addAlert('webhookURL');
5858
}
5959

60+
public function testAddAlertShouldThrowExceptionIfMicrosoftTeamsWebhookUrlUsesIntegerLoopbackAlias()
61+
{
62+
$this->expectException(\Exception::class);
63+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
64+
$this->addAlert('http://2130706433:18081/hook');
65+
}
66+
6067
public function testAddAlertSuccess()
6168
{
6269
$id = $this->addAlert($msTeamsWebhookUrl = 'https://webhook.microsft.com/webhook');
@@ -82,6 +89,14 @@ public function testUpdateAlertShouldThrowExceptionIfInvalidMicrosoftTeamsWebhoo
8289
$this->updateAlert($idAlert, 'webhookURL');
8390
}
8491

92+
public function testUpdateAlertShouldThrowExceptionIfMicrosoftTeamsWebhookUrlUsesIntegerLoopbackAlias()
93+
{
94+
$this->expectException(\Exception::class);
95+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
96+
$idAlert = $this->addAlert('https://webhook.microsft.com/webhook');
97+
$this->updateAlert($idAlert, 'http://2130706433:18081/hook');
98+
}
99+
85100
public function testUpdateAlertSuccess()
86101
{
87102
$idAlert = $this->addAlert($msTeamsWebhookUrl = 'https://webhook.microsft.com/webhook');

tests/Integration/MicrosoftTeamsTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,30 @@ public function testValidateReportParametersShouldThrowMicrosoftTeamsWebhookUrlE
164164
Piwik::postEvent('ScheduledReports.validateReportParameters', [&$parameters, 'teams']);
165165
}
166166

167+
public function testValidateReportParametersShouldThrowMicrosoftTeamsWebhookUrlExceptionForIntegerLoopbackAlias()
168+
{
169+
$this->setRequiredFields();
170+
$this->expectException(\Exception::class);
171+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
172+
$parameters = [ScheduledReports::DISPLAY_FORMAT_PARAMETER => ScheduledReports::DISPLAY_FORMAT_GRAPHS_ONLY, MicrosoftTeams::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER => 'http://2130706433:18081/hook'];
173+
Piwik::postEvent('ScheduledReports.validateReportParameters', [&$parameters, 'teams']);
174+
}
175+
176+
/**
177+
* @dataProvider provideWebhookUrlsWithIpHosts
178+
*/
179+
public function testValidateReportParametersShouldThrowMicrosoftTeamsWebhookUrlExceptionForIpHosts($webhookUrl)
180+
{
181+
$this->setRequiredFields();
182+
$this->expectException(\Exception::class);
183+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
184+
$parameters = [
185+
ScheduledReports::DISPLAY_FORMAT_PARAMETER => ScheduledReports::DISPLAY_FORMAT_GRAPHS_ONLY,
186+
MicrosoftTeams::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER => $webhookUrl,
187+
];
188+
Piwik::postEvent('ScheduledReports.validateReportParameters', [&$parameters, 'teams']);
189+
}
190+
167191
public function testValidateCustomAlertReportParametersShouldThrowMicrosoftTeamsWebhookUrInvalidException()
168192
{
169193
$this->setRequiredFields();
@@ -200,6 +224,27 @@ public function testValidateCustomAlertReportParametersShouldThrowMicrosoftTeams
200224
Piwik::postEvent('CustomAlerts.validateReportParameters', [$parameters, 'teams']);
201225
}
202226

227+
public function testValidateCustomAlertReportParametersShouldThrowMicrosoftTeamsWebhookUrlExceptionForIntegerLoopbackAlias()
228+
{
229+
$this->setRequiredFields();
230+
$this->expectException(\Exception::class);
231+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
232+
$parameters = [MicrosoftTeams::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER => 'http://2130706433:18081/hook'];
233+
Piwik::postEvent('CustomAlerts.validateReportParameters', [$parameters, 'teams']);
234+
}
235+
236+
/**
237+
* @dataProvider provideWebhookUrlsWithIpHosts
238+
*/
239+
public function testValidateCustomAlertReportParametersShouldThrowMicrosoftTeamsWebhookUrlExceptionForIpHosts($webhookUrl)
240+
{
241+
$this->setRequiredFields();
242+
$this->expectException(\Exception::class);
243+
$this->expectExceptionMessage('MicrosoftTeams_IncomingWebhookInvalidErrorMessage');
244+
$parameters = [MicrosoftTeams::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER => $webhookUrl];
245+
Piwik::postEvent('CustomAlerts.validateReportParameters', [$parameters, 'teams']);
246+
}
247+
203248
public function testValidateReportParametersMicrosoftTeamsShouldNotThrowAnyException()
204249
{
205250
$this->setRequiredFields();
@@ -224,6 +269,15 @@ public function testValidateReportParametersMicrosoftTeamsShouldNotThrowAnyExcep
224269
$this->assertNotEmpty($parameters);
225270
}
226271

272+
public function provideWebhookUrlsWithIpHosts()
273+
{
274+
return [
275+
'ipv4' => ['https://8.8.8.8/hook'],
276+
'ipv4' => ['https://10.6.4.2'],
277+
'ipv6' => ['https://[2001:4860:4860::8888]/hook'],
278+
];
279+
}
280+
227281
private function assertHasReport($login, $idSite)
228282
{
229283
$report = $this->getReport($login, $idSite);

0 commit comments

Comments
 (0)