98 lines
3.3 KiB
PHP
98 lines
3.3 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Http\Controllers;
|
||
|
|
|
||
|
|
use App\Models\Investigation;
|
||
|
|
use Illuminate\Http\JsonResponse;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Http\Response;
|
||
|
|
use Illuminate\View\View;
|
||
|
|
|
||
|
|
class InvestigationReportController extends Controller
|
||
|
|
{
|
||
|
|
public function show(Request $request, Investigation $investigation): View
|
||
|
|
{
|
||
|
|
$this->authorize('view', $investigation);
|
||
|
|
|
||
|
|
$investigation->load(['owner', 'targets.creator', 'targets.findings.source']);
|
||
|
|
|
||
|
|
return view('investigations.report', compact('investigation'));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function csv(Request $request, Investigation $investigation): Response
|
||
|
|
{
|
||
|
|
$this->authorize('view', $investigation);
|
||
|
|
|
||
|
|
$investigation->load(['targets.findings.source']);
|
||
|
|
|
||
|
|
$rows = ["target_type,target_value,source,status,summary,ran_at\n"];
|
||
|
|
|
||
|
|
foreach ($investigation->targets as $target) {
|
||
|
|
foreach ($target->findings as $finding) {
|
||
|
|
$rows[] = implode(',', array_map(
|
||
|
|
fn ($v) => '"'.str_replace('"', '""', $this->csvSafe($v)).'"',
|
||
|
|
[
|
||
|
|
$target->type,
|
||
|
|
$target->value,
|
||
|
|
$finding->source->name,
|
||
|
|
$finding->status,
|
||
|
|
$finding->summary,
|
||
|
|
$finding->ran_at,
|
||
|
|
]
|
||
|
|
))."\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$filename = 'investigation-'.$investigation->id.'-findings.csv';
|
||
|
|
|
||
|
|
return response(implode('', $rows), 200, [
|
||
|
|
'Content-Type' => 'text/csv',
|
||
|
|
'Content-Disposition' => "attachment; filename=\"{$filename}\"",
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Prefix values that Excel/Sheets would interpret as a formula, to
|
||
|
|
* prevent CSV injection via user-controlled target values or finding
|
||
|
|
* summaries pulled from external tool output.
|
||
|
|
*/
|
||
|
|
private function csvSafe(mixed $value): string
|
||
|
|
{
|
||
|
|
$value = (string) $value;
|
||
|
|
|
||
|
|
return preg_match('/^[=+\-@\t\r]/', $value) ? "'".$value : $value;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function json(Request $request, Investigation $investigation): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorize('view', $investigation);
|
||
|
|
|
||
|
|
$investigation->load(['owner', 'targets.creator', 'targets.findings.source']);
|
||
|
|
|
||
|
|
$filename = 'investigation-'.$investigation->id.'.json';
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'name' => $investigation->name,
|
||
|
|
'description' => $investigation->description,
|
||
|
|
'status' => $investigation->status,
|
||
|
|
'owner' => $investigation->owner->name,
|
||
|
|
'created_at' => $investigation->created_at,
|
||
|
|
'targets' => $investigation->targets->map(fn ($target) => [
|
||
|
|
'type' => $target->type,
|
||
|
|
'value' => $target->value,
|
||
|
|
'notes' => $target->notes,
|
||
|
|
'created_by' => $target->creator->name,
|
||
|
|
'findings' => $target->findings->map(fn ($finding) => [
|
||
|
|
'source' => $finding->source->name,
|
||
|
|
'status' => $finding->status,
|
||
|
|
'summary' => $finding->summary,
|
||
|
|
'raw_payload' => $finding->raw_payload,
|
||
|
|
'ran_at' => $finding->ran_at,
|
||
|
|
]),
|
||
|
|
]),
|
||
|
|
])->withHeaders([
|
||
|
|
'Content-Disposition' => "attachment; filename=\"{$filename}\"",
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|