Issues (524)

Security Analysis    not enabled

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.

js/variable_editor.jsx (9 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
var trim = require('underscore.string/trim');
2
var _ = require('underscore');
3
4
var React = require("react");
5
6
/**
7
 * VariableEditor allows editing variable => value pairs, with an inline "add" and "remove"
8
 * capability. Variables need to be kept unique.
9
 *
10
 * @param array variables Contains the list of key=>values
11
 *   [
12
 *     { variable: key1, value: val1	},
13
 *     { variable: key2, value: val2	},
14
 *     ...
15
 *   ]
16
 * @param array blacklist Array of blacklisted keys.
17
 * @param function submit Submission handler (gets passed the same variables struct as above, expects a promise).
18
 * @param function cancel Cancel handler.
19
 * @param function validateVariable Handler to call to validate a variable (gets passed a value, returns message).
20
 * @param bool showValues Set to false to hide value column.
21
 * @param bool showHeading Set to false to hide the header row.
22
 * @param string variableHeading Set the heading for the variable column.
23
 * @param string valueHeading Set the heading for the value column.
24
 */
25
var VariableEditor = React.createClass({
26
27
	getDefaultProps: function() {
28
		return {
29
			validateVariable: function(value) {
30
				var message = '';
31
				if (value.match(/[^a-zA-Z_0-9]/)) {
32
					message = "Only alphanumerics and underscore permitted.";
33
				}
34
				if (value.match(/^[0-9]/)) {
35
					message = "Value cannot start with a digit.";
36
				}
37
				return message;
38
			},
39
			showValues: true,
40
			showHeading: true,
41
			variableHeading: "Variable <small>(string)</small>",
42
			valueHeading: "Value <small>(string)</small>"
43
		};
44
	},
45
46
	getInitialState: function() {
47
48
		var variables = this.deepCopyVariables(this.props.variables);
49
50
		// Add additional state needed for editing.
51
		for (var i = 0; i < variables.length; i++) {
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
52
			variables[i].error = "";
53
			variables[i].vacant = false;
54
			variables[i].deleted = false;
55
		}
56
57
		variables.push({
58
			variable: "",
59
			value: "",
60
			error: "",
61
			vacant: true,
62
			deleted: false
63
		});
64
65
		return {
66
			saving: false,
67
			variables: variables,
68
			valid: true,
69
			message: ""
70
		};
71
	},
72
73
	deepCopyVariables: function(from) {
74
		var to = [];
75
		for (var i = 0; i < from.length; i++) {
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
76
			if (!from[i].deleted && !from[i].vacant) {
77
				to.push({
78
					variable: from[i].variable,
79
					value: from[i].value
80
				});
81
			}
82
		}
83
84
		return to;
85
	},
86
87
	save: function(event) {
88
		event.stopPropagation();
89
		event.preventDefault();
90
91
		this.setState({
92
			saving: true,
93
			message: ""
94
		});
95
96
		// Deep copying also filters the variables.
97
		var newVariables = this.deepCopyVariables(this.state.variables);
98
99
		var self = this;
100
		this.props.submit(newVariables)
101
			.fin(function() {
102
				// it's possible that the this.props.submit will unmount this
103
				// component due to state changes, this guards against setting
104
				// state on a unmounted component
105
				if (self.isMounted()) {
106
					self.setState({
107
						saving: false
108
					});
109
				}
110
			});
111
	},
112
113
	/**
114
	 * Main model is represented by an array, kept in the state. Wrap rows in a proxy object
115
	 * so that we can manipulate on them as if they were individual items.
116
	 *
117
	 * @param int row Row index in the variables array. If the index is past the end of the array, it's treated
118
	 *    as a new item that can be added into the model.
119
	 */
120
	rowStateProxy: function(row) {
121
		var self = this;
122
123
		var updateState = function() {
124
			self.setState({variables: self.state.variables});
125
		};
126
127
		var isVariableUnique = function(variable) {
128
			for (var i = 0; i < self.state.variables.length; i++) {
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
129
				if (row !== i
130
					&& !self.state.variables[i].deleted
131
					&& !self.state.variables[i].vacant
132
					&& self.state.variables[i].variable === variable
133
				) return false;
134
			}
135
			return true;
136
		};
137
138
		return ({
139
			isVacant: function() {
140
				return (typeof self.state.variables[row].vacant !== 'undefined' && self.state.variables[row].vacant);
141
			},
142
			setVariable: function(variable) {
143
				if (self.state.variables[row].vacant) {
144
					self.state.variables.push({
145
						variable: "",
146
						value: "",
147
						vacant: true
148
					});
149
				}
150
151
				self.state.variables[row].variable = variable;
152
				self.state.variables[row].vacant = false;
153
				updateState();
154
			},
155
			setValue: function(value) {
156
				self.state.variables[row].value = value;
157
				updateState();
158
			},
159
			remove: function() {
160
				self.state.variables[row].deleted = true;
161
				updateState();
162
			},
163
			validateVariable: function(value) {
164
				var message = self.props.validateVariable(value);
165
				if (trim(value) === "") {
166
					message = "Value cannot be empty.";
167
				}
168
				if (!isVariableUnique(value)) {
169
					message = "Value already exists.";
170
				}
171
				if (self.props.blacklist) {
172
					for (var i = 0; i < self.props.blacklist.length; i++) {
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
173
						var re = new RegExp(self.props.blacklist[i]);
174
						if (value.match(re)) {
175
							message = "Value not allowed.";
176
							break;
177
						}
178
					}
179
				}
180
181
				self.state.variables[row].error = message;
182
				updateState();
183
				return message;
184
			}
185
		});
186
	},
187
188
	isFormValid: function() {
189
		for (var i = 0; i < this.state.variables.length; i++) {
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
190
			if (!this.state.variables[i].vacant
191
				&& !this.state.variables[i].deleted
192
				&& this.state.variables[i].error) return false;
193
		}
194
195
		return true;
196
	},
197
198
	render: function() {
199
		var formValid = this.isFormValid();
200
201
		var self = this;
202
		var i = 0;
203
		var readonlyRows = _.map(this.props.readonlyVariables, function(item) {
204
			var row = (
205
				<VariableEditorReadonlyRow
206
					showValues={self.props.showValues}
207
					key={"readonly-" + i}
208
					variable={item.variable}
209
					value={item.value}
210
				/>
211
			);
212
			i++;
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
213
			return row;
214
		});
215
216
		var j = 0;
217
		var rows = _.map(this.state.variables, function(item) {
218
			var row;
219
			if (!item.deleted) {
220
				// Rely on the positional number of the model row as the key. As rows are deleted,
221
				// the variables will get marked up with "deleted: true", but remain in the model
222
				// to ensure react knows what rows to changed.
223
				row = (
224
					<VariableEditorRow
225
						showValues={self.props.showValues}
226
						key={j}
227
						disabled={self.state.saving}
228
						variable={item.variable}
229
						value={item.value}
230
						rowState={self.rowStateProxy(j)}
231
					/>
232
				);
233
			}
234
			j++;
0 ignored issues
show
As per coding-style, please do not use ++, but use += 1 instead.
Loading history...
235
			return row;
236
		});
237
238
		var message = null;
239
		if (this.state.message) {
240
			message = (
241
				<div className="alert alert-danger">{this.state.message}</div>
242
			);
243
		}
244
245
		var valueHeading = null;
246
		if (this.props.showValues) {
247
			valueHeading = <th className="value" dangerouslySetInnerHTML={{__html:this.props.valueHeading}} />;
0 ignored issues
show
Dangerous property 'dangerouslySetInnerHTML' found
Loading history...
248
		}
249
250
		var heading = null;
251
		if (this.props.showHeading) {
252
			heading = (
253
				<thead>
254
					<tr>
255
						<th
256
							className="variable"
257
							dangerouslySetInnerHTML={{__html:this.props.variableHeading}}
0 ignored issues
show
Dangerous property 'dangerouslySetInnerHTML' found
Loading history...
258
						/>
259
							{valueHeading}
260
						<th className="actions">&nbsp;</th>
261
					</tr>
262
				</thead>
263
			);
264
		}
265
266
		return (
267
			<div className="variables">
268
				<form className="variable-editor" onSubmit={this.save}>
269
					<VariableEditorActions
270
						disabled={!formValid}
271
						saving={this.state.saving}
272
						cancel={this.props.cancel}
273
					/>
274
					{message}
275
					<table className="table">
276
						{heading}
277
						<tbody>
278
							{readonlyRows}
279
							{rows}
280
						</tbody>
281
					</table>
282
				</form>
283
			</div>
284
		);
285
	}
286
});
287
288
function VariableEditorActions(props) {
289
290
	var buttonText = "";
291
	if (props.saving) {
292
		buttonText = "Saving...";
293
	} else {
294
		buttonText = "Save";
295
	}
296
297
	return (
298
		<div className="variables-actions variable-editor-actions">
299
			<input
300
				type="submit"
301
				disabled={props.disabled || props.saving}
302
				className="btn btn-primary"
303
				value={buttonText}
304
			/>
305
			<button
306
				type="button"
307
				className="btn btn-default"
308
				disabled={props.disabled || props.saving}
309
				onClick={props.cancel}
310
			>
311
				Cancel
312
			</button>
313
		</div>
314
	);
315
}
316
317
var VariableEditorRow = React.createClass({
318
319
	handleVariableChange: function(event) {
320
		this.props.rowState.setVariable(event.target.value);
321
	},
322
323
	handleValueChange: function(event) {
324
		this.props.rowState.setValue(event.target.value);
325
	},
326
327
	render: function() {
328
		var remove = null;
329
330
		if (!this.props.rowState.isVacant() && !this.props.disabled) {
331
			remove = (
332
				<button
333
					type="button"
334
					className="btn btn-danger"
335
					onClick={this.props.rowState.remove}
336
					disabled={this.props.disabled}
337
				>
338
					<span className="fa fa-times no-text"aria-hidden="true"></span>
339
				</button>
340
			);
341
		}
342
343
		var value = null;
344
		if (this.props.showValues) {
345
			value = (
346
				<td className="value">
347
					<input
348
						disabled={this.props.disabled}
349
						className='form-control'
350
						type="text"
351
						value={this.props.value}
352
						onChange={this.handleValueChange}
353
					/>
354
				</td>
355
			);
356
		}
357
358
		return (
359
			<tr>
360
				<td className="variable">
361
					<ValidatableInput
362
						disabled={this.props.disabled}
363
						type="text"
364
						value={this.props.variable}
365
						onChange={this.handleVariableChange}
366
						onValidate={this.props.rowState.validateVariable}
367
					/>
368
				</td>
369
				{value}
370
				<td className="actions">
371
					{remove}
372
				</td>
373
			</tr>
374
		);
375
	}
376
});
377
378
function VariableEditorReadonlyRow(props) {
379
380
	var value = null;
381
	if (props.showValues) {
382
		value = <td className="value">{props.value}</td>;
383
	}
384
385
	return (
386
		<tr>
387
			<td className="variable">{props.variable}</td>
388
			{value}
389
			<td className="actions text-center">
390
				<i className="fa fa-lock"></i>
391
			</td>
392
		</tr>
393
	);
394
}
395
396
/**
397
 * Input field with an ability to show an error message. Pass validate, onValidationFail and onValidationSuccess
398
 * callbacks to handle the error messaging.
399
 */
400
var ValidatableInput = React.createClass({
401
	getInitialState: function() {
402
		return {
403
			message: ""
404
		};
405
	},
406
407
	handleChange: function(event) {
408
		var message = this.props.onValidate(event.target.value);
409
		this.setState({message: message});
410
		if (this.props.onChange) this.props.onChange(event);
411
	},
412
413
	render: function() {
414
		var className = 'form-control';
415
		var alert = null;
416
		if (this.state.message) {
417
			alert = <div className='validation-message'>{this.state.message}</div>;
418
			className += ' validation-error';
419
		}
420
		return (
421
			<div className="form-group">
422
				<input
423
					disabled={this.props.disabled}
424
					className={className}
425
					type={this.props.type}
426
					onChange={this.handleChange}
427
					value={this.props.value}
428
				/>
429
				{alert}
430
			</div>
431
		);
432
	}
433
});
434
435
module.exports = VariableEditor;
436