fix(qa): CSP hash, remove pencil icon, fetch full template on modal open
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

- nginx.conf: add new CSP script-src hash (sha256-Yvj83pg...) alongside previous one
- SystemPage: remove pencil icon from template name button, keep cyan underline style
- SystemPage: switch from selectedTemplate state to selectedTemplateId + useQuery
  for getTemplateById() — ensures full template data (description, attack_procedure,
  expected_detection, tool_suggested etc.) loads before modal opens
- DB backfill already applied via SQL: UPDATE audit_logs SET timestamp = NOW()
  WHERE timestamp IS NULL (358 rows fixed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-05-19 12:53:02 +02:00
parent 63da22b77e
commit 7312f9664b
2 changed files with 32 additions and 18 deletions

View File

@@ -14,7 +14,7 @@ server {
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# CSP: allow self + inline styles (React build) + data: URIs for fonts/images
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-31OgE8E9uFi947Hj0TYz0o9NSyrQOewgXrj1ZPfYDaY='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-31OgE8E9uFi947Hj0TYz0o9NSyrQOewgXrj1ZPfYDaY=' 'sha256-Yvj83pg9TGSmhZQWii1NGmFCIaX9trnlTFVkemiMlS8='; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws: wss:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
# Hide Nginx version
server_tokens off;

View File

@@ -18,7 +18,6 @@ import {
ToggleRight,
BarChart3,
X,
Pencil,
} from "lucide-react";
import {
triggerMitreSync,
@@ -30,6 +29,7 @@ import {
import {
getTemplateStats,
getAllTemplates,
getTemplateById,
createTemplate,
updateTemplate,
toggleTemplateActive,
@@ -45,7 +45,7 @@ export default function SystemPage() {
const [intelResult, setIntelResult] = useState<IntelScanResponse | null>(null);
const [showCreateForm, setShowCreateForm] = useState(false);
const [bulkConfirm, setBulkConfirm] = useState<"activate" | "deactivate" | null>(null);
const [selectedTemplate, setSelectedTemplate] = useState<TestTemplate | null>(null);
const [selectedTemplateId, setSelectedTemplateId] = useState<string | null>(null);
// ── Existing queries ─────────────────────────────────────────────
const {
@@ -75,6 +75,15 @@ export default function SystemPage() {
queryFn: () => getAllTemplates({ limit: 200 }),
});
const {
data: selectedTemplate,
isLoading: selectedTemplateLoading,
} = useQuery({
queryKey: ["template-detail", selectedTemplateId],
queryFn: () => getTemplateById(selectedTemplateId!),
enabled: !!selectedTemplateId,
});
// ── Mutations ────────────────────────────────────────────────────
const mitreSyncMutation = useMutation({
mutationFn: triggerMitreSync,
@@ -125,10 +134,10 @@ export default function SystemPage() {
const updateTemplateMutation = useMutation({
mutationFn: ({ id, payload }: { id: string; payload: Partial<CreateTemplatePayload> }) =>
updateTemplate(id, payload),
onSuccess: (updated) => {
setSelectedTemplate(updated);
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["templates-admin"] });
queryClient.invalidateQueries({ queryKey: ["test-templates"] });
queryClient.invalidateQueries({ queryKey: ["template-detail", selectedTemplateId] });
},
});
@@ -481,12 +490,11 @@ export default function SystemPage() {
>
<td className="py-3 pr-4">
<button
onClick={() => setSelectedTemplate(tpl)}
className="flex items-center gap-1.5 text-left font-medium text-cyan-400 hover:text-cyan-300 truncate max-w-[200px] transition-colors"
onClick={() => setSelectedTemplateId(tpl.id)}
className="text-left font-medium text-cyan-400 hover:text-cyan-300 hover:underline truncate block max-w-[200px] transition-colors"
title="Click to view/edit"
>
<Pencil className="h-3 w-3 shrink-0 opacity-60" />
<span className="truncate">{tpl.name}</span>
{tpl.name}
</button>
</td>
<td className="py-3 px-4">
@@ -704,15 +712,21 @@ export default function SystemPage() {
</div>
{/* Template Detail Modal */}
{selectedTemplate && (
<TemplateDetailModal
template={selectedTemplate}
onClose={() => setSelectedTemplate(null)}
onSave={(id, payload) => updateTemplateMutation.mutate({ id, payload })}
onToggleActive={(id) => toggleActiveMutation.mutate(id)}
isSaving={updateTemplateMutation.isPending}
isToggling={toggleActiveMutation.isPending}
/>
{selectedTemplateId && (
selectedTemplateLoading ? (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60">
<Loader2 className="h-8 w-8 animate-spin text-cyan-400" />
</div>
) : selectedTemplate ? (
<TemplateDetailModal
template={selectedTemplate}
onClose={() => setSelectedTemplateId(null)}
onSave={(id, payload) => updateTemplateMutation.mutate({ id, payload })}
onToggleActive={(id) => toggleActiveMutation.mutate(id)}
isSaving={updateTemplateMutation.isPending}
isToggling={toggleActiveMutation.isPending}
/>
) : null
)}
</div>
);