Issues (33)

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.

controller/rest/bookmarkcontroller.php (3 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
/**
4
 * ownCloud - bookmarks
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Stefan Klemm <[email protected]>
10
 * @copyright Stefan Klemm 2014
11
 */
12
13
namespace OCA\Bookmarks\Controller\Rest;
14
15
use OCP\IL10N;
16
use \OCP\IRequest;
17
use \OCP\AppFramework\ApiController;
18
use \OCP\AppFramework\Http\JSONResponse;
19
use \OCP\AppFramework\Http;
20
use \OCP\IDb;
21
use \OCA\Bookmarks\Controller\Lib\Bookmarks;
22
use \OCA\Bookmarks\Controller\Lib\ExportResponse;
23
use \OCA\Bookmarks\Controller\Lib\Helper;
24
use OCP\Util;
25
26
class BookmarkController extends ApiController {
27
28
	private $userId;
29
	private $db;
30
	private $l10n;
31
32
	public function __construct($appName, IRequest $request, $userId, IDb $db, IL10N $l10n) {
33
		parent::__construct($appName, $request);
34
		$this->userId = $userId;
35
		$this->db = $db;
36
		$this->request = $request;
37
		$this->l10n = $l10n;
38
	}
39
40
	/**
41
	 * @param string $type
42
	 * @param string $tag
43
	 * @param int $page
44
	 * @param string $sort
45
	 * @return JSONResponse
46
	 *
47
	 * @NoAdminRequired
48
	 */
49
	public function legacyGetBookmarks($type = "bookmark", $tag = '', $page = 0, $sort = "bookmarks_sorting_recent") {
50
		return $this->getBookmarks($type, $tag, $page, $sort);
51
	}
52
53
	/**
54
	 * @param string $type
55
	 * @param string $tag
56
	 * @param int $page
57
	 * @param string $sort
58
	 * @return JSONResponse
59
	 *
60
	 * @NoAdminRequired
61
	 */
62
	public function getBookmarks($type = "bookmark", $tag = '', $page = 0, $sort = "bookmarks_sorting_recent") {
63
64
		if ($type == 'rel_tags') {
65
			$tags = Bookmarks::analyzeTagRequest($tag);
66
			$qtags = Bookmarks::findTags($this->userId, $this->db, $tags);
67
			return new JSONResponse(array('data' => $qtags, 'status' => 'success'));
68
		} else { // type == bookmark
69
			$filterTag = Bookmarks::analyzeTagRequest($tag);
70
71
			$offset = $page * 10;
72
73
			if ($sort == 'bookmarks_sorting_clicks') {
74
				$sqlSortColumn = 'clickcount';
75
			} else {
76
				$sqlSortColumn = 'lastmodified';
77
			}
78
			$bookmarks = Bookmarks::findBookmarks($this->userId, $this->db, $offset, $sqlSortColumn, $filterTag, true);
79
			return new JSONResponse(array('data' => $bookmarks, 'status' => 'success'));
80
		}
81
	}
82
83
	/**
84
	 * @param string $url
85
	 * @param array $item
86
	 * @param int $from_own
87
	 * @param string $title
88
	 * @param bool $is_public
89
	 * @param string $description
90
	 * @return JSONResponse
91
	 *
92
	 * @NoAdminRequired
93
	 */
94
	public function newBookmark($url = "", $item = array(), $from_own = 0, $title = "", $is_public = false, $description = "") {
95
		$url_http = $url_https = '';
96
		if ($from_own == 0) {
97
			// allow only http(s) and (s)ftp
98
			$protocols = '/^(https?|s?ftp)\:\/\//i';
99
			try {
100
				if (preg_match($protocols, $url)) {
101
					$data = Bookmarks::getURLMetadata($url);
102
					// if not (allowed) protocol is given, assume http and https (and fetch both)
103
				} else {
104
					// append https to url and fetch it
105
					$url_https = 'https://' . $url;
106
					$data_https = Bookmarks::getURLMetadata($url_https);
107
					// append http to url and fetch it
108
					$url_http = 'http://' . $url;
109
					$data_http = Bookmarks::getURLMetadata($url_http);
110
				}
111
			} catch (\Exception $e) {
112
				return new JSONResponse(array('status' => 'error'), Http::STATUS_BAD_REQUEST);
113
			}
114
115
			if ($title === '' && isset($data['title'])) { // prefer original url if working
116
				$title = $data['title'];
117
				//url remains unchanged
118
			} elseif (isset($data_https['title'])) { // test if https works
119
				$title = $title === '' ? $data_https['title'] : $title;
120
				$url = $url_https;
121
			} elseif (isset($data_http['title'])) { // otherwise test http for results
122
				$title = $title === '' ? $data_http['title'] : $title;
123
				$url = $url_http;
124
			}
125
		}
126
127
		// Check if it is a valid URL (after adding http(s) prefix)
128
		$urlData = parse_url($url);
129 View Code Duplication
		if ($urlData === false || !isset($urlData['scheme']) || !isset($urlData['host'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
			return new JSONResponse(array('status' => 'error'), Http::STATUS_BAD_REQUEST);
131
		}
132
133
		$tags = isset($item['tags']) ? $item['tags'] : array();
134
135
		$id = Bookmarks::addBookmark($this->userId, $this->db, $url, $title, $tags, $description, $is_public);
136
		$bm = Bookmarks::findUniqueBookmark($id, $this->userId, $this->db);
137
		return new JSONResponse(array('item' => $bm, 'status' => 'success'));
138
	}
139
140
	/**
141
	 * @param int $id
142
	 * @param string $url
143
	 * @param array $item
144
	 * @param string $title
145
	 * @param bool $is_public Description
146
	 * @param null $record_id
147
	 * @param string $description
148
	 * @return Http\TemplateResponse
149
	 *
150
	 * @NoAdminRequired
151
	 */
152
	//TODO id vs record_id?
153
	public function legacyEditBookmark($id = null, $url = "", $item = array(), $title = "", $is_public = false, $record_id = null, $description = "") {
154
		if ($id == null) {
155
			return $this->newBookmark($url, $item, false, $title, $is_public, $description);
156
		} else {
157
			return $this->editBookmark($id, $url, $item, $title, $is_public, $record_id, $description);
158
		}
159
	}
160
161
	/**
162
	 * @param int $id
163
	 * @param string $url
164
	 * @param array $item
165
	 * @param string $title
166
	 * @param bool $is_public Description
167
	 * @param null $record_id
168
	 * @param string $description
169
	 * @return JSONResponse
170
	 *
171
	 * @NoAdminRequired
172
	 */
173
	public function editBookmark($id = null, $url = "", $item = array(), $title = "", $is_public = false, $record_id = null, $description = "") {
174
175
		// Check if it is a valid URL
176
		$urlData = parse_url($url);
177 View Code Duplication
		if ($urlData === false || !isset($urlData['scheme']) || !isset($urlData['host'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
			return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
179
		}
180
181
		if ($record_id == null) {
182
			return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
183
		}
184
185
		$tags = isset($item['tags']) ? $item['tags'] : array();
186
187
		if (is_numeric($record_id)) {
188
			$id = Bookmarks::editBookmark($this->userId, $this->db, $record_id, $url, $title, $tags, $description, $is_public = false);
189
		}
190
191
		$bm = Bookmarks::findUniqueBookmark($id, $this->userId, $this->db);
192
		return new JSONResponse(array('item' => $bm, 'status' => 'success'));
193
	}
194
195
	/**
196
	 * @param int $id
197
	 * @return JSONResponse
198
	 *
199
	 * @NoAdminRequired
200
	 */
201
	public function legacyDeleteBookmark($id = -1) {
202
		return $this->deleteBookmark($id);
203
	}
204
205
	/**
206
	 * @param int $id
207
	 * @return \OCP\AppFramework\Http\JSONResponse
208
	 *
209
	 * @NoAdminRequired
210
	 */
211
	public function deleteBookmark($id = -1) {
212
		if ($id == -1) {
213
			return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
214
		}
215
216
		if (!Bookmarks::deleteUrl($this->userId, $this->db, $id)) {
217
			return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
218
		} else {
219
			return new JSONResponse(array('status' => 'success'), Http::STATUS_OK);
220
		}
221
	}
222
223
	/**
224
	  @NoAdminRequired
225
	 * 
226
	 * @param string $url
227
	 * @return \OCP\AppFramework\Http\JSONResponse
228
	 */
229
	public function clickBookmark($url = "") {
230
231
		// Check if it is a valid URL
232
		$urlData = parse_url($url);
233 View Code Duplication
		if ($urlData === false || !isset($urlData['scheme']) || !isset($urlData['host'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
			return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
235
		}
236
237
		$query = $this->db->prepareQuery('
238
			UPDATE `*PREFIX*bookmarks`
239
			SET `clickcount` = `clickcount` + 1
240
			WHERE `user_id` = ?
241
				AND `url` LIKE ?
242
		');
243
244
		$params = array($this->userId, htmlspecialchars_decode($url));
245
		$query->execute($params);
246
247
		return new JSONResponse(array('status' => 'success'), Http::STATUS_OK);
248
	}
249
250
	/**
251
	  @NoAdminRequired
252
	 * 
253
	 * @return \OCP\AppFramework\Http\JSONResponse
254
	 */
255
	public function importBookmark() {
256
		$full_input = $this->request->getUploadedFile("bm_import");
257
258
		if (empty($full_input)) {
259
			Util::writeLog('bookmarks', "No file provided for import", Util::WARN);
260
			$error = array();
261
			$error[] = $this->l10n->t('No file provided for import');
262
		} else {
263
			$error = array();
264
			$file = $full_input['tmp_name'];
265
			if ($full_input['type'] == 'text/html') {
266
				$error = Bookmarks::importFile($this->userId, $this->db, $file);
267
				if (empty($error)) {
268
					return new JSONResponse(array('status' => 'success'));
269
				}
270
			} else {
271
				$error[] = $this->l10n->t('Unsupported file type for import');
272
			}
273
		}
274
275
		return new JSONResponse(array('status' => 'error', 'data' => $error));
276
	}
277
278
	/**
279
	  @NoAdminRequired
280
	 * 
281
	 * @return \OCP\AppFramework\Http\Response
282
	 */
283
	public function exportBookmark() {
284
285
		$file = <<<EOT
286
<!DOCTYPE NETSCAPE-Bookmark-file-1>
287
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
288
<!-- This is an automatically generated file.
289
It will be read and overwritten.
290
Do Not Edit! -->
291
<TITLE>Bookmarks</TITLE>
292
<H1>Bookmarks</H1>
293
<DL><p>
294
EOT;
295
		$bookmarks = Bookmarks::findBookmarks($this->userId, $this->db, 0, 'id', array(), true, -1);
296
		foreach ($bookmarks as $bm) {
297
			$title = $bm['title'];
298
			if (trim($title) === '') {
299
				$url_parts = parse_url($bm['url']);
300
				$title = isset($url_parts['host']) ? Helper::getDomainWithoutExt($url_parts['host']) : $bm['url'];
301
			}
302
			$file .= '<DT><A HREF="' . \OC_Util::sanitizeHTML($bm['url']) . '" TAGS="' . implode(',', \OC_Util::sanitizeHTML($bm['tags'])) . '">';
303
			$file .= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '</A>';
304
			if ($bm['description'])
305
				$file .= '<DD>' . htmlspecialchars($bm['description'], ENT_QUOTES, 'UTF-8');
306
			$file .= "\n";
307
		}
308
309
		return new ExportResponse($file);
310
	}
311
312
}
313