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; # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# CSP: allow self + inline styles (React build) + data: URIs for fonts/images # 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 # Hide Nginx version
server_tokens off; server_tokens off;

View File

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