@extends('layouts.app') @section('title', '源碼掃描報告') @section('content')
返回掃描清單
@if($codeScan->status === 'done') 匯出 PDF 重新掃描 @endif
@csrf @method('DELETE')
{{-- 基本資訊 --}}
來源
{{ $codeScan->source_ref }}
@if($codeScan->branch) {{ $codeScan->branch }} @endif
掃描工具
{{ $codeScan->scannerLabel() }} @if($codeScan->language)
{{ $codeScan->language }}
@endif
狀態
@if($codeScan->status==='done') 完成 @elseif($codeScan->status==='running') 執行中 @elseif($codeScan->status==='failed') 失敗 @else 待執行 @endif
開始
{{ $codeScan->started_at?->format('Y-m-d H:i:s') ?? '—' }}
結束
{{ $codeScan->finished_at?->format('H:i:s') ?? '—' }}
@if($codeScan->durationSeconds())
@php $s=$codeScan->durationSeconds(); @endphp {{ $s>=60?floor($s/60).'m '.($s%60).'s':$s.'s' }}
@endif
{{-- 即時進度 --}} @if(in_array($codeScan->status, ['pending','running']))
掃描進行中 每 5 秒更新
等待輸出…
@endif @if($codeScan->status === 'failed' && $codeScan->error_log)
錯誤訊息:
{{ $codeScan->error_log }}
@endif @if($codeScan->status === 'done') @php $bySev = $summary['by_severity'] ?? []; $total = $summary['total'] ?? 0; $byOwasp = $summary['by_owasp'] ?? []; $fileCount = $summary['file_count'] ?? null; $dirCount = $summary['dir_count'] ?? null; $sevCounts = []; foreach($findings as $f) { $s=$f['severity']??'info'; $sevCounts[$s]=($sevCounts[$s]??0)+1; } @endphp {{-- 統計 --}}
@foreach(['critical'=>['danger','嚴重'],'high'=>['warning','高'],'medium'=>['info','中'],'low'=>['primary','低'],'info'=>['secondary','資訊']] as $sev=>[$col,$lbl])
{{ $bySev[$sev] ?? 0 }}
{{ $lbl }}
@endforeach
{{ $total }}
總計
{{-- 原始碼範圍統計 --}} @if($fileCount !== null)
掃描檔案:{{ number_format($fileCount) }} 資料夾:{{ number_format($dirCount ?? 0) }}
@endif {{-- OWASP 分佈 --}} @if(!empty($byOwasp))
OWASP Top 10
@foreach($owaspLabels as $oid => $olabel) @php $cnt = $byOwasp[$oid] ?? 0; @endphp
{{ $oid }} {{ $olabel }} @if($cnt>0) {{ $cnt }} @else - @endif
@endforeach
@endif {{-- 發現清單 --}}
@foreach(['critical'=>['danger','嚴重'],'high'=>['warning','高'],'medium'=>['info','中'],'low'=>['primary','低'],'info'=>['secondary','資訊']] as $sev=>[$col,$lbl]) @if(($sevCounts[$sev]??0)>0) @endif @endforeach
@if(empty($findings))
未發現任何問題,源碼安全狀態良好
@else
@foreach($findings as $i => $f) @php $sev = $f['severity'] ?? 'info'; $sevColor = ['critical'=>'danger','high'=>'warning','medium'=>'info','low'=>'primary','info'=>'secondary'][$sev] ?? 'secondary'; $issueType = strtolower($f['type'] ?? 'vulnerability'); $typeLabel = ['vulnerability'=>'漏洞','bug'=>'程式錯誤','code_smell'=>'程式碼異味','security_hotspot'=>'安全熱點'][$issueType] ?? $issueType; $typeColor = ['vulnerability'=>'danger','bug'=>'warning','code_smell'=>'secondary','security_hotspot'=>'info'][$issueType] ?? 'secondary'; $searchText = strtolower(($f['name']??'').' '.($f['file']??'').' '.($f['matched_at']??'').' '.($f['template_id']??'').' '.implode(' ',(array)($f['owasp']??[])).' '.implode(' ',(array)($f['cve_id']??[]))); @endphp @endforeach
嚴重度 問題描述 檔案位置 OWASP / CVE
{{ strtoupper($sev) }} @if($codeScan->scanner_type === 'sonarqube')
{{ $typeLabel }} @endif
{{ Str::limit($f['name'] ?? '—', 80) }}
@if(!empty($f['template_id']))
{{ $f['template_id'] }}
@endif
{{ $f['file'] ?? $f['matched_at'] ?? '—' }} @if(!empty($f['line'])) :{{ $f['line'] }} @endif
@if(!empty($f['code'])) {{ $f['code'] }} @endif
@foreach((array)($f['owasp']??[]) as $oid) {{ $oid }} @endforeach @foreach((array)($f['cve_id']??[]) as $cid) {{ $cid }} @endforeach
{{-- 欄1:檔案位置 + 程式碼片段 --}}
檔案位置
{{ $f['file'] ?? $f['matched_at'] ?? '—' }} @if(!empty($f['line'])) 第 {{ $f['line'] }} 行 @endif {{-- 程式碼片段 --}} @if(!empty($f['code']))
程式碼片段
{{ $f['code'] }}
@elseif($codeScan->scanner_type === 'sonarqube' && !empty($f['file']) && !empty($f['line']))
程式碼片段

                        
@endif
{{-- 欄2:問題說明 --}}
問題說明

{{ $f['description'] ?: $f['name'] }}

{{-- 欄3:解決方式 --}}
解決方式
@php $ruleKey = $f['extra']['rule'] ?? ''; $ruleDetail = $sonarRules[$ruleKey] ?? null; @endphp @if(!empty($f['remediation']))
{{ $f['remediation'] }}
@elseif($ruleDetail) {{-- SonarQube 官方建議:直接顯示中文說明 --}} @foreach($ruleDetail['sections'] as $sec) @if(in_array($sec['label'], ['問題根因','修復方式']))
{{ $sec['label'] }}
@if(!empty($sec['zh'])) {{ $sec['zh'] }} @endif
@endif @endforeach @if($ruleDetail['effort'])
修復耗時 {{ $ruleDetail['effort'] }}
@endif @elseif(!empty($f['extra']['fix']))
{{ $f['extra']['fix'] }}
@else
尚無建議
@endif
{{-- 欄4:參考資料 + 分類 --}}
@php // SonarQube:優先用 rule 的外部參考連結,過濾掉 localhost $displayRefs = []; if ($codeScan->scanner_type === 'sonarqube' && !empty($ruleDetail['references'])) { $displayRefs = $ruleDetail['references']; } else { foreach ((array)($f['reference'] ?? []) as $ref) { if (!str_contains($ref, 'localhost')) { $displayRefs[] = ['url' => $ref, 'label' => $ref]; } } } @endphp @if(!empty($displayRefs))
參考資料
@foreach(array_slice($displayRefs, 0, 5) as $ref) @php $refUrl = is_array($ref) ? $ref['url'] : $ref; $refLabel = is_array($ref) ? $ref['label'] : $ref; @endphp {{ $refLabel }} @endforeach @endif @if(!empty($f['owasp']))
OWASP
@foreach((array)$f['owasp'] as $oid) {{ $oid }} @endforeach @endif @if(!empty($f['extra']['cwe']))
CWE
@foreach((array)$f['extra']['cwe'] as $c) {{ $c }} @endforeach @endif
@if(!empty($f['extra']['cvss_score'])) CVSS:{{ $f['extra']['cvss_score'] }}
@endif @if(!empty($f['extra']['package'])) 套件:{{ $f['extra']['package'] }}@{{ $f['extra']['installed_ver']??'' }}@if(!empty($f['extra']['fixed_ver'])) → {{ $f['extra']['fixed_ver'] }}@endif
@endif @if(!empty($f['extra']['rule_id'])) Rule:{{ $f['extra']['rule_id'] }}
@endif
@endif
@endif
@endsection @push('scripts') @endpush