Issues (10)

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.

src/QueryBuilder.php (1 issue)

Severity

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
namespace Asparagus;
4
5
use InvalidArgumentException;
6
use RangeException;
7
use RuntimeException;
8
9
/**
10
 * Abstraction layer to build SPARQL queries
11
 *
12
 * Nested filters not supported
13
 * Supports SPARQL v1.0 (v1.1 to come)
14
 *
15
 * @since 0.1
16
 *
17
 * @license GNU GPL v2+
18
 * @author Bene* < [email protected] >
19
 */
20
class QueryBuilder {
21
22
	/**
23
	 * @var ExpressionValidator
24
	 */
25
	private $expressionValidator;
26
27
	/**
28
	 * @var UsageValidator
29
	 */
30
	private $usageValidator;
31
32
	/**
33
	 * @var QueryPrefixBuilder
34
	 */
35
	private $prefixBuilder;
36
37
	/**
38
	 * @var string[] list of expressions to select
39
	 */
40
	private $selects = array();
41
42
	/**
43
	 * @var string uniqueness constraint, one of DISTINCT, REDUCED or empty
44
	 */
45
	private $uniqueness = '';
46
47
	/**
48
	 * @var string form of the query, one of SELECT or DESCRIBE
49
	 */
50
	private $queryForm = null;
51
52
	/**
53
	 * @var GraphBuilder
54
	 */
55
	private $graphBuilder;
56
57
	/**
58
	 * @var QueryModifierBuilder
59
	 */
60
	private $modifierBuilder;
61
62
	/**
63
	 * @var string[] $prefixes
64
	 * @throws InvalidArgumentException
65
	 */
66
	public function __construct( array $prefixes = array() ) {
67
		$this->expressionValidator = new ExpressionValidator();
68
		$this->usageValidator = new UsageValidator();
69
		$this->prefixBuilder = new QueryPrefixBuilder( $prefixes, $this->usageValidator );
70
		$this->graphBuilder = new GraphBuilder( $this->usageValidator );
71
		$this->modifierBuilder = new QueryModifierBuilder( $this->usageValidator );
72
	}
73
74
	/**
75
	 * @since 0.3
76
	 *
77
	 * @return string[] list of expressions to select
78
	 */
79
	public function getSelects() {
80
		return $this->selects;
81
	}
82
83
	/**
84
	 * Specifies the expressions to select.
85
	 *
86
	 * @param string|string[] $expressions
87
	 * @return self
88
	 * @throws InvalidArgumentException
89
	 * @throws RuntimeException
90
	 */
91
	public function select( $expressions /* expressions ... */ ) {
92
		$expressions = is_array( $expressions ) ? $expressions : func_get_args();
93
94
		$this->setQueryForm( 'SELECT' );
95
		$this->addExpressions( $expressions );
96
97
		return $this;
98
	}
99
100
	/**
101
	 * Specifies the expressions to select. Duplicate results are eliminated.
102
	 *
103
	 * @since 0.3
104
	 *
105
	 * @param string|string[] $expressions
106
	 * @return self
107
	 * @throws InvalidArgumentException
108
	 * @throws RuntimeException
109
	 */
110
	public function selectDistinct( $expressions /* expressions ... */ ) {
111
		$expressions = is_array( $expressions ) ? $expressions : func_get_args();
112
113
		$this->setQueryForm( 'SELECT' );
114
		$this->uniqueness = 'DISTINCT ';
115
		$this->addExpressions( $expressions );
116
117
		return $this;
118
	}
119
120
	/**
121
	 * Specifies the expressions to select. Duplicate results may be eliminated.
122
	 *
123
	 * @since 0.3
124
	 *
125
	 * @param string|string[] $expressions
126
	 * @return self
127
	 * @throws InvalidArgumentException
128
	 * @throws RuntimeException
129
	 */
130
	public function selectReduced( $expressions /* expressions ... */ ) {
131
		$expressions = is_array( $expressions ) ? $expressions : func_get_args();
132
133
		$this->setQueryForm( 'SELECT' );
134
		$this->uniqueness = 'REDUCED ';
135
		$this->addExpressions( $expressions );
136
137
		return $this;
138
	}
139
140
	/**
141
	 * Specifies the expressions to describe.
142
	 *
143
	 * @since 0.4
144
	 *
145
	 * @param string|string[] $expressions
146
	 * @return self
147
	 * @throws InvalidArgumentException
148
	 * @throws RuntimeException
149
	 */
150
	public function describe( $expressions /* expressions ... */ ) {
151
		$expressions = is_array( $expressions ) ? $expressions : func_get_args();
152
153
		$this->setQueryForm( 'DESCRIBE' );
154
		$this->addExpressions(
155
			$expressions,
156
			ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_FUNCTION_AS
157
				| ExpressionValidator::VALIDATE_IRI | ExpressionValidator::VALIDATE_PREFIXED_IRI
158
		);
159
160
		return $this;
161
	}
162
163
	private function setQueryForm( $queryForm ) {
164
		if ( $this->queryForm !== null ) {
165
			throw new RuntimeException( 'Query type is already set to ' . $this->queryForm );
166
		}
167
168
		$this->queryForm = $queryForm;
169
	}
170
171
	private function addExpressions( array $expressions, $options = null ) {
172
		foreach ( $expressions as $expression ) {
173
			$this->expressionValidator->validate(
174
				$expression,
175
				$options ?: ExpressionValidator::VALIDATE_VARIABLE | ExpressionValidator::VALIDATE_FUNCTION_AS
176
			);
177
178
			// @todo temp hack to add AS definitions to defined variables
179
			$regexHelper = new RegexHelper();
180
			$matches = $regexHelper->getMatches( 'AS \{variable}', $expression );
181
			$this->usageValidator->trackDefinedVariables( $matches );
182
183
			// @todo detect functions and wrap with brackets automatically
184
			$this->usageValidator->trackUsedVariables( $expression );
185
			$this->usageValidator->trackUsedPrefixes( $expression );
186
			$this->selects[] = $expression;
187
		}
188
	}
189
190
	/**
191
	 * Adds the given triple as a condition.
192
	 *
193
	 * @param string $subject
194
	 * @param string $predicate
195
	 * @param string $object
196
	 * @return self
197
	 * @throws InvalidArgumentException
198
	 */
199
	public function where( $subject, $predicate, $object ) {
200
		$this->graphBuilder->where( $subject, $predicate, $object );
201
		return $this;
202
	}
203
204
	/**
205
	 * Adds the given triple/double/single value as an additional condition
206
	 * to the previously added condition.
207
	 *
208
	 * @param string $subject
209
	 * @param string|null $predicate
210
	 * @param string|null $object
211
	 * @return self
212
	 * @throws InvalidArgumentException
213
	 */
214
	public function also( $subject, $predicate = null, $object = null ) {
215
		$this->graphBuilder->also( $subject, $predicate, $object );
216
		return $this;
217
	}
218
219
	/**
220
	 * Adds the given graph or triple as an optional condition.
221
	 *
222
	 * @since 0.3
223
	 *
224
	 * @param string|GraphBuilder $subject
225
	 * @param string|null $predicate
226
	 * @param string|null $object
227
	 * @return self
228
	 * @throws InvalidArgumentException
229
	 */
230
	public function optional( $subject, $predicate = null, $object = null ) {
231
		$this->graphBuilder->optional( $subject, $predicate, $object );
232
		return $this;
233
	}
234
235
	/**
236
	 * Adds the given expression as a filter to this query.
237
	 *
238
	 * @since 0.3
239
	 *
240
	 * @param string $expression
241
	 * @return self
242
	 * @throws InvalidArgumentException
243
	 */
244
	public function filter( $expression ) {
245
		$this->graphBuilder->filter( $expression );
246
		return $this;
247
	}
248
249
	/**
250
	 * Adds a filter that the given graph or triple exists.
251
	 *
252
	 * @since 0.3
253
	 *
254
	 * @param string|GraphBuilder $subject
255
	 * @param string|null $predicate
256
	 * @param string|null $object
257
	 * @return self
258
	 * @throws InvalidArgumentException
259
	 */
260
	public function filterExists( $subject, $predicate = null, $object = null ) {
261
		$this->graphBuilder->filterExists( $subject, $predicate, $object );
262
		return $this;
263
	}
264
265
	/**
266
	 * Adds a filter that the given graph or triple does not exist.
267
	 *
268
	 * @since 0.3
269
	 *
270
	 * @param string|GraphBuilder $subject
271
	 * @param string|null $predicate
272
	 * @param string|null $object
273
	 * @return self
274
	 * @throws InvalidArgumentException
275
	 */
276
	public function filterNotExists( $subject, $predicate = null, $object = null ) {
277
		$this->graphBuilder->filterNotExists( $subject, $predicate, $object );
278
		return $this;
279
	}
280
281
	/**
282
	 * Adds the given graphs as alternative conditions.
283
	 *
284
	 * @since 0.3
285
	 *
286
	 * @param GraphBuilder|GraphBuilder[] $graphs
287
	 * @return self
288
	 * @throws InvalidArgumentException
289
	 */
290
	public function union( $graphs /* graphs ... */ ) {
0 ignored issues
show
The parameter $graphs is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
291
		call_user_func_array( array( $this->graphBuilder, 'union' ), func_get_args() );
292
		return $this;
293
	}
294
295
	/**
296
	 * Adds the given subquery.
297
	 *
298
	 * @param QueryBuilder $query
299
	 * @return self
300
	 * @throws InvalidArgumentException
301
	 */
302
	public function subquery( QueryBuilder $query ) {
303
		$this->graphBuilder->subquery( $query );
304
		return $this;
305
	}
306
307
	/**
308
	 * Creates a new subquery builder.
309
	 *
310
	 * @return QueryBuilder
311
	 */
312
	public function newSubquery() {
313
		return new QueryBuilder( $this->prefixBuilder->getPrefixes() );
314
	}
315
316
	/**
317
	 * Creates a new subgraph builder.
318
	 *
319
	 * @since 0.3
320
	 *
321
	 * @return GraphBuilder
322
	 */
323
	public function newSubgraph() {
324
		return new GraphBuilder( $this->usageValidator );
325
	}
326
327
	/**
328
	 * Sets the GROUP BY modifier.
329
	 *
330
	 * @param string|string[] $expressions
331
	 * @return self
332
	 * @throws InvalidArgumentException
333
	 */
334
	public function groupBy( $expressions /* expressions ... */ )  {
335
		$expressions = is_array( $expressions ) ? $expressions : func_get_args();
336
337
		$this->modifierBuilder->groupBy( $expressions );
338
		return $this;
339
	}
340
341
	/**
342
	 * Sets the HAVING modifier.
343
	 *
344
	 * @param string $expression
345
	 * @return self
346
	 * @throws InvalidArgumentException
347
	 */
348
	public function having( $expression ) {
349
		$this->modifierBuilder->having( $expression );
350
		return $this;
351
	}
352
353
	/**
354
	 * Sets the ORDER BY modifier.
355
	 *
356
	 * @param string $expression
357
	 * @param string $direction one of ASC or DESC
358
	 * @return self
359
	 * @throws InvalidArgumentException
360
	 */
361
	public function orderBy( $expression, $direction = 'ASC' ) {
362
		$this->modifierBuilder->orderBy( $expression, $direction );
363
		return $this;
364
	}
365
366
	/**
367
	 * Sets the LIMIT modifier.
368
	 *
369
	 * @param int $limit
370
	 * @return self
371
	 * @throws InvalidArgumentException
372
	 */
373
	public function limit( $limit ) {
374
		$this->modifierBuilder->limit( $limit );
375
		return $this;
376
	}
377
378
	/**
379
	 * Sets the OFFSET modifier.
380
	 *
381
	 * @param int $offset
382
	 * @return self
383
	 * @throws InvalidArgumentException
384
	 */
385
	public function offset( $offset ) {
386
		$this->modifierBuilder->offset( $offset );
387
		return $this;
388
	}
389
390
	/**
391
	 * Returns the plain SPARQL string of this query.
392
	 *
393
	 * @param bool $includePrefixes
394
	 * @return string
395
	 * @throws InvalidArgumentException
396
	 * @throws RangeException
397
	 */
398
	public function getSPARQL( $includePrefixes = true ) {
399
		if ( !is_bool( $includePrefixes ) ) {
400
			throw new InvalidArgumentException( '$includePrefixes has to be a bool' );
401
		}
402
403
		$this->usageValidator->validate();
404
405
		$sparql = $includePrefixes ? $this->prefixBuilder->getSPARQL() : '';
406
		// @todo throw an exception instead of defaulting to SELECT?
407
		$sparql .= $this->queryForm ?: 'SELECT';
408
		$sparql .= ' ' . $this->uniqueness . $this->formatSelects() . ' WHERE';
409
		$sparql .= ' {' . $this->graphBuilder->getSPARQL() . ' }';
410
		$sparql .= $this->modifierBuilder->getSPARQL();
411
412
		return $sparql;
413
	}
414
415
	private function formatSelects() {
416
		return empty( $this->selects ) ? '*' : implode( ' ', $this->selects );
417
	}
418
419
	/**
420
	 * @see self::getSPARQL
421
	 *
422
	 * @return string
423
	 */
424
	public function __toString() {
425
		return $this->getSPARQL();
426
	}
427
428
	/**
429
	 * Returns the formatted SPARQL string of this query.
430
	 *
431
	 * @see QueryFormatter::format
432
	 *
433
	 * @return string
434
	 */
435
	public function format() {
436
		$formatter = new QueryFormatter();
437
		return $formatter->format( $this->getSPARQL() );
438
	}
439
440
}
441