Skip to content

Commit b4a6d8b

Browse files
committed
Fixed CVEs
fixed CVEs fixed CVEs code format
1 parent 99e9d0d commit b4a6d8b

File tree

43 files changed

+868
-262
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+868
-262
lines changed

innopacks/common/src/Repositories/AddressRepo.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,13 @@ public static function getAddressTypes(): array
4444
*/
4545
public function builder(array $filters = []): Builder
4646
{
47-
$builder = Address::query();
48-
$customerID = $filters['customer_id'] ?? 0;
49-
if ($customerID) {
50-
$builder->where('customer_id', $customerID);
47+
$builder = Address::query();
48+
if (isset($filters['customer_id'])) {
49+
$builder->where('customer_id', (int) $filters['customer_id']);
5150
}
5251

53-
$guestID = $filters['guest_id'] ?? '';
54-
if (empty($customerID) && $guestID) {
55-
$builder->where('guest_id', $guestID);
52+
if (isset($filters['guest_id'])) {
53+
$builder->where('guest_id', (int) $filters['guest_id']);
5654
}
5755

5856
return fire_hook_filter('repo.address.builder', $builder);

innopacks/common/src/Repositories/OrderRepo.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,12 @@ private function handleData($requestData): array
206206
$customer = Customer::query()->find($customerID);
207207
}
208208

209+
$guestID = $requestData['guest_id'] ?? '';
210+
209211
$shippingAddressID = (int) $requestData['shipping_address_id'];
210212
$billingAddressID = (int) $requestData['billing_address_id'];
211-
$shippingAddress = $shippingAddressID ? Address::query()->findOrFail($shippingAddressID) : null;
212-
$billingAddress = $billingAddressID ? Address::query()->findOrFail($billingAddressID) : null;
213+
$shippingAddress = $this->getValidatedAddress($shippingAddressID, $customerID, $guestID);
214+
$billingAddress = $this->getValidatedAddress($billingAddressID, $customerID, $guestID);
213215

214216
$saData = $shippingAddress ? (new AddressListItem($shippingAddress))->jsonSerialize() : [];
215217
$baData = $billingAddress ? (new AddressListItem($billingAddress))->jsonSerialize() : [];
@@ -275,6 +277,33 @@ public function getOrderByNumber($orderNumber, bool $force = false): mixed
275277
return $this->builder(['number' => $orderNumber])->first();
276278
}
277279

280+
/**
281+
* Get validated address by ID and customer/guest ownership
282+
*
283+
* @param int $addressID
284+
* @param int $customerID
285+
* @param string $guestID
286+
* @return Address|null
287+
*/
288+
private function getValidatedAddress(int $addressID, int $customerID, string $guestID = ''): ?Address
289+
{
290+
if (! $addressID) {
291+
return null;
292+
}
293+
294+
$query = Address::query()->where('id', $addressID);
295+
296+
if ($customerID) {
297+
$query->where('customer_id', $customerID);
298+
} elseif ($guestID) {
299+
$query->where('customer_id', 0)->where('guest_id', $guestID);
300+
} else {
301+
throw new Exception('Address validation requires either customer_id or guest_id');
302+
}
303+
304+
return $query->firstOrFail();
305+
}
306+
278307
/**
279308
* Generate order number.
280309
*

innopacks/front/src/Requests/UploadFileRequest.php renamed to innopacks/common/src/Requests/UploadFileRequest.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
88
*/
99

10-
namespace InnoShop\Front\Requests;
10+
namespace InnoShop\Common\Requests;
1111

1212
use Illuminate\Foundation\Http\FormRequest;
1313

@@ -30,14 +30,18 @@ public function authorize(): bool
3030
*/
3131
public function rules(): array
3232
{
33-
if (is_admin()) {
34-
$rule = 'required|image|mimes:jpg,png,jpeg,gif,svg,webp,zip,doc,docx,xls,xlsx,ppt,pptx,pdf,mp4|max:4096';
33+
// Unified security policy - no dangerous file types including SVG
34+
$allowedMimes = 'jpg,png,jpeg,gif,webp,zip,doc,docx,xls,xlsx,ppt,pptx,pdf,mp4';
35+
36+
// Dynamic file size limits based on context
37+
if (request()->is('panel/*') || is_admin()) {
38+
$maxSize = 8192; // 8MB for admin/panel
3539
} else {
36-
$rule = 'required|image|mimes:jpg,png,jpeg,gif,webp,zip,doc,docx,xls,xlsx,ppt,pptx,pdf,mp4|max:2048';
40+
$maxSize = 2048; // 2MB for regular users
3741
}
3842

3943
return [
40-
'file' => $rule,
44+
'file' => "required|file|mimes:{$allowedMimes}|max:{$maxSize}",
4145
'type' => 'required|alpha_dash',
4246
];
4347
}

innopacks/front/src/Requests/UploadImageRequest.php renamed to innopacks/common/src/Requests/UploadImageRequest.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
88
*/
99

10-
namespace InnoShop\Front\Requests;
10+
namespace InnoShop\Common\Requests;
1111

1212
use Illuminate\Foundation\Http\FormRequest;
1313

@@ -30,14 +30,19 @@ public function authorize(): bool
3030
*/
3131
public function rules(): array
3232
{
33-
if (is_admin()) {
34-
$rule = 'required|image|mimes:jpg,png,jpeg,gif,svg,webp|max:4096';
33+
// Unified security policy for all contexts
34+
// No SVG support for security reasons
35+
$allowedMimes = 'jpg,png,jpeg,gif,webp';
36+
37+
// Dynamic file size limits based on context
38+
if (request()->is('panel/*') || is_admin()) {
39+
$maxSize = 8192; // 8MB for admin/panel
3540
} else {
36-
$rule = 'required|image|mimes:jpg,png,jpeg,gif,webp|max:2048';
41+
$maxSize = 2048; // 2MB for regular users
3742
}
3843

3944
return [
40-
'image' => $rule,
45+
'image' => "required|image|mimes:{$allowedMimes}|max:{$maxSize}",
4146
'type' => 'required|alpha_dash',
4247
];
4348
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<?php
2+
/**
3+
* Copyright (c) Since 2024 InnoShop - All Rights Reserved
4+
*
5+
* @link https://www.innoshop.com
6+
* @author InnoShop <team@innoshop.com>
7+
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
8+
*/
9+
10+
namespace InnoShop\Common\Services;
11+
12+
use Exception;
13+
14+
class FileSecurityValidator
15+
{
16+
/**
17+
* List of dangerous file extensions
18+
*/
19+
private const DANGEROUS_EXTENSIONS = [
20+
'php', 'php3', 'php4', 'php5', 'phtml', 'php7', 'php8',
21+
'asp', 'aspx', 'jsp', 'pl', 'py', 'rb', 'sh', 'cgi',
22+
'exe', 'bat', 'cmd', 'com', 'scr', 'vbs', 'js', 'jar',
23+
'htaccess', 'htpasswd',
24+
];
25+
26+
/**
27+
* File extensions that may contain malicious code
28+
*/
29+
private const POTENTIALLY_DANGEROUS_EXTENSIONS = [
30+
'svg', 'html', 'htm', 'xml',
31+
];
32+
33+
/**
34+
* Validate file extension security
35+
*
36+
* @param string $fileName File name
37+
* @param bool $allowSvg Whether to allow SVG files (requires additional processing)
38+
* @throws Exception
39+
*/
40+
public static function validateFileExtension(string $fileName, bool $allowSvg = false): void
41+
{
42+
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
43+
44+
// Check absolutely dangerous extensions
45+
if (in_array($extension, self::DANGEROUS_EXTENSIONS)) {
46+
throw new Exception("Dangerous file extension '{$extension}' is not allowed");
47+
}
48+
49+
// Check potentially dangerous extensions (like SVG)
50+
if (! $allowSvg && in_array($extension, self::POTENTIALLY_DANGEROUS_EXTENSIONS)) {
51+
throw new Exception("Potentially dangerous file extension '{$extension}' is not allowed. Use allowSvg=true if you want to enable SVG with proper sanitization.");
52+
}
53+
}
54+
55+
/**
56+
* Validate and sanitize SVG file content (remove scripts and event handlers)
57+
*
58+
* @param string $svgContent SVG file content
59+
* @return string Sanitized SVG content
60+
* @throws Exception
61+
*/
62+
public static function sanitizeSvgContent(string $svgContent): string
63+
{
64+
// Remove script tags
65+
$svgContent = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi', '', $svgContent);
66+
67+
// Remove event handler attributes
68+
$dangerousAttributes = [
69+
'onload', 'onerror', 'onmouseover', 'onmouseout', 'onclick', 'onmousemove',
70+
'onmousedown', 'onmouseup', 'onfocus', 'onblur', 'onkeydown', 'onkeyup',
71+
'onsubmit', 'onreset', 'onchange', 'onselect', 'javascript:',
72+
];
73+
74+
foreach ($dangerousAttributes as $attr) {
75+
$svgContent = preg_replace('/'.$attr.'\s*=\s*["\'][^"\']*["\']/i', '', $svgContent);
76+
}
77+
78+
// Remove external resource references (prevent external script loading)
79+
$svgContent = preg_replace('/href\s*=\s*["\'](?!#)[^"\']*["\']/i', '', $svgContent);
80+
81+
// Check if it's still valid SVG
82+
if (! preg_match('/<svg\b/i', $svgContent)) {
83+
throw new Exception('Invalid SVG content after sanitization');
84+
}
85+
86+
return $svgContent;
87+
}
88+
89+
/**
90+
* Check if file MIME type is safe
91+
*
92+
* @param string $mimeType MIME type
93+
* @throws Exception
94+
*/
95+
public static function validateMimeType(string $mimeType): void
96+
{
97+
$dangerousMimeTypes = [
98+
'application/x-php',
99+
'application/x-httpd-php',
100+
'text/x-php',
101+
'application/php',
102+
'application/x-sh',
103+
'application/x-csh',
104+
'text/x-shellscript',
105+
];
106+
107+
if (in_array(strtolower($mimeType), $dangerousMimeTypes)) {
108+
throw new Exception("Dangerous MIME type '{$mimeType}' is not allowed");
109+
}
110+
}
111+
112+
/**
113+
* Check if file name contains path traversal attacks
114+
*
115+
* @param string $fileName File name
116+
* @throws Exception
117+
*/
118+
public static function validateFileName(string $fileName): void
119+
{
120+
// Check path traversal
121+
if (str_contains($fileName, '..') || str_contains($fileName, '/') || str_contains($fileName, '\\')) {
122+
throw new Exception('Invalid file name: path traversal detected');
123+
}
124+
125+
// Check file name length
126+
if (strlen($fileName) > 255) {
127+
throw new Exception('File name too long');
128+
}
129+
130+
// Check empty file name
131+
if (empty(trim($fileName))) {
132+
throw new Exception('File name cannot be empty');
133+
}
134+
}
135+
136+
/**
137+
* Comprehensive file security validation
138+
*
139+
* @param string $fileName File name
140+
* @param string|null $mimeType MIME type
141+
* @param bool $allowSvg Whether to allow SVG
142+
* @throws Exception
143+
*/
144+
public static function validateFile(string $fileName, ?string $mimeType = null, bool $allowSvg = false): void
145+
{
146+
self::validateFileName($fileName);
147+
self::validateFileExtension($fileName, $allowSvg);
148+
149+
if ($mimeType) {
150+
self::validateMimeType($mimeType);
151+
}
152+
}
153+
154+
/**
155+
* Get list of safe image file extensions
156+
*/
157+
public static function getSafeImageExtensions(): array
158+
{
159+
return ['jpg', 'jpeg', 'png', 'gif', 'webp'];
160+
}
161+
162+
/**
163+
* Get list of safe document file extensions
164+
*/
165+
public static function getSafeDocumentExtensions(): array
166+
{
167+
return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'zip', 'rar'];
168+
}
169+
170+
/**
171+
* Validate and sanitize directory path to prevent path traversal attacks
172+
*
173+
* @param string $path Directory path to validate
174+
* @return string Sanitized safe path
175+
* @throws Exception If path contains dangerous patterns
176+
*/
177+
public static function validateDirectoryPath(string $path): string
178+
{
179+
// URL decode the path first
180+
$decodedPath = urldecode($path);
181+
182+
// Check for path traversal attacks BEFORE any normalization
183+
if (str_contains($decodedPath, '..')) {
184+
throw new Exception('Path traversal attack detected');
185+
}
186+
187+
// Check for null bytes and other dangerous characters
188+
if (str_contains($decodedPath, "\0") || str_contains($decodedPath, "\x00")) {
189+
throw new Exception('Null byte attack detected');
190+
}
191+
192+
// Normalize path separators
193+
$normalizedPath = str_replace('\\', '/', $decodedPath);
194+
195+
// Remove multiple consecutive slashes
196+
$normalizedPath = preg_replace('#/+#', '/', $normalizedPath);
197+
198+
// Handle root directory case
199+
if ($normalizedPath === '' || $normalizedPath === '/') {
200+
return '/';
201+
}
202+
203+
// Handle relative paths - convert to absolute paths with leading slash
204+
if (! str_starts_with($normalizedPath, '/')) {
205+
$normalizedPath = '/'.$normalizedPath;
206+
}
207+
208+
// Remove trailing slash (except for root)
209+
if ($normalizedPath !== '/') {
210+
$normalizedPath = rtrim($normalizedPath, '/');
211+
}
212+
213+
// Final check after normalization to prevent any remaining traversal attempts
214+
if (str_contains($normalizedPath, '..')) {
215+
throw new Exception('Path traversal attack detected after normalization');
216+
}
217+
218+
return $normalizedPath;
219+
}
220+
}

innopacks/front/src/Controllers/Account/AddressesController.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,14 @@ public function store(Request $request): mixed
5959
*/
6060
public function update(Request $request, Address $address): mixed
6161
{
62+
$currentCustomerId = current_customer_id();
63+
64+
if ($address->customer_id !== $currentCustomerId) {
65+
return json_fail('Unauthorized', null, 403);
66+
}
67+
6268
$data = $request->all();
63-
$data['customer_id'] = current_customer_id();
69+
$data['customer_id'] = $currentCustomerId;
6470

6571
$address = AddressRepo::getInstance()->update($address, $data);
6672
$result = new AddressListItem($address);
@@ -74,6 +80,12 @@ public function update(Request $request, Address $address): mixed
7480
*/
7581
public function destroy(Address $address): mixed
7682
{
83+
$currentCustomerId = current_customer_id();
84+
85+
if ($address->customer_id !== $currentCustomerId) {
86+
return json_fail('Unauthorized', null, 403);
87+
}
88+
7789
AddressRepo::getInstance()->destroy($address);
7890

7991
return delete_json_success();

0 commit comments

Comments
 (0)