This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||||
2 | |||||
3 | namespace App\Admin\Controllers; |
||||
4 | |||||
5 | use App\Models\Eloquent\Problem; |
||||
6 | use App\Models\Eloquent\OJ; |
||||
7 | use App\Http\Controllers\Controller; |
||||
8 | use App\Admin\Forms\ImportPOEM; |
||||
9 | use Encore\Admin\Controllers\HasResourceActions; |
||||
10 | use Encore\Admin\Form; |
||||
11 | use Encore\Admin\Grid; |
||||
12 | use Encore\Admin\Layout\Content; |
||||
13 | use Encore\Admin\Show; |
||||
14 | use Exception; |
||||
15 | use Illuminate\Support\MessageBag; |
||||
16 | use ZipArchive; |
||||
17 | use Illuminate\Support\Facades\Storage; |
||||
18 | |||||
19 | class ProblemController extends Controller |
||||
20 | { |
||||
21 | use HasResourceActions; |
||||
22 | |||||
23 | /** |
||||
24 | * Index interface. |
||||
25 | * |
||||
26 | * @param Content $content |
||||
27 | * @return Content |
||||
28 | */ |
||||
29 | public function index(Content $content) |
||||
30 | { |
||||
31 | return $content |
||||
32 | ->header('Problems') |
||||
33 | ->description('all problems, problems managed by babel will not show here') |
||||
34 | ->body($this->grid()->render()); |
||||
35 | } |
||||
36 | |||||
37 | /** |
||||
38 | * Show interface. |
||||
39 | * |
||||
40 | * @param mixed $id |
||||
41 | * @param Content $content |
||||
42 | * @return Content |
||||
43 | */ |
||||
44 | public function show($id, Content $content) |
||||
45 | { |
||||
46 | $problem=Problem::findOrFail($id); |
||||
47 | if (!$problem->markdown || $problem->onlinejudge->ocode!=='noj') { |
||||
48 | return abort('403', 'Problem managed by BABEL cannot be accessed by Admin Portal.'); |
||||
49 | } |
||||
50 | return $content |
||||
51 | ->header('Problem Detail') |
||||
52 | ->description('the detail of problems') |
||||
53 | ->body($this->detail($id)); |
||||
54 | } |
||||
55 | |||||
56 | /** |
||||
57 | * Edit interface. |
||||
58 | * |
||||
59 | * @param mixed $id |
||||
60 | * @param Content $content |
||||
61 | * @return Content |
||||
62 | */ |
||||
63 | public function edit($id, Content $content) |
||||
64 | { |
||||
65 | $problem=Problem::findOrFail($id); |
||||
66 | if (!$problem->markdown || $problem->onlinejudge->ocode!=='noj') { |
||||
67 | return abort('403', 'Problem managed by BABEL cannot be accessed by Admin Portal.'); |
||||
68 | } |
||||
69 | return $content |
||||
70 | ->header('Edit Problem') |
||||
71 | ->description('edit the detail of problems') |
||||
72 | ->body($this->form()->edit($id)); |
||||
73 | } |
||||
74 | |||||
75 | /** |
||||
76 | * Import interface. |
||||
77 | * |
||||
78 | * @param Content $content |
||||
79 | * @return Content |
||||
80 | */ |
||||
81 | public function import(Content $content) |
||||
82 | { |
||||
83 | return $content |
||||
84 | ->header('Import New Problem') |
||||
85 | ->description('import a new problem from POEM or POETRY') |
||||
86 | ->body(new ImportPOEM()); |
||||
87 | } |
||||
88 | |||||
89 | /** |
||||
90 | * Create interface. |
||||
91 | * |
||||
92 | * @param Content $content |
||||
93 | * @return Content |
||||
94 | */ |
||||
95 | public function create(Content $content) |
||||
96 | { |
||||
97 | return $content |
||||
98 | ->header('Create New Problem') |
||||
99 | ->description('create a new problem') |
||||
100 | ->body($this->form(true)); |
||||
101 | } |
||||
102 | |||||
103 | /** |
||||
104 | * Make a grid builder. |
||||
105 | * |
||||
106 | * @return Grid |
||||
107 | */ |
||||
108 | protected function grid() |
||||
109 | { |
||||
110 | $grid=new Grid(new Problem); |
||||
111 | $grid->model()->where([ |
||||
112 | 'markdown'=>1, |
||||
113 | 'OJ'=>OJ::where(['ocode'=>'noj'])->first()->oid, |
||||
114 | ])->orderBy('pcode', 'asc'); |
||||
115 | $grid->column('pid', "ID")->sortable(); |
||||
116 | $grid->column('pcode', "PCode")->editable()->sortable(); |
||||
117 | $grid->title("Title")->editable(); |
||||
118 | $grid->solved_count(); |
||||
119 | $grid->time_limit("Time/ms")->editable(); |
||||
120 | $grid->memory_limit("Memory/kb")->editable(); |
||||
121 | $grid->OJ("OJ")->display(function() { |
||||
122 | return $this->onlinejudge->name; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
123 | }); |
||||
124 | $grid->tot_score("Score"); |
||||
125 | $grid->partial("Partial")->display(function($partial) { |
||||
126 | return $partial ? 'Yes' : 'No'; |
||||
127 | }); |
||||
128 | $grid->markdown("Markdown")->display(function($markdown) { |
||||
129 | return $markdown ? 'Yes' : 'No'; |
||||
130 | }); |
||||
131 | $grid->column('hide', 'Hide')->switch(); |
||||
132 | $grid->update_date('Updated At'); |
||||
133 | // $grid->order_index("order")->sortable(); |
||||
134 | $grid->filter(function(Grid\Filter $filter) { |
||||
135 | $filter->disableIdFilter(); |
||||
136 | $filter->like('pcode'); |
||||
137 | $filter->like('title'); |
||||
138 | }); |
||||
139 | |||||
140 | // $grid->disableCreateButton(); |
||||
141 | |||||
142 | return $grid; |
||||
143 | } |
||||
144 | |||||
145 | /** |
||||
146 | * Make a show builder. |
||||
147 | * |
||||
148 | * @param mixed $id |
||||
149 | * @return Show |
||||
150 | */ |
||||
151 | protected function detail($id) |
||||
152 | { |
||||
153 | $show=new Show(Problem::findOrFail($id)); |
||||
154 | return $show; |
||||
155 | } |
||||
156 | |||||
157 | /** |
||||
158 | * Make a form builder for create view and edit. |
||||
159 | * |
||||
160 | * @return Form |
||||
161 | */ |
||||
162 | protected function form($create=false) |
||||
0 ignored issues
–
show
The parameter
$create is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
163 | { |
||||
164 | $form=new Form(new Problem); |
||||
165 | $form->tab('Basic', function(Form $form) { |
||||
166 | $form->text('pid')->icon('MDI key')->readonly(); |
||||
167 | $form->text('pcode')->icon('MDI label-black')->rules('required|alpha_dash|min:3|max:20'); |
||||
168 | $form->text('title')->icon('MDI format-title')->rules('required'); |
||||
169 | $form->text('time_limit')->icon('MDI timer')->default(1000)->append('MS')->rules('required'); |
||||
170 | $form->text('memory_limit')->icon('MDI memory')->default(65536)->append('Kb')->rules('required'); |
||||
171 | $form->simplemde('description')->rules('required'); |
||||
172 | $form->simplemde('input'); |
||||
173 | $form->simplemde('output'); |
||||
174 | $form->simplemde('note'); |
||||
175 | $form->hasMany('problemSamples', 'samples', function(Form\NestedForm $form) { |
||||
176 | $form->textarea('sample_input', 'sample input')->rows(3); |
||||
177 | $form->textarea('sample_output', 'sample output')->rows(3); |
||||
178 | $form->textarea('sample_note', 'sample note')->rows(3); |
||||
179 | }); |
||||
180 | /* $form->table('samples', function ($table) { |
||||
181 | $table->textarea('sample_input', 'sample input'); |
||||
182 | $table->textarea('sample_output', 'sample output'); |
||||
183 | $table->textarea('sample_note', 'sample note'); |
||||
184 | }); */ |
||||
185 | $ojs_temp=OJ::select('oid', 'name')->get()->all(); |
||||
186 | $ojs=[]; |
||||
187 | foreach ($ojs_temp as $v) { |
||||
188 | $ojs[$v->oid]=$v->name; |
||||
189 | } |
||||
190 | $form->select('oj', 'OJ')->options($ojs)->default(1)->rules('required'); |
||||
191 | /* $form->display('update_date'); */ |
||||
192 | /* $form->text('tot_score')->rules('required'); |
||||
193 | $form->select('partial', 'Partial Score')->options([ |
||||
194 | 0 => "No", |
||||
195 | 1 => "Yes" |
||||
196 | ])->rules('required'); */ |
||||
197 | $form->radio('hide', 'Hide') |
||||
198 | ->options([ |
||||
199 | 0 => 'NO', |
||||
200 | 1 => 'YES' |
||||
201 | ])->default(0)->rules('required'); |
||||
202 | $form->radio('spj', 'Use SPJ') |
||||
203 | ->options([ |
||||
204 | 0 => 'NO', |
||||
205 | 1 => 'YES', |
||||
206 | ])->default(0)->rules('required'); |
||||
207 | $form->clang('spj_src', 'SPJ Source Code'); |
||||
208 | if ($form->isCreating()) { |
||||
209 | $form->chunk_file('test_case')->rules('required'); |
||||
210 | } else { |
||||
211 | $form->chunk_file('test_case'); |
||||
212 | } |
||||
213 | |||||
214 | $form->ignore(['test_case']); |
||||
215 | |||||
216 | //Hidden parameters |
||||
217 | |||||
218 | $form->hidden('markdown'); |
||||
219 | $form->hidden('input_type'); |
||||
220 | $form->hidden('output_type'); |
||||
221 | $form->hidden('solved_count'); |
||||
222 | $form->hidden('difficulty'); |
||||
223 | $form->hidden('partial'); |
||||
224 | $form->hidden('tot_score'); |
||||
225 | $form->hidden('file'); |
||||
226 | $form->hidden('spj_lang'); |
||||
227 | $form->hidden('spj_version'); |
||||
228 | }); |
||||
229 | /* if($create){ |
||||
230 | $form->tools(function (Form\Tools $tools) { |
||||
231 | $tools->append('<a href="/'.config('admin.route.prefix').'/problems/import" class="btn btn-sm btn-success" style="margin-right:1rem"><i class="MDI file-powerpoint-box"></i> Import from file</a>'); |
||||
232 | }); |
||||
233 | } */ |
||||
234 | $form->saving(function(Form $form) { |
||||
235 | $err=function($msg, $title='Test case file parse faild.') { |
||||
236 | $error=new MessageBag([ |
||||
237 | 'title' => $title, |
||||
238 | 'message' => $msg, |
||||
239 | ]); |
||||
240 | return back()->with(compact('error')); |
||||
241 | }; |
||||
242 | $pcode=$form->pcode; |
||||
243 | $p=Problem::where('pcode', $pcode)->first(); |
||||
244 | //check pcode has been token. |
||||
245 | $pid=$form->pid ?? null; |
||||
246 | if (!empty($p) && $p->pid!=$pid) { |
||||
247 | return $err('Pcode has been token', 'Error occur.'); |
||||
248 | } |
||||
249 | //Make sure the user enters SPJ_SRc in spj problem. |
||||
250 | if ($form->spj && empty($form->spj_src)) { |
||||
251 | return $err('The SPJ problem must provide spj_src', 'create problem error'); |
||||
252 | } |
||||
253 | |||||
254 | $test_case=null; |
||||
255 | |||||
256 | if (!is_null(request()->get('test_case'))) { |
||||
257 | $test_case=explode('http://fake.path/', request()->get('test_case'), 2)[1]; |
||||
0 ignored issues
–
show
It seems like
request()->get('test_case') can also be of type null ; however, parameter $string of explode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
258 | $path=Storage::disk('temp')->path($test_case); |
||||
259 | |||||
260 | if (pathinfo($path, PATHINFO_EXTENSION)!=='zip') { |
||||
261 | return $err('You must upload a zip file iuclude test case info and content.'); |
||||
262 | } |
||||
263 | |||||
264 | $zip=new ZipArchive; |
||||
265 | |||||
266 | if ($zip->open($path)!==true) { |
||||
267 | return $err('You must upload a zip file without encrypt and can open successfully.'); |
||||
268 | }; |
||||
269 | |||||
270 | //check info file. Try to generate if it does not exist. |
||||
271 | $info_content=[]; |
||||
272 | if (($zip->getFromName('info'))===false) { |
||||
273 | if (!$form->spj) { |
||||
274 | $info_content=[ |
||||
275 | 'spj' => false, |
||||
276 | 'test_cases' => [] |
||||
277 | ]; |
||||
278 | $files=[]; |
||||
279 | for ($i=0; $i<$zip->numFiles; $i++) { |
||||
280 | $filename=$zip->getNameIndex($i); |
||||
281 | $files[]=$filename; |
||||
282 | } |
||||
283 | $files_in=array_filter($files, function($filename) { |
||||
284 | return pathinfo($filename, PATHINFO_EXTENSION)=='in'; |
||||
285 | }); |
||||
286 | sort($files_in); |
||||
287 | $testcase_index=1; |
||||
288 | foreach ($files_in as $filename_in) { |
||||
289 | $filename=basename($filename_in, '.in'); |
||||
290 | $filename_out=$filename.'.out'; |
||||
291 | if (($zip->getFromName($filename_out))===false) { |
||||
292 | continue; |
||||
293 | } |
||||
294 | $test_case_in=preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $zip->getFromName($filename_in)); |
||||
295 | $test_case_out=preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $zip->getFromName($filename_out)); |
||||
296 | $info_content['test_cases']["{$testcase_index}"]=[ |
||||
297 | 'input_size' => strlen($test_case_in), |
||||
298 | 'input_name' => $filename_in, |
||||
299 | 'output_size' => strlen($test_case_out), |
||||
300 | 'output_name' => $filename_out, |
||||
301 | 'stripped_output_md5' => md5(utf8_encode(rtrim($test_case_out))) |
||||
302 | ]; |
||||
303 | $testcase_index+=1; |
||||
304 | } |
||||
305 | if ($testcase_index==1) { |
||||
0 ignored issues
–
show
|
|||||
306 | return $err('Cannot detect any validate testcases, please make sure they are placed under the root directory of the zip file.'); |
||||
307 | } |
||||
308 | } else { |
||||
309 | $info_content=[ |
||||
310 | 'spj' => true, |
||||
311 | 'test_cases' => [] |
||||
312 | ]; |
||||
313 | $files=[]; |
||||
314 | for ($i=0; $i<$zip->numFiles; $i++) { |
||||
315 | $filename=$zip->getNameIndex($i); |
||||
316 | $files[]=$filename; |
||||
317 | } |
||||
318 | $files_in=array_filter($files, function($filename) { |
||||
319 | return pathinfo($filename, PATHINFO_EXTENSION)=='in'; |
||||
320 | }); |
||||
321 | sort($files_in); |
||||
322 | $testcase_index=1; |
||||
323 | foreach ($files_in as $filename_in) { |
||||
324 | $test_case_in=$zip->getFromName($filename_in); |
||||
325 | $info_content['test_cases']["{$testcase_index}"]=[ |
||||
326 | 'input_size' => strlen($test_case_in), |
||||
327 | 'input_name' => $filename_in |
||||
328 | ]; |
||||
329 | $testcase_index+=1; |
||||
330 | } |
||||
331 | if ($testcase_index==1) { |
||||
0 ignored issues
–
show
|
|||||
332 | return $err('Cannot detect any validate testcases, please make sure they are placed under the root directory of the zip file.'); |
||||
333 | } |
||||
334 | } |
||||
335 | $zip->addFromString('info', json_encode($info_content)); |
||||
336 | $zip->close(); |
||||
337 | //return $err('The zip files must include a file named info including info of test cases, and the format can see ZsgsDesign/NOJ wiki.'); |
||||
338 | } else { |
||||
339 | $info_content=json_decode($zip->getFromName('info'), true); |
||||
340 | }; |
||||
341 | $zip->open($path); |
||||
342 | //If there is an INFO file, check that the contents of the file match the actual situation |
||||
343 | $test_cases=$info_content['test_cases']; |
||||
344 | foreach ($test_cases as $index => $case) { |
||||
345 | if (!isset($case['input_name']) || (!$form->spj && !isset($case['output_name']))) { |
||||
346 | return $err("Test case index {$index}: configuration missing input/output files name."); |
||||
347 | } |
||||
348 | $test_case_in=$zip->getFromName($case['input_name']); |
||||
349 | if (!$form->spj) { |
||||
350 | $test_case_out=$zip->getFromName($case['output_name']); |
||||
351 | } |
||||
352 | if ($test_case_in===false || (!$form->spj && $test_case_out===false)) { |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
353 | return $err("Test case index {$index}: missing input/output files that record in the configuration."); |
||||
354 | } |
||||
355 | $zip->addFromString($case['input_name'], preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $test_case_in)); |
||||
356 | if (!$form->spj) { |
||||
357 | $zip->addFromString($case['output_name'], preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $test_case_out)); |
||||
358 | } |
||||
359 | } |
||||
360 | $zip->close(); |
||||
361 | $zip->open($path); |
||||
362 | if (!empty($form->pid)) { |
||||
363 | $problem=Problem::find($form->pid); |
||||
364 | if (!empty($problem)) { |
||||
365 | $pcode=$problem->pcode; |
||||
366 | } else { |
||||
367 | $pcode=$form->pcode; |
||||
368 | } |
||||
369 | } else { |
||||
370 | $pcode=$form->pcode; |
||||
371 | } |
||||
372 | |||||
373 | if (Storage::disk('test_case')->exists($pcode)) { |
||||
0 ignored issues
–
show
It seems like
$pcode can also be of type array ; however, parameter $path of Illuminate\Filesystem\FilesystemAdapter::exists() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
374 | Storage::disk('test_case')->deleteDirectory($pcode); |
||||
0 ignored issues
–
show
It seems like
$pcode can also be of type array ; however, parameter $directory of Illuminate\Filesystem\Fi...pter::deleteDirectory() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
375 | } |
||||
376 | |||||
377 | Storage::disk('test_case')->makeDirectory($pcode); |
||||
0 ignored issues
–
show
It seems like
$pcode can also be of type array ; however, parameter $path of Illuminate\Filesystem\Fi...dapter::makeDirectory() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
378 | |||||
379 | $zip->extractTo(Storage::disk('test_case')->path($pcode)); |
||||
0 ignored issues
–
show
It seems like
$pcode can also be of type array ; however, parameter $path of Illuminate\Filesystem\FilesystemAdapter::path() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
380 | |||||
381 | $form->tot_score=count($info_content['test_cases']); |
||||
382 | |||||
383 | } |
||||
384 | //Set the spj-related data |
||||
385 | if ($form->spj) { |
||||
386 | $form->spj_lang='c'; |
||||
387 | $form->spj_version="{$form->pcode}#".time(); |
||||
388 | } |
||||
389 | //Set default data |
||||
390 | if ($form->isCreating() && empty($test_case)) { |
||||
391 | $form->tot_score=0; |
||||
392 | } |
||||
393 | $form->markdown=true; |
||||
394 | $form->input_type='standard input'; |
||||
395 | $form->output_type='standard output'; |
||||
396 | $form->solved_count=0; |
||||
397 | $form->difficulty=-1; |
||||
398 | $form->partial=1; |
||||
399 | $form->file=0; |
||||
400 | }); |
||||
401 | return $form; |
||||
402 | } |
||||
403 | } |
||||
404 |