From 9f0eacd548d1afad26d79c17883cfa3976a6cd8e Mon Sep 17 00:00:00 2001 From: Florian Magin Date: Fri, 16 Jan 2026 17:42:32 +0100 Subject: [PATCH 1/2] Fix "View in Portal" UI action --- .../ghidra/core/services/api/GhidraRevengService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java b/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java index 1fa73c9..7f42ca7 100644 --- a/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java +++ b/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java @@ -910,7 +910,10 @@ public void openPortalFor(ProgramWithID programWithID) { } public void openPortalFor(TypedApiInterface.AnalysisID analysisID) { - openPortal("analyses", String.valueOf(analysisID.id())); + // The URL expects the old binary ID in the path, and then the analysis ID in the query + // if the binary ID is valid, it takes precedence, + // and we have to provide _something_ for the overall URL to be valid, but we can just pass a placeholder value + openPortal("analyses", String.format("placeholder?analysis-id=%s", analysisID.id())); } public void openPortal(String... subPath) { From 18020819b3a730e06666266e77c4d12c298e7fcd Mon Sep 17 00:00:00 2001 From: Florian Magin Date: Sat, 17 Jan 2026 14:54:36 +0100 Subject: [PATCH 2/2] Add helper to convert AnalysisID back to BinaryID --- .../services/api/GhidraRevengService.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java b/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java index 7f42ca7..39827f6 100644 --- a/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java +++ b/src/main/java/ai/reveng/toolkit/ghidra/core/services/api/GhidraRevengService.java @@ -171,6 +171,22 @@ private Optional getAnalysisIDFor(Program program) } return Optional.empty(); } + + /// This is a helper to get the AnalysisID from the BinaryID, in the rare cases that this is required + /// Currently the only known case is when opening the analysis on the portal in the browser + private Optional getBinaryIDFromAnalysisID(TypedApiInterface.AnalysisID analysisID) { + try { + var info = api.getAnalysisBasicInfo(analysisID); + var results = api.search(new TypedApiInterface.BinaryHash(info.getSha256Hash())); + var binaryId = results.stream().filter( r -> r.analysis_id().equals(analysisID)) + .findAny().map( r -> r.binary_id()); + return binaryId; + + } catch (ApiException e) { + throw new RuntimeException(e); + } + } + private Optional getAnalysisIDFromOptions( Program program ) { @@ -910,10 +926,13 @@ public void openPortalFor(ProgramWithID programWithID) { } public void openPortalFor(TypedApiInterface.AnalysisID analysisID) { + var binaryID = getBinaryIDFromAnalysisID(analysisID); // The URL expects the old binary ID in the path, and then the analysis ID in the query // if the binary ID is valid, it takes precedence, // and we have to provide _something_ for the overall URL to be valid, but we can just pass a placeholder value - openPortal("analyses", String.format("placeholder?analysis-id=%s", analysisID.id())); + openPortal("analyses", String.format("%s?analysis-id=%s", + binaryID.map(BinaryID::value).orElse(-1), + analysisID.id())); } public void openPortal(String... subPath) { @@ -1021,6 +1040,7 @@ public AnalysisStatus waitForFinishedAnalysis( public ProgramWithID startAnalysis(Program program, AnalysisOptionsBuilder analysisOptionsBuilder) throws ApiException { var analysisID = api.analyse(analysisOptionsBuilder); + return addAnalysisIDtoProgramOptions(program, analysisID); }