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 | class SpecialChangeContentModel extends FormSpecialPage { |
||
4 | |||
5 | public function __construct() { |
||
6 | parent::__construct( 'ChangeContentModel', 'editcontentmodel' ); |
||
7 | } |
||
8 | |||
9 | public function doesWrites() { |
||
10 | return true; |
||
11 | } |
||
12 | |||
13 | /** |
||
14 | * @var Title|null |
||
15 | */ |
||
16 | private $title; |
||
17 | |||
18 | /** |
||
19 | * @var Revision|bool|null |
||
20 | * |
||
21 | * A Revision object, false if no revision exists, null if not loaded yet |
||
22 | */ |
||
23 | private $oldRevision; |
||
24 | |||
25 | protected function setParameter( $par ) { |
||
26 | $par = $this->getRequest()->getVal( 'pagetitle', $par ); |
||
27 | $title = Title::newFromText( $par ); |
||
28 | if ( $title ) { |
||
29 | $this->title = $title; |
||
30 | $this->par = $title->getPrefixedText(); |
||
31 | } else { |
||
32 | $this->par = ''; |
||
33 | } |
||
34 | } |
||
35 | |||
36 | protected function getDisplayFormat() { |
||
37 | return 'ooui'; |
||
38 | } |
||
39 | |||
40 | protected function alterForm( HTMLForm $form ) { |
||
41 | if ( !$this->title ) { |
||
42 | $form->setMethod( 'GET' ); |
||
43 | } |
||
44 | |||
45 | $this->addHelpLink( 'Help:ChangeContentModel' ); |
||
46 | |||
47 | // T120576 |
||
48 | $form->setSubmitTextMsg( 'changecontentmodel-submit' ); |
||
49 | } |
||
50 | |||
51 | public function validateTitle( $title ) { |
||
52 | if ( !$title ) { |
||
53 | // No form input yet |
||
54 | return true; |
||
55 | } |
||
56 | |||
57 | // Already validated by HTMLForm, but if not, throw |
||
58 | // and exception instead of a fatal |
||
59 | $titleObj = Title::newFromTextThrow( $title ); |
||
60 | |||
61 | $this->oldRevision = Revision::newFromTitle( $titleObj ) ?: false; |
||
62 | |||
63 | if ( $this->oldRevision ) { |
||
64 | $oldContent = $this->oldRevision->getContent(); |
||
65 | if ( !$oldContent->getContentHandler()->supportsDirectEditing() ) { |
||
66 | return $this->msg( 'changecontentmodel-nodirectediting' ) |
||
67 | ->params( ContentHandler::getLocalizedName( $oldContent->getModel() ) ) |
||
68 | ->escaped(); |
||
69 | } |
||
70 | } |
||
71 | |||
72 | return true; |
||
73 | } |
||
74 | |||
75 | protected function getFormFields() { |
||
76 | $fields = [ |
||
77 | 'pagetitle' => [ |
||
78 | 'type' => 'title', |
||
79 | 'creatable' => true, |
||
80 | 'name' => 'pagetitle', |
||
81 | 'default' => $this->par, |
||
82 | 'label-message' => 'changecontentmodel-title-label', |
||
83 | 'validation-callback' => [ $this, 'validateTitle' ], |
||
84 | ], |
||
85 | ]; |
||
86 | if ( $this->title ) { |
||
87 | $options = $this->getOptionsForTitle( $this->title ); |
||
88 | if ( empty( $options ) ) { |
||
89 | throw new ErrorPageError( |
||
90 | 'changecontentmodel-emptymodels-title', |
||
91 | 'changecontentmodel-emptymodels-text', |
||
92 | $this->title->getPrefixedText() |
||
93 | ); |
||
94 | } |
||
95 | $fields['pagetitle']['readonly'] = true; |
||
96 | $fields += [ |
||
97 | 'model' => [ |
||
98 | 'type' => 'select', |
||
99 | 'name' => 'model', |
||
100 | 'options' => $options, |
||
101 | 'label-message' => 'changecontentmodel-model-label' |
||
102 | ], |
||
103 | 'reason' => [ |
||
104 | 'type' => 'text', |
||
105 | 'name' => 'reason', |
||
106 | 'validation-callback' => function( $reason ) { |
||
107 | $match = EditPage::matchSummarySpamRegex( $reason ); |
||
108 | if ( $match ) { |
||
0 ignored issues
–
show
|
|||
109 | return $this->msg( 'spamprotectionmatch', $match )->parse(); |
||
110 | } |
||
111 | |||
112 | return true; |
||
113 | }, |
||
114 | 'label-message' => 'changecontentmodel-reason-label', |
||
115 | ], |
||
116 | ]; |
||
117 | } |
||
118 | |||
119 | return $fields; |
||
120 | } |
||
121 | |||
122 | private function getOptionsForTitle( Title $title = null ) { |
||
123 | $models = ContentHandler::getContentModels(); |
||
124 | $options = []; |
||
125 | foreach ( $models as $model ) { |
||
126 | $handler = ContentHandler::getForModelID( $model ); |
||
127 | if ( !$handler->supportsDirectEditing() ) { |
||
128 | continue; |
||
129 | } |
||
130 | if ( $title ) { |
||
131 | if ( $title->getContentModel() === $model ) { |
||
132 | continue; |
||
133 | } |
||
134 | if ( !$handler->canBeUsedOn( $title ) ) { |
||
135 | continue; |
||
136 | } |
||
137 | } |
||
138 | $options[ContentHandler::getLocalizedName( $model )] = $model; |
||
139 | } |
||
140 | |||
141 | return $options; |
||
142 | } |
||
143 | |||
144 | public function onSubmit( array $data ) { |
||
145 | global $wgContLang; |
||
146 | |||
147 | if ( $data['pagetitle'] === '' ) { |
||
148 | // Initial form view of special page, pass |
||
149 | return false; |
||
150 | } |
||
151 | |||
152 | // At this point, it has to be a POST request. This is enforced by HTMLForm, |
||
153 | // but lets be safe verify that. |
||
154 | if ( !$this->getRequest()->wasPosted() ) { |
||
155 | throw new RuntimeException( "Form submission was not POSTed" ); |
||
156 | } |
||
157 | |||
158 | $this->title = Title::newFromText( $data['pagetitle'] ); |
||
159 | $titleWithNewContentModel = clone $this->title; |
||
160 | $titleWithNewContentModel->setContentModel( $data['model'] ); |
||
161 | $user = $this->getUser(); |
||
162 | // Check permissions and make sure the user has permission to: |
||
163 | $errors = wfMergeErrorArrays( |
||
164 | // edit the contentmodel of the page |
||
165 | $this->title->getUserPermissionsErrors( 'editcontentmodel', $user ), |
||
166 | // edit the page under the old content model |
||
167 | $this->title->getUserPermissionsErrors( 'edit', $user ), |
||
168 | // edit the contentmodel under the new content model |
||
169 | $titleWithNewContentModel->getUserPermissionsErrors( 'editcontentmodel', $user ), |
||
170 | // edit the page under the new content model |
||
171 | $titleWithNewContentModel->getUserPermissionsErrors( 'edit', $user ) |
||
172 | ); |
||
173 | if ( $errors ) { |
||
174 | $out = $this->getOutput(); |
||
175 | $wikitext = $out->formatPermissionsErrorMessage( $errors ); |
||
176 | // Hack to get our wikitext parsed |
||
177 | return Status::newFatal( new RawMessage( '$1', [ $wikitext ] ) ); |
||
178 | } |
||
179 | |||
180 | $page = WikiPage::factory( $this->title ); |
||
0 ignored issues
–
show
It seems like
$this->title can be null ; however, factory() 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);
}
}
![]() |
|||
181 | if ( $this->oldRevision === null ) { |
||
182 | $this->oldRevision = $page->getRevision() ?: false; |
||
183 | } |
||
184 | $oldModel = $this->title->getContentModel(); |
||
185 | if ( $this->oldRevision ) { |
||
186 | $oldContent = $this->oldRevision->getContent(); |
||
187 | try { |
||
188 | $newContent = ContentHandler::makeContent( |
||
189 | $oldContent->getNativeData(), $this->title, $data['model'] |
||
190 | ); |
||
191 | } catch ( MWException $e ) { |
||
192 | return Status::newFatal( |
||
193 | $this->msg( 'changecontentmodel-cannot-convert' ) |
||
194 | ->params( |
||
195 | $this->title->getPrefixedText(), |
||
196 | ContentHandler::getLocalizedName( $data['model'] ) |
||
197 | ) |
||
198 | ); |
||
199 | } |
||
200 | } else { |
||
201 | // Page doesn't exist, create an empty content object |
||
202 | $newContent = ContentHandler::getForModelID( $data['model'] )->makeEmptyContent(); |
||
203 | } |
||
204 | |||
205 | // All other checks have passed, let's check rate limits |
||
206 | if ( $user->pingLimiter( 'editcontentmodel' ) ) { |
||
207 | throw new ThrottledError(); |
||
208 | } |
||
209 | |||
210 | $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW; |
||
211 | $flags |= EDIT_INTERNAL; |
||
212 | if ( $user->isAllowed( 'bot' ) ) { |
||
213 | $flags |= EDIT_FORCE_BOT; |
||
214 | } |
||
215 | |||
216 | $log = new ManualLogEntry( 'contentmodel', $this->oldRevision ? 'change' : 'new' ); |
||
217 | $log->setPerformer( $user ); |
||
218 | $log->setTarget( $this->title ); |
||
0 ignored issues
–
show
It seems like
$this->title can be null ; however, setTarget() 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);
}
}
![]() |
|||
219 | $log->setComment( $data['reason'] ); |
||
220 | $log->setParameters( [ |
||
221 | '4::oldmodel' => $oldModel, |
||
222 | '5::newmodel' => $data['model'] |
||
223 | ] ); |
||
224 | |||
225 | $formatter = LogFormatter::newFromEntry( $log ); |
||
226 | $formatter->setContext( RequestContext::newExtraneousContext( $this->title ) ); |
||
0 ignored issues
–
show
It seems like
$this->title can be null ; however, newExtraneousContext() 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);
}
}
![]() |
|||
227 | $reason = $formatter->getPlainActionText(); |
||
228 | if ( $data['reason'] !== '' ) { |
||
229 | $reason .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $data['reason']; |
||
230 | } |
||
231 | # Truncate for whole multibyte characters. |
||
232 | $reason = $wgContLang->truncate( $reason, 255 ); |
||
233 | |||
234 | // Run edit filters |
||
235 | $derivativeContext = new DerivativeContext( $this->getContext() ); |
||
236 | $derivativeContext->setTitle( $this->title ); |
||
0 ignored issues
–
show
It seems like
$this->title can be null ; however, setTitle() 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);
}
}
![]() |
|||
237 | $derivativeContext->setWikiPage( $page ); |
||
0 ignored issues
–
show
It seems like
$page defined by \WikiPage::factory($this->title) on line 180 can be null ; however, DerivativeContext::setWikiPage() 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);
}
}
![]() |
|||
238 | $status = new Status(); |
||
239 | if ( !Hooks::run( 'EditFilterMergedContent', |
||
240 | [ $derivativeContext, $newContent, $status, $reason, |
||
241 | $user, false ] ) |
||
242 | ) { |
||
243 | if ( $status->isGood() ) { |
||
244 | // TODO: extensions should really specify an error message |
||
245 | $status->fatal( 'hookaborted' ); |
||
246 | } |
||
247 | return $status; |
||
248 | } |
||
249 | |||
250 | $status = $page->doEditContent( |
||
251 | $newContent, |
||
252 | $reason, |
||
253 | $flags, |
||
254 | $this->oldRevision ? $this->oldRevision->getId() : false, |
||
255 | $user |
||
256 | ); |
||
257 | if ( !$status->isOK() ) { |
||
258 | return $status; |
||
259 | } |
||
260 | |||
261 | $logid = $log->insert(); |
||
262 | $log->publish( $logid ); |
||
263 | |||
264 | return $status; |
||
265 | } |
||
266 | |||
267 | public function onSuccess() { |
||
268 | $out = $this->getOutput(); |
||
269 | $out->setPageTitle( $this->msg( 'changecontentmodel-success-title' ) ); |
||
270 | $out->addWikiMsg( 'changecontentmodel-success-text', $this->title ); |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Return an array of subpages beginning with $search that this special page will accept. |
||
275 | * |
||
276 | * @param string $search Prefix to search for |
||
277 | * @param int $limit Maximum number of results to return (usually 10) |
||
278 | * @param int $offset Number of results to skip (usually 0) |
||
279 | * @return string[] Matching subpages |
||
280 | */ |
||
281 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
||
282 | return $this->prefixSearchString( $search, $limit, $offset ); |
||
283 | } |
||
284 | |||
285 | protected function getGroupName() { |
||
286 | return 'pagetools'; |
||
287 | } |
||
288 | } |
||
289 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: