Feat: Wire frontend to backend scan endpoint, replace UTXO report with findings

- Add GET /api/wallet/scan endpoint that shells out to detect.py
- Add CORS config and detect.py script path to application.properties
- walletService.js now calls the real scan endpoint instead of mock
- Replace UtxoCard-based ReportScreen with FindingCard-based layout
- FindingCard: collapsible card with data-driven details panel (address groups, string lists, key-value scalars)
- VulnerabilityBadge: all 14 finding types labeled, severity lowercased, critical style added
- ReportScreen: summary bar shows findings/warnings/txs analyzed; clean banner; separate warnings section
This commit is contained in:
LORDBABUINO
2026-02-27 02:00:39 -03:00
parent 78d335b571
commit ccc61d663e
10 changed files with 461 additions and 39 deletions
+4
View File
@@ -23,6 +23,10 @@ Content-Type: application/json
### Get UTXOs
GET {{baseUrl}}/api/wallet/{{analyze.response.body.$.analysisId}}/utxos
### Scan descriptor
# @name scan
GET {{baseUrl}}/api/wallet/scan?descriptor={{descriptor}}
> {%
client.test("status is 200", function() {
client.assert(response.status === 200, "expected 200");
@@ -1,20 +1,26 @@
package org.backend.stealth.controller;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.backend.stealth.mocks.WalletMockData;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ApplicationScoped
@Path("/api/wallet")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class WalletResource {
@ConfigProperty(name = "stealth.detect.script", defaultValue = "../../script/detect.py")
String detectScript;
private static final Map<String, String> sessions = new ConcurrentHashMap<>();
// DTOs
@@ -64,4 +70,35 @@ public class WalletResource {
}
return Response.ok(WalletMockData.buildReport(descriptor)).build();
}
@GET
@Path("/scan")
public Response scan(@QueryParam("descriptor") String descriptor) {
if (descriptor == null || descriptor.isBlank()) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(Map.of("error", "descriptor query parameter is required"))
.build();
}
try {
ProcessBuilder pb = new ProcessBuilder("python3", detectScript, descriptor);
pb.redirectErrorStream(false);
Process process = pb.start();
String output = new String(process.getInputStream().readAllBytes());
int exitCode = process.waitFor();
if (exitCode != 0 || output.isBlank()) {
String stderr = new String(process.getErrorStream().readAllBytes());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(Map.of("error", stderr.isBlank() ? "detect.py produced no output" : stderr.strip()))
.build();
}
return Response.ok(output).type(MediaType.APPLICATION_JSON).build();
} catch (Exception e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(Map.of("error", e.getMessage()))
.build();
}
}
}
@@ -0,0 +1,7 @@
quarkus.http.port=8080
quarkus.http.cors=true
quarkus.http.cors.origins=http://localhost:5173
quarkus.http.cors.methods=GET,POST,OPTIONS
quarkus.http.cors.headers=Content-Type,Accept
stealth.detect.script=../../script/detect.py