PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / trunk
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration vtrunk
2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 1.45 All 41 releases
← All changes | app/Services/Libs/FileSystem.php +77 -3 1.95trunk View file →
@@ -63,8 +63,78 @@
63 63 return $this->_getDir() . DIRECTORY_SEPARATOR . $file;
64 64 }
65 65
66 66 /**
67 + * Resolve a stored attachment path only when it belongs to Fluent Boards uploads.
68 + *
69 + * @param string $storedPath
70 + * @param int|null $boardId
71 + * @return string|null
72 + */
73 + public function _resolveLocalAttachmentPath($storedPath, $boardId = null)
74 + {
75 + if (!$storedPath) {
76 + return null;
77 + }
78 +
79 + $storedFilename = rawurldecode((string) $storedPath);
80 + $isBareFilename = $storedFilename !== ''
81 + && strpos($storedFilename, '/') === false
82 + && strpos($storedFilename, '\\') === false;
83 +
84 + if ($isBareFilename && $boardId === null) {
85 + return null;
86 + }
87 +
88 + $filePath = $isBareFilename ? null : realpath($storedPath);
89 + $allowedDirectory = $this->_getDir();
90 + $pluginRoot = realpath($allowedDirectory);
91 +
92 + if (!$pluginRoot) {
93 + return null;
94 + }
95 +
96 + $pluginRoot = rtrim($pluginRoot, DIRECTORY_SEPARATOR);
97 + $allowedRoot = $pluginRoot;
98 +
99 + if ($boardId !== null) {
100 + if (!is_int($boardId) && !is_string($boardId)) {
101 + return null;
102 + }
103 +
104 + $boardId = filter_var($boardId, FILTER_VALIDATE_INT, [
105 + 'options' => ['min_range' => 1],
106 + ]);
107 + if ($boardId === false) {
108 + return null;
109 + }
110 +
111 + $allowedDirectory .= DIRECTORY_SEPARATOR . 'board_' . $boardId;
112 + if (is_link($allowedDirectory)) {
113 + return null;
114 + }
115 +
116 + $allowedRoot = realpath($allowedDirectory);
117 + $expectedBoardRoot = $pluginRoot . DIRECTORY_SEPARATOR . 'board_' . $boardId;
118 + if (!$allowedRoot || $allowedRoot !== $expectedBoardRoot) {
119 + return null;
120 + }
121 + }
122 +
123 + if ($isBareFilename) {
124 + $filePath = realpath($allowedDirectory . DIRECTORY_SEPARATOR . $storedFilename);
125 + }
126 +
127 + if (!$filePath || !$allowedRoot || !is_file($filePath)) {
128 + return null;
129 + }
130 +
131 + $allowedRoot = rtrim($allowedRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
132 +
133 + return strpos($filePath, $allowedRoot) === 0 ? $filePath : null;
134 + }
135 +
136 + /**
67 137 * Upload files into custom upload dir of this application
68 138 * @return array
69 139 */
70 140 public function _uploadFromRequest()
@@ -84,9 +154,14 @@
84 154 }
85 155
86 156 $this->overrideUploadDir();
87 157
88 - $uploadOverrides = ['test_form' => false];
158 + $uploadOverrides = [
159 + 'test_form' => false,
160 + // Accept the same allow-list FluentBoards validates against, so wp_handle_upload
161 + // doesn't reject broader types (e.g. .json, .md) by extension.
162 + 'mimes' => \FluentBoards\App\Services\UploadService::getAllowedMimeMap(),
163 + ];
89 164 $uploadedFiles = []; // Initialize the array
90 165
91 166 if(is_object($files)) {
92 167 $files = [$files];
@@ -177,10 +252,9 @@
177 252 * @return array $file
178 253 */
179 254 public function _renameFileName($file)
180 255 {
181 - $currentTimeStamp = (new \DateTimeImmutable())->getTimestamp();
182 - $prefix = $currentTimeStamp . '-';
256 + $prefix = wp_generate_uuid4() . '-';
183 257 $prefix = apply_filters('fluent_boards/uploaded_file_name_prefix', $prefix);
184 258 $file['name'] = $prefix . $file['name'];
185 259
186 260 return $file;