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
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:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user