Skip to content

Commit 9abfc4a

Browse files
Alex-AlanizMarfuen
andauthored
fix(cloud-tests): improve error messages and user feedback (#1703)
* fix(cloud-tests): improve error messages and user feedback - Extract clean messages from GCP/Azure error responses - Add info banner explaining propagation delays (12-24h) for empty results - Auto-dismiss error banners after 30 seconds Fixes JSON blob display in GCP errors, adds context for empty scan results, and prevents error banner clutter. * fix(cloud-tests): update propagation delay timing to 24-48 hours Updated propagation delay messaging from 12-24 hours to 24-48 hours to better reflect real-world cloud security service propagation times. Also removed "or run another scan" text as rescanning doesn't affect propagation delays. --------- Co-authored-by: Mariano Fuentes <[email protected]>
1 parent dd6ed6e commit 9abfc4a

File tree

1 file changed

+53
-3
lines changed

1 file changed

+53
-3
lines changed

apps/app/src/app/(app)/[orgId]/cloud-tests/components/ResultsView.tsx

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { Button } from '@comp/ui/button';
44
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@comp/ui/select';
55
import { useRealtimeRun } from '@trigger.dev/react-hooks';
6-
import { CheckCircle2, Loader2, RefreshCw, X } from 'lucide-react';
6+
import { CheckCircle2, Info, Loader2, RefreshCw, X } from 'lucide-react';
77
import { useEffect, useState } from 'react';
88
import { FindingsTable } from './FindingsTable';
99

@@ -27,6 +27,23 @@ interface ResultsViewProps {
2727

2828
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
2929

30+
// Helper function to extract clean error messages from cloud provider errors
31+
function extractCleanErrorMessage(errorMessage: string): string {
32+
try {
33+
// Try to parse as JSON (GCP returns JSON blob)
34+
const parsed = JSON.parse(errorMessage);
35+
36+
// GCP error structure: { error: { message: "actual message" } }
37+
if (parsed.error?.message) {
38+
return parsed.error.message;
39+
}
40+
} catch {
41+
// Not JSON, return original
42+
}
43+
44+
return errorMessage;
45+
}
46+
3047
export function ResultsView({
3148
findings,
3249
scanTaskId,
@@ -46,6 +63,7 @@ export function ResultsView({
4663
const [selectedStatus, setSelectedStatus] = useState<string>('all');
4764
const [selectedSeverity, setSelectedSeverity] = useState<string>('all');
4865
const [showSuccessBanner, setShowSuccessBanner] = useState(false);
66+
const [showErrorBanner, setShowErrorBanner] = useState(false);
4967

5068
// Show success banner when scan completes, auto-hide after 5 seconds
5169
useEffect(() => {
@@ -58,6 +76,17 @@ export function ResultsView({
5876
}
5977
}, [scanCompleted]);
6078

79+
// Auto-dismiss error banner after 30 seconds
80+
useEffect(() => {
81+
if (scanFailed) {
82+
setShowErrorBanner(true);
83+
const timer = setTimeout(() => {
84+
setShowErrorBanner(false);
85+
}, 30000);
86+
return () => clearTimeout(timer);
87+
}
88+
}, [scanFailed]);
89+
6190
// Get unique statuses and severities
6291
const uniqueStatuses = Array.from(
6392
new Set(findings.map((f) => f.status).filter(Boolean) as string[]),
@@ -117,15 +146,36 @@ export function ResultsView({
117146
</div>
118147
)}
119148

120-
{scanFailed && !isScanning && (
149+
{/* Propagation delay info banner - only when scan succeeds but returns empty output */}
150+
{scanCompleted && findings.length === 0 && !isScanning && !scanFailed && (
151+
<div className="bg-blue-50 dark:bg-blue-950/20 flex items-center gap-3 rounded-lg border border-blue-200 dark:border-blue-900 p-4">
152+
<Info className="text-blue-600 dark:text-blue-400 h-5 w-5 flex-shrink-0" />
153+
<div className="flex-1">
154+
<p className="text-blue-900 dark:text-blue-100 text-sm font-medium">Initial scan complete</p>
155+
<p className="text-muted-foreground text-xs">
156+
Security findings may take 24-48 hours to appear after enabling cloud security services. Check back later.
157+
</p>
158+
</div>
159+
</div>
160+
)}
161+
162+
{showErrorBanner && scanFailed && !isScanning && (
121163
<div className="bg-destructive/10 flex items-center gap-3 rounded-lg border border-destructive/20 p-4">
122164
<X className="text-destructive h-5 w-5 flex-shrink-0" />
123165
<div className="flex-1">
124166
<p className="text-destructive text-sm font-medium">Scan failed</p>
125167
<p className="text-muted-foreground text-xs">
126-
{run?.error?.message || 'An error occurred during the scan. Please try again.'}
168+
{extractCleanErrorMessage(run?.error?.message || 'An error occurred during the scan. Please try again.')}
127169
</p>
128170
</div>
171+
<Button
172+
variant="ghost"
173+
size="sm"
174+
onClick={() => setShowErrorBanner(false)}
175+
className="text-muted-foreground hover:text-foreground h-auto p-1"
176+
>
177+
<X className="h-4 w-4" />
178+
</Button>
129179
</div>
130180
)}
131181

0 commit comments

Comments
 (0)