ZsgsDesign /
NOJ
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
Loading history...
|
|||||
| 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. Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |