Cross-site Scripting (XSS) - Stored in crater-invoice/crater
Description
Cross-site Scripting (XSS) - Stored in Packagist bytefury/crater prior to 6.0.2.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Stored XSS in Crater invoice app prior to 6.0.2 via unrestricted file upload allows attackers to execute arbitrary JavaScript in victim browsers.
Vulnerability
Stored cross-site scripting (XSS) vulnerability in Crater, an open-source invoicing application, prior to version 6.0.2. The vulnerability exists in the file upload functionality, specifically in the UploadReceiptController and CompanyController which did not validate uploaded file types. An attacker could upload a malicious file (e.g., an SVG with embedded JavaScript) that would be stored and later served to other users. The fix introduced custom request classes (ExpenseRequest, AvatarRequest, CompanyLogoRequest) to enforce file type validation [1][2].
Exploitation
An attacker with the ability to upload files (e.g., as an authenticated user with permission to create expenses or update company settings) can upload a crafted file containing JavaScript. The file is stored on the server and when other users (including administrators) view the uploaded file (e.g., viewing an expense receipt or company logo), the malicious script executes in their browser context. No additional user interaction beyond viewing the page is required [1][2].
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's session. This can lead to theft of session cookies, account takeover, defacement, or other actions performed on behalf of the victim. The vulnerability is classified as stored XSS with a CVSS score of 6.1 (Medium) [3].
Mitigation
The vulnerability is fixed in Crater version 6.0.2, released on 2022-01-27. Users should upgrade to version 6.0.2 or later. The fix enforces file type validation using dedicated request classes. No workaround is available for earlier versions. The CVE is not listed in CISA's Known Exploited Vulnerabilities catalog [1][2][3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
bytefury/craterPackagist | < 6.0.0 | 6.0.0 |
Affected products
2- Range: unspecified
Patches
1cdc913d16cf6Unrestricted php file upload fix (#681)
7 files changed · +176 −8
app/Http/Controllers/V1/Admin/Expense/ExpensesController.php+2 −2 modified@@ -39,7 +39,7 @@ public function index(Request $request) /** * Store a newly created resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @return \Illuminate\Http\JsonResponse */ public function store(ExpenseRequest $request) @@ -67,7 +67,7 @@ public function show(Expense $expense) /** * Update the specified resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @param \Crater\Models\Expense $expense * @return \Illuminate\Http\JsonResponse */
app/Http/Controllers/V1/Admin/Expense/UploadReceiptController.php+3 −2 modified@@ -5,17 +5,18 @@ use Crater\Http\Controllers\Controller; use Crater\Models\Expense; use Illuminate\Http\Request; +use Crater\Http\Requests\ExpenseRequest; class UploadReceiptController extends Controller { /** * Upload the expense receipts to storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\ExpenseRequest $request * @param Expense $expense * @return \Illuminate\Http\JsonResponse */ - public function __invoke(Request $request, Expense $expense) + public function __invoke(ExpenseRequest $request, Expense $expense) { $this->authorize('update', $expense);
app/Http/Controllers/V1/Admin/Settings/CompanyController.php+6 −4 modified@@ -9,6 +9,8 @@ use Crater\Http\Resources\UserResource; use Crater\Models\Company; use Illuminate\Http\Request; +use Crater\Http\Requests\AvatarRequest; +use Crater\Http\Requests\CompanyLogoRequest; class CompanyController extends Controller { @@ -58,10 +60,10 @@ public function updateCompany(CompanyRequest $request) /** * Upload the company logo to storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\CompanyLogoRequest $request * @return \Illuminate\Http\JsonResponse */ - public function uploadCompanyLogo(Request $request) + public function uploadCompanyLogo(CompanyLogoRequest $request) { $company = Company::find($request->header('company')); @@ -89,10 +91,10 @@ public function uploadCompanyLogo(Request $request) /** * Upload the Admin Avatar to public storage. * - * @param \Illuminate\Http\Request $request + * @param \Crater\Http\Requests\AvatarRequest $request * @return \Illuminate\Http\JsonResponse */ - public function uploadAvatar(Request $request) + public function uploadAvatar(AvatarRequest $request) { $user = auth()->user();
app/Http/Requests/AvatarRequest.php+40 −0 added@@ -0,0 +1,40 @@ +<?php + +namespace Crater\Http\Requests; + +use Crater\Rules\Base64Mime; +use Illuminate\Foundation\Http\FormRequest; + +class AvatarRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize() + { + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'admin_avatar' => [ + 'nullable', + 'file', + 'mimes:gif,jpg,png', + 'max:20000' + ], + 'avatar' => [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +}
app/Http/Requests/CompanyLogoRequest.php+34 −0 added@@ -0,0 +1,34 @@ +<?php + +namespace Crater\Http\Requests; + +use Crater\Rules\Base64Mime; +use Illuminate\Foundation\Http\FormRequest; + +class CompanyLogoRequest extends FormRequest +{ + /** + * Determine if the user is authorized to make this request. + * + * @return bool + */ + public function authorize() + { + return true; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'company_logo' => [ + 'nullable', + new Base64Mime(['gif', 'jpg', 'png']) + ] + ]; + } +}
app/Http/Requests/ExpenseRequest.php+6 −0 modified@@ -51,6 +51,12 @@ public function rules() 'currency_id' => [ 'required' ], + 'attachment_receipt' => [ + 'nullable', + 'file', + 'mimes:jpg,png,pdf,doc,docx,xls,xlsx,ppt,pptx', + 'max:20000' + ] ]; if ($companyCurrency && $this->currency_id) {
app/Rules/Base64Mime.php+85 −0 added@@ -0,0 +1,85 @@ +<?php + +namespace Crater\Rules; + +use Illuminate\Contracts\Validation\Rule; + +class Base64Mime implements Rule +{ + private $attribute; + private $extensions; + + /** + * Create a new rule instance. + * + * @return void + */ + public function __construct(array $extensions) + { + $this->extensions = $extensions; + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + $this->attribute = $attribute; + + try { + $data = json_decode($value)->data; + } catch (\Exception $e) { + return False; + } + + $pattern = '/^data:\w+\/[\w\+]+;base64,[\w\+\=\/]+$/'; + + if(!preg_match($pattern, $data)) { + return False; + } + + $data = explode(',', $data); + + if(!isset($data[1]) || empty($data[1])) { + return False; + } + + try { + $data = base64_decode($data[1]); + $f = finfo_open(); + $result = finfo_buffer($f, $data, FILEINFO_EXTENSION); + + if($result === '???') + return False; + + if(strpos($result, '/')) { + foreach(explode('/', $result) as $ext) { + if(in_array($ext, $this->extensions)) + return True; + } + } else { + if(in_array($result, $this->extensions)) + return True; + } + } catch (\Exception $e) { + return False; + } + + return False; + + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The ' . $this->attribute . ' must be a json with file of type: ' . implode(', ', $this->extensions) . ' encoded in base64.'; + } +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-6vfw-74wr-3chhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-0372ghsaADVISORY
- github.com/crater-invoice/crater/commit/cdc913d16cf624aee852bc9163a7c6ffc8d1da9dghsax_refsource_MISCWEB
- github.com/crater-invoice/crater/pull/681ghsaWEB
- huntr.dev/bounties/563232b9-5a93-4f4d-8389-ed805b262ef1ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.