This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Wikibase\Client\Hooks; |
||
4 | |||
5 | use Diff\DiffOp\DiffOp; |
||
6 | use Diff\DiffOp\DiffOpAdd; |
||
7 | use Diff\DiffOp\DiffOpChange; |
||
8 | use EchoEvent; |
||
9 | use Title; |
||
10 | use User; |
||
11 | use Wikibase\Client\NamespaceChecker; |
||
12 | use Wikibase\Client\RepoLinker; |
||
13 | use Wikibase\Client\WikibaseClient; |
||
14 | use Wikibase\Lib\Changes\Change; |
||
15 | use Wikibase\Lib\Changes\ItemChange; |
||
16 | use WikiPage; |
||
17 | |||
18 | /** |
||
19 | * Handlers for client Echo notifications |
||
20 | * |
||
21 | * @license GPL-2.0-or-later |
||
22 | * @author Matěj Suchánek |
||
23 | */ |
||
24 | class EchoNotificationsHandlers { |
||
25 | |||
26 | /** |
||
27 | * Type of notification |
||
28 | */ |
||
29 | const NOTIFICATION_TYPE = 'page-connection'; |
||
30 | |||
31 | /** |
||
32 | * @var RepoLinker |
||
33 | */ |
||
34 | private $repoLinker; |
||
35 | |||
36 | /** |
||
37 | * @var NamespaceChecker |
||
38 | */ |
||
39 | private $namespaceChecker; |
||
40 | |||
41 | /** |
||
42 | * @var string |
||
43 | */ |
||
44 | private $siteId; |
||
45 | |||
46 | /** |
||
47 | * @var bool |
||
48 | */ |
||
49 | private $sendEchoNotification; |
||
50 | |||
51 | /** |
||
52 | * @var string |
||
53 | */ |
||
54 | private $repoSiteName; |
||
55 | |||
56 | /** |
||
57 | * @param RepoLinker $repoLinker |
||
58 | * @param NamespaceChecker $namespaceChecker |
||
59 | * @param string $siteId |
||
60 | * @param bool $sendEchoNotification |
||
61 | * @param string $repoSiteName |
||
62 | */ |
||
63 | public function __construct( |
||
64 | RepoLinker $repoLinker, |
||
65 | NamespaceChecker $namespaceChecker, |
||
66 | $siteId, |
||
67 | $sendEchoNotification, |
||
68 | $repoSiteName |
||
69 | ) { |
||
70 | $this->repoLinker = $repoLinker; |
||
71 | $this->namespaceChecker = $namespaceChecker; |
||
72 | $this->siteId = $siteId; |
||
73 | $this->sendEchoNotification = $sendEchoNotification; |
||
74 | $this->repoSiteName = $repoSiteName; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * @return self |
||
79 | */ |
||
80 | public static function factory() { |
||
81 | $wikibaseClient = WikibaseClient::getDefaultInstance(); |
||
82 | $settings = $wikibaseClient->getSettings(); |
||
83 | |||
84 | return new self( |
||
85 | $wikibaseClient->newRepoLinker(), |
||
86 | $wikibaseClient->getNamespaceChecker(), |
||
87 | $settings->getSetting( 'siteGlobalID' ), |
||
88 | $settings->getSetting( 'sendEchoNotification' ), |
||
89 | $settings->getSetting( 'repoSiteName' ) |
||
90 | ); |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Handler for EchoGetBundleRules hook |
||
95 | * @see https://www.mediawiki.org/wiki/Notifications/Developer_guide#Bundled_notifications |
||
96 | * |
||
97 | * @param EchoEvent $event |
||
98 | * @param string &$bundleString |
||
99 | */ |
||
100 | public static function onEchoGetBundleRules( EchoEvent $event, &$bundleString ) { |
||
101 | if ( $event->getType() === self::NOTIFICATION_TYPE ) { |
||
102 | $bundleString = self::NOTIFICATION_TYPE; |
||
103 | } |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Handler for LocalUserCreated hook. |
||
108 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/LocalUserCreated |
||
109 | * @param User $user User object that was created. |
||
110 | * @param bool $autocreated True when account was auto-created |
||
111 | */ |
||
112 | public static function onLocalUserCreated( User $user, $autocreated ) { |
||
113 | $self = self::factory(); |
||
114 | $self->doLocalUserCreated( $user, $autocreated ); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * @param User $user |
||
119 | * @param bool $autocreated |
||
120 | */ |
||
121 | public function doLocalUserCreated( User $user, $autocreated ) { |
||
0 ignored issues
–
show
|
|||
122 | if ( $this->sendEchoNotification === true ) { |
||
123 | $user->setOption( 'echo-subscriptions-web-wikibase-action', true ); |
||
124 | $user->saveSettings(); |
||
125 | } |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * Handler for WikibaseHandleChange hook |
||
130 | * @see doWikibaseHandleChange |
||
131 | * |
||
132 | * @param Change $change |
||
133 | */ |
||
134 | public static function onWikibaseHandleChange( Change $change ) { |
||
135 | $self = self::factory(); |
||
136 | $self->doWikibaseHandleChange( $change ); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * @param Change $change |
||
141 | * |
||
142 | * @return bool |
||
143 | */ |
||
144 | public function doWikibaseHandleChange( Change $change ) { |
||
145 | if ( $this->sendEchoNotification !== true ) { |
||
146 | return false; |
||
147 | } |
||
148 | |||
149 | if ( !( $change instanceof ItemChange ) ) { |
||
150 | return false; |
||
151 | } |
||
152 | |||
153 | $siteLinkDiff = $change->getSiteLinkDiff(); |
||
154 | if ( $siteLinkDiff->isEmpty() ) { |
||
155 | return false; |
||
156 | } |
||
157 | |||
158 | $siteId = $this->siteId; |
||
159 | if ( !isset( $siteLinkDiff[$siteId] ) || !isset( $siteLinkDiff[$siteId]['name'] ) ) { |
||
160 | return false; |
||
161 | } |
||
162 | |||
163 | $siteLinkDiffOp = $siteLinkDiff[$siteId]['name']; |
||
164 | |||
165 | $title = $this->getTitleForNotification( $siteLinkDiffOp ); |
||
166 | if ( $title !== false ) { |
||
167 | $metadata = $change->getMetadata(); |
||
168 | $entityId = $change->getEntityId(); |
||
169 | $agent = User::newFromName( $metadata['user_text'], false ); |
||
170 | EchoEvent::create( [ |
||
171 | 'agent' => $agent, |
||
172 | 'extra' => [ |
||
173 | // maybe also a diff link? |
||
174 | 'url' => $this->repoLinker->getEntityUrl( $entityId ), |
||
0 ignored issues
–
show
It seems like
$entityId defined by $change->getEntityId() on line 168 can be null ; however, Wikibase\Client\RepoLinker::getEntityUrl() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
175 | 'repoSiteName' => $this->repoSiteName, |
||
176 | 'entity' => $entityId->getSerialization(), |
||
177 | ], |
||
178 | 'title' => $title, |
||
179 | 'type' => self::NOTIFICATION_TYPE |
||
180 | ] ); |
||
181 | |||
182 | return true; |
||
183 | } |
||
184 | |||
185 | return false; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Determines whether the change was a real sitelink addition |
||
190 | * and returns either title, or false |
||
191 | * |
||
192 | * @param DiffOp $siteLinkDiffOp |
||
193 | * |
||
194 | * @return Title|false |
||
195 | */ |
||
196 | private function getTitleForNotification( DiffOp $siteLinkDiffOp ) { |
||
197 | if ( $siteLinkDiffOp instanceof DiffOpAdd ) { |
||
198 | $new = $siteLinkDiffOp->getNewValue(); |
||
199 | $newTitle = Title::newFromText( $new ); |
||
200 | return $this->canNotifyForTitle( $newTitle ) ? $newTitle : false; |
||
201 | } |
||
202 | |||
203 | // if it's a sitelink change, make sure it wasn't triggered by a page move |
||
204 | if ( $siteLinkDiffOp instanceof DiffOpChange ) { |
||
205 | $new = $siteLinkDiffOp->getNewValue(); |
||
206 | $newTitle = Title::newFromText( $new ); |
||
207 | |||
208 | if ( !$this->canNotifyForTitle( $newTitle ) ) { |
||
209 | return false; |
||
210 | } |
||
211 | |||
212 | $old = $siteLinkDiffOp->getOldValue(); |
||
213 | $oldTitle = Title::newFromText( $old ); |
||
214 | |||
215 | // propably means that there was a page move |
||
216 | // without keeping the old title as redirect |
||
217 | if ( !$oldTitle->exists() ) { |
||
218 | return false; |
||
219 | } |
||
220 | |||
221 | // even if the old page is a redirect, make sure it redirects to the new title |
||
222 | if ( $oldTitle->isRedirect() ) { |
||
223 | $page = WikiPage::factory( $oldTitle ); |
||
224 | $targetTitle = $page->getRedirectTarget(); |
||
225 | if ( $targetTitle && $targetTitle->equals( $newTitle ) ) { |
||
226 | return false; |
||
227 | } |
||
228 | } |
||
229 | |||
230 | return $newTitle; |
||
231 | } |
||
232 | |||
233 | return false; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Whether it's reasonable to send a notification for the title |
||
238 | * |
||
239 | * @param Title $title |
||
240 | * |
||
241 | * @return bool |
||
242 | */ |
||
243 | private function canNotifyForTitle( Title $title ) { |
||
244 | return $title->exists() && !$title->isRedirect() |
||
245 | && $this->namespaceChecker->isWikibaseEnabled( $title->getNamespace() ); |
||
246 | } |
||
247 | |||
248 | } |
||
249 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.