Issues (1401)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

repo/includes/Api/SetClaim.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Wikibase\Repo\Api;
6
7
use ApiBase;
8
use ApiMain;
9
use ApiUsageException;
10
use DataValues\IllegalValueException;
11
use Deserializers\Deserializer;
12
use Diff\Comparer\ComparableComparer;
13
use Diff\Differ\OrderedListDiffer;
14
use IBufferingStatsdDataFactory;
15
use InvalidArgumentException;
16
use LogicException;
17
use OutOfBoundsException;
18
use Wikibase\DataModel\Entity\StatementListProvidingEntity;
19
use Wikibase\DataModel\Services\Statement\StatementGuidParser;
20
use Wikibase\DataModel\Services\Statement\StatementGuidParsingException;
21
use Wikibase\DataModel\Statement\Statement;
22
use Wikibase\DataModel\Statement\StatementList;
23
use Wikibase\Lib\Summary;
24
use Wikibase\Repo\ChangeOp\StatementChangeOpFactory;
25
use Wikibase\Repo\ClaimSummaryBuilder;
26
use Wikibase\Repo\Diff\ClaimDiffer;
27
use Wikibase\Repo\FederatedProperties\FederatedPropertiesException;
28
use Wikibase\Repo\WikibaseRepo;
29
30
/**
31
 * API module for creating or updating an entire Claim.
32
 *
33
 * @license GPL-2.0-or-later
34
 * @author Jeroen De Dauw < [email protected] >
35
 * @author Tobias Gritschacher < [email protected] >
36
 * @author Addshore
37
 */
38
class SetClaim extends ApiBase {
39
40
	use FederatedPropertyApiValidatorTrait;
41
42
	/**
43
	 * @var StatementChangeOpFactory
44
	 */
45
	private $statementChangeOpFactory;
46
47
	/**
48
	 * @var ApiErrorReporter
49
	 */
50
	protected $errorReporter;
51
52
	/**
53
	 * @var Deserializer
54
	 */
55
	private $statementDeserializer;
56
57
	/**
58
	 * @var StatementModificationHelper
59
	 */
60
	private $modificationHelper;
61
62
	/**
63
	 * @var StatementGuidParser
64
	 */
65
	private $guidParser;
66
67
	/**
68
	 * @var ResultBuilder
69
	 */
70
	private $resultBuilder;
71
72
	/**
73
	 * @var EntitySavingHelper
74
	 */
75
	private $entitySavingHelper;
76
77
	/** @var IBufferingStatsdDataFactory */
78
	private $stats;
79
80
	public function __construct(
81
		ApiMain $mainModule,
82
		string $moduleName,
83
		ApiErrorReporter $errorReporter,
84
		Deserializer $statementDeserializer,
85
		StatementChangeOpFactory $statementChangeOpFactory,
86
		StatementModificationHelper $modificationHelper,
87
		StatementGuidParser $guidParser,
88
		callable $resultBuilderInstantiator,
89
		callable $entitySavingHelperInstantiator,
90
		IBufferingStatsdDataFactory $stats,
91
		bool $federatedPropertiesEnabled
92
	) {
93
		parent::__construct( $mainModule, $moduleName );
94
95
		$this->errorReporter = $errorReporter;
96
		$this->statementDeserializer = $statementDeserializer;
97
		$this->statementChangeOpFactory = $statementChangeOpFactory;
98
		$this->modificationHelper = $modificationHelper;
99
		$this->guidParser = $guidParser;
100
		$this->resultBuilder = $resultBuilderInstantiator( $this );
101
		$this->entitySavingHelper = $entitySavingHelperInstantiator( $this );
102
		$this->stats = $stats;
103
		$this->federatedPropertiesEnabled = $federatedPropertiesEnabled;
104
	}
105
106
	public static function factory( ApiMain $mainModule, string $moduleName, IBufferingStatsdDataFactory $stats ): self {
107
		$wikibaseRepo = WikibaseRepo::getDefaultInstance();
108
		$apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $mainModule->getContext() );
109
		$changeOpFactoryProvider = $wikibaseRepo->getChangeOpFactoryProvider();
110
111
		$modificationHelper = new StatementModificationHelper(
112
			$wikibaseRepo->getSnakFactory(),
113
			$wikibaseRepo->getEntityIdParser(),
114
			$wikibaseRepo->getStatementGuidValidator(),
115
			$apiHelperFactory->getErrorReporter( $mainModule )
116
		);
117
118
		return new self(
119
			$mainModule,
120
			$moduleName,
121
			$apiHelperFactory->getErrorReporter( $mainModule ),
122
			$wikibaseRepo->getExternalFormatStatementDeserializer(),
123
			$changeOpFactoryProvider->getStatementChangeOpFactory(),
124
			$modificationHelper,
125
			$wikibaseRepo->getStatementGuidParser(),
126
			function ( $module ) use ( $apiHelperFactory ) {
127
				return $apiHelperFactory->getResultBuilder( $module );
128
			},
129
			function ( $module ) use ( $apiHelperFactory ) {
130
				return $apiHelperFactory->getEntitySavingHelper( $module );
131
			},
132
			$stats,
133
			$wikibaseRepo->inFederatedPropertyMode()
134
		);
135
	}
136
137
	/**
138
	 * @inheritDoc
139
	 */
140
	public function execute(): void {
141
		try {
142
			$this->executeInternal();
143
		} catch ( FederatedPropertiesException $ex ) {
144
			$this->errorReporter->dieWithError(
145
				'wikibase-federated-properties-save-api-error-message',
146
				'failed-save'
147
			);
148
		}
149
	}
150
151
	private function executeInternal(): void {
152
		$params = $this->extractRequestParams();
153
		$statement = $this->getStatementFromParams( $params );
154
		$guid = $statement->getGuid();
155
156
		if ( $guid === null ) {
157
			$this->errorReporter->dieError( 'GUID must be set when setting a claim', 'invalid-claim' );
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
158
		}
159
160
		try {
161
			$statementGuid = $this->guidParser->parse( $guid );
162
		} catch ( StatementGuidParsingException $ex ) {
163
			$this->errorReporter->dieException( $ex, 'invalid-claim' );
164
			throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' );
165
		}
166
167
		$entityId = $statementGuid->getEntityId();
168
		$this->validateAlteringEntityById( $entityId );
169
		$entity = $this->entitySavingHelper->loadEntity( $entityId );
170
171
		if ( !( $entity instanceof StatementListProvidingEntity ) ) {
172
			$this->errorReporter->dieError( 'The given entity cannot contain statements', 'not-supported' );
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
173
			throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' );
174
		}
175
176
		if ( $params['ignoreduplicatemainsnak'] ) {
177
			if ( $this->statementMainSnakAlreadyExists( $statement, $entity->getStatements() ) ) {
178
				$this->addWarning( 'wikibase-setclaim-warning-duplicatemainsnak' );
179
				return;
180
			}
181
		}
182
		$summary = $this->getSummary( $params, $statement, $entity->getStatements() );
183
184
		$index = $params['index'] ?? null;
185
		$changeop = $this->statementChangeOpFactory->newSetStatementOp( $statement, $index );
186
		$this->modificationHelper->applyChangeOp( $changeop, $entity, $summary );
187
188
		$status = $this->entitySavingHelper->attemptSaveEntity( $entity, $summary );
189
		$this->resultBuilder->addRevisionIdFromStatusToResult( $status, 'pageinfo' );
190
		$this->resultBuilder->markSuccess();
191
		$this->resultBuilder->addStatement( $statement );
192
193
		$this->stats->increment( 'wikibase.repo.api.wbsetclaim.total' );
194
		if ( $index !== null ) {
195
			$this->stats->increment( 'wikibase.repo.api.wbsetclaim.index' );
196
		}
197
	}
198
199
	private function statementMainSnakAlreadyExists(
200
		Statement $statement,
201
		StatementList $existingStatements
202
	) : bool {
203
		$propertyId = $statement->getPropertyId();
204
		$mainSnak = $statement->getMainSnak();
205
		foreach ( $existingStatements as $existingStatement ) {
206
			if ( $existingStatement->getPropertyId()->equals( $propertyId ) ) {
207
				if ( $existingStatement->getMainSnak()->equals( $mainSnak ) ) {
208
					return true;
209
				}
210
			}
211
		}
212
		return false;
213
	}
214
215
	/**
216
	 * @param array $params
217
	 * @param Statement $statement
218
	 * @param StatementList $statementList
219
	 *
220
	 * @throws InvalidArgumentException
221
	 * @return Summary
222
	 *
223
	 * @todo this summary builder is ugly and summary stuff needs to be refactored
224
	 */
225
	private function getSummary( array $params, Statement $statement, StatementList $statementList ): Summary {
226
		$claimSummaryBuilder = new ClaimSummaryBuilder(
227
			$this->getModuleName(),
228
			new ClaimDiffer( new OrderedListDiffer( new ComparableComparer() ) )
229
		);
230
231
		$summary = $claimSummaryBuilder->buildClaimSummary(
232
			$statementList->getFirstStatementWithGuid( $statement->getGuid() ),
233
			$statement
234
		);
235
236
		if ( isset( $params['summary'] ) ) {
237
			$summary->setUserSummary( $params['summary'] );
238
		}
239
240
		return $summary;
241
	}
242
243
	/**
244
	 * @param array $params
245
	 *
246
	 * @throws IllegalValueException
247
	 * @throws ApiUsageException
248
	 * @throws LogicException
249
	 * @return Statement
250
	 */
251
	private function getStatementFromParams( array $params ): Statement {
252
		try {
253
			$serializedStatement = json_decode( $params['claim'], true );
254
			if ( !is_array( $serializedStatement ) ) {
255
				throw new IllegalValueException( 'Failed to get statement from Serialization' );
256
			}
257
			$statement = $this->statementDeserializer->deserialize( $serializedStatement );
258
			if ( !( $statement instanceof Statement ) ) {
259
				throw new IllegalValueException( 'Failed to get statement from Serialization' );
260
			}
261
			return $statement;
262
		} catch ( InvalidArgumentException $invalidArgumentException ) {
263
			$this->errorReporter->dieError(
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
264
				'Failed to get claim from claim Serialization ' . $invalidArgumentException->getMessage(),
265
				'invalid-claim'
266
			);
267
		} catch ( OutOfBoundsException $outOfBoundsException ) {
268
			$this->errorReporter->dieError(
0 ignored issues
show
Deprecated Code introduced by
The method Wikibase\Repo\Api\ApiErrorReporter::dieError() has been deprecated with message: Use dieWithError() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
269
				'Failed to get claim from claim Serialization ' . $outOfBoundsException->getMessage(),
270
				'invalid-claim'
271
			);
272
		}
273
274
		// Note: since dieUsage() never returns, this should be unreachable!
275
		throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' );
276
	}
277
278
	/**
279
	 * @inheritDoc
280
	 */
281
	public function isWriteMode(): bool {
282
		return true;
283
	}
284
285
	/**
286
	 * @see ApiBase::needsToken
287
	 *
288
	 * @return string
289
	 */
290
	public function needsToken(): string {
291
		return 'csrf';
292
	}
293
294
	/**
295
	 * @inheritDoc
296
	 */
297
	protected function getAllowedParams(): array {
298
		return array_merge(
299
			[
300
				'claim' => [
301
					self::PARAM_TYPE => 'text',
302
					self::PARAM_REQUIRED => true,
303
				],
304
				'index' => [
305
					self::PARAM_TYPE => 'integer',
306
				],
307
				'summary' => [
308
					self::PARAM_TYPE => 'string',
309
				],
310
				'tags' => [
311
					self::PARAM_TYPE => 'tags',
312
					self::PARAM_ISMULTI => true,
313
				],
314
				'token' => null,
315
				'baserevid' => [
316
					self::PARAM_TYPE => 'integer',
317
				],
318
				'bot' => false,
319
				'ignoreduplicatemainsnak' => false,
320
			],
321
			parent::getAllowedParams()
322
		);
323
	}
324
325
	/**
326
	 * @inheritDoc
327
	 */
328
	protected function getExamplesMessages(): array {
329
		return [
330
			'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",'
331
				. '"type":"claim","mainsnak":{"snaktype":"value","property":"P1",'
332
				. '"datavalue":{"value":"City","type":"string"}}}'
333
				=> 'apihelp-wbsetclaim-example-1',
334
			'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",'
335
				. '"type":"claim","mainsnak":{"snaktype":"value","property":"P1",'
336
				. '"datavalue":{"value":"City","type":"string"}}}&index=0'
337
				=> 'apihelp-wbsetclaim-example-2',
338
			'action=wbsetclaim&claim={"id":"Q2$5627445f-43cb-ed6d-3adb-760e85bd17ee",'
339
				. '"type":"statement","mainsnak":{"snaktype":"value","property":"P1",'
340
				. '"datavalue":{"value":"City","type":"string"}},'
341
				. '"references":[{"snaks":{"P2":[{"snaktype":"value","property":"P2",'
342
				. '"datavalue":{"value":"The Economy of Cities","type":"string"}}]},'
343
				. '"snaks-order":["P2"]}],"rank":"normal"}'
344
				=> 'apihelp-wbsetclaim-example-3',
345
		];
346
	}
347
348
}
349