@php $_pdfLogo = null; foreach (['png','svg','jpg','jpeg'] as $_ext) { $_path = public_path('logo.'.$_ext); if (file_exists($_path)) { $_mime = $_ext === 'svg' ? 'image/svg+xml' : 'image/'.$_ext; $_pdfLogo = 'data:'.$_mime.';base64,'.base64_encode(file_get_contents($_path)); break; } } @endphp @php $bySev = $summary['by_severity'] ?? []; $byOwasp = $summary['by_owasp'] ?? []; $total = $summary['total'] ?? 0; $critical = $bySev['critical'] ?? 0; $high = $bySev['high'] ?? 0; $medium = $bySev['medium'] ?? 0; $low = $bySev['low'] ?? 0; $info = $bySev['info'] ?? 0; $fileCount = $summary['file_count'] ?? null; $dirCount = $summary['dir_count'] ?? null; // 整體風險等級 if ($critical > 0) { $riskLevel = 'CRITICAL'; $riskClass = 'critical'; $riskDesc = "本次掃描發現 {$critical} 個嚴重等級(Critical)漏洞,程式碼面臨極高安全風險,攻擊者可能直接利用這些漏洞取得系統控制權或竊取敏感資料。建議立即停機處理。"; } elseif ($high > 0) { $riskLevel = 'HIGH'; $riskClass = 'high'; $riskDesc = "本次掃描發現 {$high} 個高等級(High)漏洞,程式碼存在重大安全風險。若未及時修復,攻擊者可能造成資料洩漏或服務異常。建議於 7 天內完成修補。"; } elseif ($medium > 0) { $riskLevel = 'MEDIUM'; $riskClass = 'medium'; $riskDesc = "本次掃描發現 {$medium} 個中等等級(Medium)漏洞,程式碼存在一定安全風險。建議於 30 天內評估並完成修補,避免漏洞被組合利用。"; } elseif ($low > 0) { $riskLevel = 'LOW'; $riskClass = 'low'; $riskDesc = "本次掃描結果整體風險偏低,僅發現 {$low} 個低等級(Low)問題。建議於例行維護週期中一併修復。"; } else { $riskLevel = 'INFO'; $riskClass = 'info'; $riskDesc = "本次掃描未發現明顯安全問題,所有發現皆為資訊性項目。系統整體程式碼安全狀態良好,建議持續定期執行掃描。"; } // 掃描工具資訊 $scannerMeta = [ 'semgrep' => ['label' => 'Semgrep SAST', 'ver' => 'Latest', 'color' => '#3b82f6', 'type' => 'SAST 靜態分析'], 'trivy' => ['label' => 'Trivy SCA', 'ver' => 'Latest', 'color' => '#06b6d4', 'type' => 'SCA 依賴套件掃描'], 'sonarqube' => ['label' => 'SonarQube', 'ver' => 'Community', 'color' => '#f59e0b', 'type' => '程式碼品質+安全分析'], ][$codeScan->scanner_type] ?? ['label' => $codeScan->scanner_type, 'ver' => '', 'color' => '#6b7280', 'type' => '']; $sec = $codeScan->durationSeconds(); $duration = $sec ? ($sec >= 60 ? floor($sec/60).'m '.($sec%60).'s' : $sec.'s') : '—'; // 來源顯示 $sourceRef = parse_url($codeScan->source_ref ?? '', PHP_URL_PATH) ?: ($codeScan->source_ref ?? '—'); // OWASP Top 10 2021 $owaspDetail = [ 'A01:2021' => ['name'=>'存取控制失效', 'desc'=>'使用者被允許執行超出其預期權限的操作,可能導致未授權的資料讀取、修改或刪除。'], 'A02:2021' => ['name'=>'加密機制失效', 'desc'=>'與加密相關的失敗,通常導致敏感資料暴露。包括明文傳輸、弱加密演算法、不安全的金鑰管理。'], 'A03:2021' => ['name'=>'注入式攻擊', 'desc'=>'攻擊者向直譯器發送惡意資料,如 SQL、OS 命令、XSS。可能導致資料竊取或系統入侵。'], 'A04:2021' => ['name'=>'不安全設計', 'desc'=>'設計階段缺乏安全考量,包括缺乏威脅建模、不安全的設計模式。'], 'A05:2021' => ['name'=>'安全設定錯誤', 'desc'=>'不安全的預設設定、不完整設定、不必要的功能啟用等。'], 'A06:2021' => ['name'=>'易受攻擊與過時的元件', 'desc'=>'使用有已知漏洞的函式庫、框架或軟體模組,可能破壞應用程式防禦。'], 'A07:2021' => ['name'=>'身分驗證失效', 'desc'=>'與身分驗證和工作階段管理相關的弱點,允許攻擊者入侵密碼或工作階段令牌。'], 'A08:2021' => ['name'=>'軟體及資料完整性失效', 'desc'=>'未驗證完整性的軟體更新、關鍵資料及 CI/CD 管道,如不安全的反序列化。'], 'A09:2021' => ['name'=>'安全記錄及監控失效', 'desc'=>'缺乏足夠的記錄和監控,導致無法偵測攻擊,使攻擊者能長期潛伏系統中。'], 'A10:2021' => ['name'=>'伺服器端請求偽造', 'desc'=>'應用程式在未驗證使用者提供 URL 的情況下發送請求,攻擊者可存取內部服務。'], ]; $sevColors = ['critical'=>'#dc2626','high'=>'#ea580c','medium'=>'#d97706','low'=>'#16a34a','info'=>'#475569']; $sevDescs = [ 'critical'=>'可直接被遠端利用,無需授權即可取得系統控制權。建議立即(24H內)修復。', 'high' =>'可能造成資料洩漏、身分驗證繞過等重大損害。建議 7 天內修復。', 'medium' =>'單獨利用危害有限,但可與其他漏洞組合使用。建議 30 天內修復。', 'low' =>'危害程度較低,通常為設定不當或資訊暴露。建議例行維護時修復。', 'info' =>'非直接威脅,主要為技術指紋識別,可輔助了解系統暴露面。', ]; @endphp {{-- ══════════════ 封面頁 ══════════════ --}}
@if($_pdfLogo) {{ config('app.company_name') }} @else @endif
@if(!$_pdfLogo)
{{ config('app.company_name') }}
@endif
資安檢測服務
CONFIDENTIAL
{{-- 源碼掃描工具圖示 --}} @if($codeScan->scanner_type === 'trivy') @elseif($codeScan->scanner_type === 'sonarqube') @else </> @endif
{{ $scannerMeta['label'] }}
{{ $scannerMeta['ver'] }}
Source Code Security Assessment
源碼掃描報告
Static Code Analysis Report

Prepared for  ·  委託單位
{{ $project?->client?->name ?? $project?->name ?? '(未指定委託單位)' }}

Source  ·  掃描來源
{{ $sourceRef }}
Project  ·  所屬專案
{{ $project?->name ?? '—' }}
Language  ·  主要語言
{{ $codeScan->language ?? '—' }}
Scan Time  ·  掃描時間
{{ $codeScan->started_at?->format('H:i') ?? '—' }} @if($codeScan->finished_at) – {{ $codeScan->finished_at->format('H:i') }} @endif
@if($duration !== '—')
歷時 {{ $duration }}
@endif

Report Date  ·  報告產生日期
{{ now()->format('Y 年 m 月 d 日') }}
掃描類型:{{ $scannerMeta['type'] }}
掃描日期:{{ $codeScan->started_at?->format('Y-m-d') ?? '—' }}
Overall Risk Level  ·  整體風險等級
{{ $riskLevel }}
共 {{ $total }} 筆   C {{ $critical }} H {{ $high }} M {{ $medium }} L {{ $low }} I {{ $info }}
@if($fileCount !== null)
📄 {{ number_format($fileCount) }} 個檔案  ·  📁 {{ number_format($dirCount ?? 0) }} 個資料夾
@endif
{{-- ══════════════ 正文頁 ══════════════ --}}
{{-- 一、執行摘要 --}}
一、執行摘要
整體風險等級:{{ $riskLevel }}
{{ $riskDesc }}

本次使用 {{ $scannerMeta['label'] }} 掃描引擎({{ $scannerMeta['type'] }}), 針對來源 {{ $sourceRef }} 進行自動化源碼安全檢測。 @if($codeScan->language) 主要程式語言:{{ $codeScan->language }}。@endif 掃描作業於 {{ $codeScan->started_at?->format('Y年m月d日 H:i') }} 開始, 歷時 {{ $duration }} 完成,涵蓋 OWASP Top 10 等主流漏洞類型。 @if($fileCount !== null) 本次共掃描 {{ number_format($fileCount) }} 個檔案、{{ number_format($dirCount ?? 0) }} 個資料夾。 @endif

{{ $critical }}
CRITICAL
嚴重
{{ $high }}
HIGH
高風險
{{ $medium }}
MEDIUM
中風險
{{ $low }}
LOW
低風險
{{ $info }}
INFO
資訊
{{-- 二、風險等級說明 --}}
二、風險等級說明
Critical嚴重漏洞:可直接被遠端利用,無需授權即可取得系統控制權或存取敏感資料。如 SQL Injection、RCE、硬寫死密碼等。建議立即修復(24H內)
High 高風險漏洞:可能造成重大損害,如資料洩漏、身分驗證繞過、不安全的依賴套件(已知 CVE)。建議 7 天內修復
Medium 中等風險:單獨利用危害有限,但可能組合利用。常見如 XSS、CSRF、資訊洩漏、不安全的設定。建議 30 天內修復
Low 低風險:危害程度較低,通常為設定不當或資訊暴露問題。建議於例行維護時修復
Info 資訊性:非直接安全威脅,主要為程式碼品質問題或技術指紋識別,可輔助了解系統狀態。
{{-- 三、主要發現摘要 --}} @php $nonInfo = array_filter($findings, fn($f) => ($f['severity'] ?? 'info') !== 'info'); $topFindings = array_slice(array_values($nonInfo) ?: $findings, 0, 10); @endphp
三、主要發現摘要(Top {{ count($topFindings) }})
@if(empty($topFindings))

本次掃描未發現高風險問題,所有發現均為資訊性項目。

@else @foreach($topFindings as $i => $f) @php $sev = $f['severity'] ?? 'info'; @endphp
{{ strtoupper($sev) }}  {{ Str::limit($f['name'] ?? $f['template_id'] ?? '—', 80) }} @if(!empty($f['file']))  → {{ $f['file'] }}{{ !empty($f['line']) ? ':'.$f['line'] : '' }} @endif @if(!empty($f['description']))
{{ mb_strimwidth($f['description'], 0, 150, '…') }}
@endif
@endforeach @endif
{{-- 四、OWASP Top 10 分佈 --}} @if(!empty($byOwasp))
四、OWASP Top 10 風險分佈

OWASP Top 10 是國際公認的十大 Web 應用程式安全風險清單,以下為本次掃描結果與 OWASP 分類的對應關係。

@foreach($owaspLabels as $oid => $olabel) @php $cnt = $byOwasp[$oid] ?? 0; @endphp @endforeach
分類 ID 風險名稱 發現數 狀態
{{ $oid }} {{ $olabel }} {{ $cnt > 0 ? $cnt : '—' }} @if($cnt > 0) ⚠ 發現 @else ✓ 未發現 @endif
@endif {{-- 五、修補建議優先順序 --}} @php $fixFindings = array_filter($findings, function($f) use ($sonarRules) { if (!in_array($f['severity'] ?? '', ['critical','high','medium','low'])) return false; if (!empty($f['remediation'])) return true; $rk = $f['extra']['rule'] ?? ''; return !empty($sonarRules[$rk]); }); @endphp @if(!empty($fixFindings))
五、修補建議優先順序
@php $rank = 1; @endphp @foreach($findings as $f) @php $fSev = $f['severity'] ?? ''; if (!in_array($fSev, ['critical','high','medium','low'])) continue; $fRuleKey = $f['extra']['rule'] ?? ''; $fRuleDetail = $sonarRules[$fRuleKey] ?? null; $fRemedText = $f['remediation'] ?? ''; if (empty($fRemedText) && $fRuleDetail) { $parts = []; foreach ($fRuleDetail['sections'] as $sec) { if (in_array($sec['label'], ['問題根因','修復方式']) && !empty($sec['zh'])) { $parts[] = $sec['zh']; } } $fRemedText = implode(' ', $parts); } if (empty($fRemedText)) continue; @endphp @if($rank > 30) @break @endif @endforeach
優先 嚴重度 問題名稱 檔案位置 建議修復措施
{{ $rank++ }} {{ strtoupper($fSev) }} {{ Str::limit($f['name'] ?? '—', 50) }} {{ Str::limit($f['file'] ?? $f['matched_at'] ?? '—', 40) }}{{ !empty($f['line']) ? ':'.$f['line'] : '' }} {{ $fRemedText }}
@endif {{-- 六、詳細發現清單 --}}
{{ !empty($fixFindings) ? '六' : '五' }}、詳細發現清單(共 {{ $total }} 筆)
@forelse($findings as $i => $f) @php $sev = $f['severity'] ?? 'info'; $sevColor = $sevColors[$sev] ?? '#475569'; $fOwasp = (array)($f['owasp'] ?? []); $fCve = (array)($f['cve_id'] ?? []); $fRef = (array)($f['reference'] ?? []); $descText = $f['description'] ?? $f['name'] ?? ''; $ruleKey = $f['extra']['rule'] ?? ''; $ruleDetail = $sonarRules[$ruleKey] ?? null; $remedText = $f['remediation'] ?? ($f['extra']['fix'] ?? ''); if (empty($remedText) && $ruleDetail) { // 從 SonarQube rule 中文說明組成解決方式文字 $parts = []; foreach ($ruleDetail['sections'] as $sec) { if (in_array($sec['label'], ['問題根因', '修復方式']) && !empty($sec['zh'])) { $parts[] = $sec['label'] . ':' . $sec['zh']; } } $remedText = implode("\n", $parts); } // SonarQube 程式碼片段(預先抓取的) $snippetKey = ($f['file'] ?? '') . ':' . ($f['line'] ?? ''); $codeSnippet = $sourceSnippets[$snippetKey] ?? null; $codeText = !empty($f['code']) ? $f['code'] : null; @endphp
{{ strtoupper($sev) }}
#{{ str_pad($i+1, 3, '0', STR_PAD_LEFT) }}
{{ Str::limit($f['name'] ?? '—', 90) }}
{{ $f['template_id'] ?? '' }} @foreach($fCve as $cid)  {{ $cid }} @endforeach @foreach($fOwasp as $oid)  {{ $oid }} @endforeach
{{-- 檔案位置 --}}
📄 檔案位置
{{ $f['file'] ?? $f['matched_at'] ?? '—' }}{{ !empty($f['line']) ? ':'.$f['line'] : '' }} @if(!empty($f['line_end']) && $f['line_end'] != $f['line']) –{{ $f['line_end'] }} @endif
{{-- 四欄詳細:檔案+程式碼 ╱ 問題說明 ╱ 解決方式 ╱ 參考資料 --}}
{{-- A: 檔案位置 + 程式碼片段 --}}
📄 檔案位置
{{ $f['file'] ?? $f['matched_at'] ?? '—' }} @if(!empty($f['line'])) :{{ $f['line'] }} @endif
{{-- 程式碼片段 --}} @if($codeText || $codeSnippet)
程式碼片段
@if($codeText) @foreach(explode("\n", $codeText) as $lineIdx => $lineStr) @endforeach @else @foreach($codeSnippet as $sl) @endforeach @endif
{{ $lineIdx + 1 }} {{ $lineStr }}
{{ $sl['line'] }} {{ $sl['isHighlight'] ? '▶ ' : '' }}{{ $sl['code'] }}
@endif {{-- OWASP --}} @if(!empty($fOwasp))
@foreach($fOwasp as $oid) @php $od = $owaspDetail[$oid] ?? null; @endphp
{{ $oid }} {{ $od['name'] ?? '' }}
@endforeach
@endif
{{-- B: 問題說明 --}}
ℹ 問題說明
{{ mb_strimwidth($descText, 0, 350, '…') }}
@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
{{-- C: 解決方式 --}}
🔧 解決方式
@if(!empty($remedText))
{{ $remedText }}
@else
請參考右側參考連結
@endif @if(!empty($f['extra']['rule_id']))
Rule:{{ $f['extra']['rule_id'] }}
@endif
{{-- D: 參考資料 --}}
🔗 參考資料
@if(!empty($fCve))
CVE
@foreach($fCve as $cid)
{{ $cid }}
@endforeach @endif @if(!empty($f['extra']['cwe']))
CWE
@foreach((array)$f['extra']['cwe'] as $c)
{{ $c }}
@endforeach @endif @php $pdfRefs = []; if (!empty($ruleDetail['references'])) { $pdfRefs = $ruleDetail['references']; } else { foreach ($fRef as $ref) { if (!str_contains($ref, 'localhost')) $pdfRefs[] = ['url' => $ref, 'label' => $ref]; } } @endphp @if(!empty($pdfRefs))
參考連結
@foreach(array_slice($pdfRefs, 0, 4) as $ref) @php $rUrl = is_array($ref) ? $ref['url'] : $ref; $rLabel = is_array($ref) ? $ref['label'] : $ref; @endphp @endforeach @endif @if(!empty($f['extra']['rule_id']))
Rule: {{ $f['extra']['rule_id'] }}
@endif @if(!empty($f['tags']))
@foreach(array_slice((array)$f['tags'], 0, 5) as $tag) {{ $tag }} @endforeach
@endif
@empty

本次掃描未發現任何問題

@endforelse