From dc1b9ae900092dfcaf0e4314fdf0bc28a1078a0a Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:59:51 -0500 Subject: [PATCH] GP-5238: from review GP-5238: adding a sanity check GP-5238: allow switching between frames GP-5238: comments GP-5238: fix for hv matches GP-5238: chooser for cutsom query GP-5238: delta working? GP-5238: first pass at delta GP-5238: oops GP-5238: key bindings GP-5238: better options + docs --- .../DecompilerTaint/DecompilerTaint.html | 19 +++- .../decompiler/taint/AbstractTaintState.java | 99 +++++++++++++++---- .../core/decompiler/taint/TaintLabel.java | 7 +- .../core/decompiler/taint/TaintOptions.java | 35 ++++--- .../core/decompiler/taint/TaintPlugin.java | 69 +++++++++++-- .../core/decompiler/taint/TaintProvider.java | 29 +++--- .../decompiler/taint/TaintQueryResult.java | 17 ++-- .../core/decompiler/taint/TaintService.java | 4 +- .../core/decompiler/taint/TaintState.java | 10 +- .../actions/TaintAbstractQueryAction.java | 6 +- .../taint/actions/TaintQueryAction.java | 4 +- .../taint/actions/TaintQueryCustomAction.java | 7 +- .../actions/TaintQueryDefaultAction.java | 7 +- .../taint/ctadl/TaintStateCTADL.java | 22 ++++- .../SarifTaintCodeFlowResultHandler.java | 6 +- .../taint/sarif/SarifTaintResultHandler.java | 76 +++++++++++++- .../decompiler/taint/DecompilerTaintTest.java | 5 +- .../src/main/java/sarif/SarifPlugin.java | 6 +- .../Sarif/src/main/java/sarif/SarifUtils.java | 41 +++++--- .../main/java/sarif/model/SarifDataFrame.java | 50 +++++----- 20 files changed, 393 insertions(+), 126 deletions(-) diff --git a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html index 3868a263b4..994eec89de 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html +++ b/Ghidra/Features/DecompilerDependent/src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html @@ -124,6 +124,17 @@

+
+

+ Force Direction

+

+ May be "forward", "backward", "all", or blank. Forward computes all source-to-sink + slices, backward sink-to-source slices, and all both. Blank chooses source-to-sink + or sink-to-source based on whether the number of sources or the number of sinks is + greater. +

+
+

Highlight Color

@@ -144,15 +155,17 @@

Output Format

- Currently always "sarif+all". + Describes the output set. Currently default to "sarif+all", which should be appropriate + for most cases. Other options are "sarif" (output paths only), "sarif+instructions" + (output paths and instructions), and "sarif+graphs" (output paths and graphs).

- Use all access paths

+ Match on Fields

- Use all access paths for sink/source variables. + Use all access paths, including field references, for sink/source variables.

diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java index 15ed6489aa..725821c531 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/AbstractTaintState.java @@ -51,7 +51,9 @@ public abstract class AbstractTaintState implements TaintState { private AddressSet taintAddressSet = new AddressSet(); private Map> taintVarnodeMap = new HashMap<>(); - /// private QueryDataFrame currentQueryData; + private AddressSet deltaAddressSet = new AddressSet(); + private Map> deltaVarnodeMap = new HashMap<>(); + private SarifSchema210 currentQueryData; protected TaintOptions taintOptions; @@ -59,6 +61,8 @@ public abstract class AbstractTaintState implements TaintState { private boolean cancellation; + private TaskType taskType; + public AbstractTaintState(TaintPlugin plugin) { this.plugin = plugin; } @@ -281,10 +285,15 @@ public abstract class AbstractTaintState implements TaintState { if (queryType.equals(QueryType.SRCSINK) || queryType.equals(QueryType.CUSTOM)) { // The datalog that specifies the query. - if (queryType.equals(QueryType.CUSTOM)) { + if (queryType.equals(QueryType.CUSTOM)) { Path queryPath = Path.of(taintOptions.getTaintOutputDirectory(), taintOptions.getTaintQueryDLName()); - queryFile = queryPath.toFile(); + GhidraFileChooser chooser = new GhidraFileChooser(plugin.getProvider().getComponent()); + chooser.setCurrentDirectory(queryPath.toFile()); + queryFile = chooser.getSelectedFile(); + if (queryFile == null) { + return false; + } } param_list.add(queryFile.getAbsolutePath()); } @@ -296,12 +305,8 @@ public abstract class AbstractTaintState implements TaintState { pb.redirectError(Redirect.INHERIT); Process p = pb.start(); - switch (taintOptions.getTaintOutputForm()) { - case "sarif+all": - readQueryResultsIntoDataFrame(program, p.getInputStream()); - break; - default: - } + readQueryResultsIntoDataFrame(program, p.getInputStream()); + // We wait for the process to finish after starting to read the input stream, // otherwise waitFor() might wait for a running process trying to write to // a filled output buffer. This causes waitFor() to wait indefinitely. @@ -401,25 +406,54 @@ public abstract class AbstractTaintState implements TaintState { @Override public void setTaintAddressSet(AddressSet aset) { - taintAddressSet = aset; + switch (taskType) { + case SET_TAINT -> taintAddressSet = aset; + case SET_DELTA -> deltaAddressSet = aset; + case APPLY_DELTA -> { + // Empty + } + } } @Override public AddressSet getTaintAddressSet() { - return taintAddressSet; + return switch(taskType) { + case SET_TAINT -> taintAddressSet; + case SET_DELTA -> deltaAddressSet; + case APPLY_DELTA -> taintAddressSet.subtract(deltaAddressSet); + default -> new AddressSet(); + }; + } + + @Override + public void setTaskType(TaskType taskType) { + this.taskType = taskType; } @Override public void augmentAddressSet(ClangToken token) { Address addr = token.getMinAddress(); - if (addr != null) { - taintAddressSet.add(addr); + if (addr == null) { + return; + } + switch (taskType) { + case SET_TAINT -> taintAddressSet.add(addr); + case SET_DELTA -> deltaAddressSet.add(addr); + case APPLY_DELTA -> { + if (!deltaAddressSet.contains(addr)) { + taintAddressSet.add(addr); + } + } } } @Override - public void setTaintVarnodeMap(Map> vmap) { - taintVarnodeMap = vmap; + public void setTaintVarnodeMap(Map> vmap, TaskType delta) { + switch (delta) { + case SET_TAINT -> taintVarnodeMap = vmap; + case SET_DELTA -> deltaVarnodeMap = vmap; + case APPLY_DELTA -> taintVarnodeMap = vmap; + } } @Override @@ -427,11 +461,42 @@ public abstract class AbstractTaintState implements TaintState { return taintVarnodeMap; } + @Override + public Set getQuerySet(Address key) { + Set set = new HashSet<>(); + switch (taskType) { + case SET_TAINT -> set = taintVarnodeMap.get(key); + case SET_DELTA -> set = deltaVarnodeMap.get(key); + case APPLY_DELTA -> { + Set A = taintVarnodeMap.get(key); + if (A != null) { + set.addAll(A); + } + Set B = deltaVarnodeMap.get(key); + if (A != null) { + set.removeAll(B); + } + } + } + return set; + } + @Override public void clearTaint() { Msg.info(this, "TaintState: clearTaint() - clearing address set"); - taintAddressSet.clear(); - taintVarnodeMap.clear(); + switch (taskType) { + case SET_TAINT -> { + taintAddressSet.clear(); + taintVarnodeMap.clear(); + } + case SET_DELTA -> { + deltaAddressSet.clear(); + deltaVarnodeMap.clear(); + } + case APPLY_DELTA -> { + // EMPTY + } + } } @Override diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java index 25a3be343a..c7c8bd90a3 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintLabel.java @@ -51,8 +51,11 @@ public class TaintLabel { } Varnode exactSpot = token.getVarnode(); - if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl - highVar = hfun.splitOutMergeGroup(exactSpot.getHigh(), exactSpot); + if (exactSpot != null) { // The user pointed at a particular usage, not just the vardecl + HighVariable high = exactSpot.getHigh(); + if (high instanceof HighLocal) { + highVar = hfun.splitOutMergeGroup(high, exactSpot); + } } String fn = token instanceof ClangFuncNameToken ftoken ? ftoken.getText() diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java index d9f3b26f6c..1f32ddf8b6 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintOptions.java @@ -18,7 +18,7 @@ package ghidra.app.plugin.core.decompiler.taint; import java.awt.Color; import generic.theme.GColor; -import ghidra.app.plugin.core.decompiler.taint.TaintPlugin.Highlighter; +import ghidra.app.plugin.core.decompiler.taint.TaintPlugin.*; import ghidra.app.util.HelpTopics; import ghidra.framework.options.ToolOptions; import ghidra.framework.plugintool.Plugin; @@ -45,12 +45,13 @@ public class TaintOptions { /* The default name of the index database file. */ public final static String OP_KEY_TAINT_DB = "Taint.Query.Index"; + public final static String OP_KEY_TAINT_QUERY_DIRECTION = "Taint.Force Direction"; public final static String OP_KEY_TAINT_QUERY_OUTPUT_FORM = "Taint.Output Format"; /* Color used in the decompiler to highlight taint. */ public final static String TAINT_HIGHLIGHT = "Taint.Highlight Color"; /* How to apply highlight taint. */ public final static String TAINT_HIGHLIGHT_STYLE = "Taint.Highlight Style"; - public final static String TAINT_ALL_ACCESS = "Taint.Use all access paths"; + public final static String TAINT_ALL_ACCESS = "Taint.Match on Fields"; private final static Boolean TAINT_ALL_ACCESS_PATHS = true; public final static String DEFAULT_TAINT_ENGINE_PATH = ""; @@ -60,7 +61,6 @@ public class TaintOptions { /* this is the text code that contains the datalog query the plugin writes. */ public final static String DEFAULT_TAINT_QUERY = "taintquery.dl"; public final static String DEFAULT_TAINT_DB = "ctadlir.db"; - public final static String DEFAULT_TAINT_OUTPUT_FORM = "sarif+all"; public final static Boolean DEFAULT_GET_PATHS = true; @@ -75,7 +75,8 @@ public class TaintOptions { private String taintQuery; private String taintDB; - private String taintQueryOutputForm; + private TaintDirection taintQueryDirection; + private TaintFormat taintQueryOutputForm; private Highlighter taintHighlightStyle; private Color taintHighlightColor; @@ -110,7 +111,8 @@ public class TaintOptions { taintOutputDir = DEFAULT_TAINT_OUTPUT_DIR; taintQuery = DEFAULT_TAINT_QUERY; taintDB = DEFAULT_TAINT_DB; - taintQueryOutputForm = DEFAULT_TAINT_OUTPUT_FORM; + taintQueryOutputForm = TaintFormat.ALL; + taintQueryDirection = TaintDirection.DEFAULT; taintUseAllAccess = TAINT_ALL_ACCESS_PATHS; } @@ -125,7 +127,7 @@ public class TaintOptions { */ public void registerOptions(Plugin ownerPlugin, ToolOptions opt, Program program) { - opt.registerOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, DEFAULT_TAINT_OUTPUT_FORM, + opt.registerOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, TaintFormat.ALL, new HelpLocation(HelpTopics.DECOMPILER, "Taint Output Type"), "The type of Source-Sink query output (e.g., sarif, summary, text"); @@ -181,7 +183,8 @@ public class TaintOptions { taintOutputDir = opt.getString(OP_KEY_TAINT_OUTPUT_DIR, ""); taintDB = opt.getString(OP_KEY_TAINT_DB, ""); - taintQueryOutputForm = opt.getString(OP_KEY_TAINT_QUERY_OUTPUT_FORM, ""); + taintQueryDirection = opt.getEnum(OP_KEY_TAINT_QUERY_DIRECTION, TaintDirection.DEFAULT); + taintQueryOutputForm = opt.getEnum(OP_KEY_TAINT_QUERY_OUTPUT_FORM, TaintFormat.ALL); taintHighlightStyle = opt.getEnum(TAINT_HIGHLIGHT_STYLE, TAINT_HIGHLIGHT_STYLE_DEFAULT); taintHighlightColor = opt.getColor(TAINT_HIGHLIGHT, TAINT_HIGHLIGHT_COLOR); @@ -189,7 +192,7 @@ public class TaintOptions { } - public String getTaintOutputForm() { + public TaintFormat getTaintOutputForm() { return taintQueryOutputForm; } @@ -233,13 +236,17 @@ public class TaintOptions { return taintHighlightStyle; } + public TaintDirection getTaintDirection() { + return taintQueryDirection; + } + public Boolean getTaintUseAllAccess() { return taintUseAllAccess; } - public void setTaintOutputForm(String form) { + public void setTaintOutputForm(TaintFormat form) { this.taintQueryOutputForm = form; - taintProvider.setOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, form); + taintProvider.setOption(OP_KEY_TAINT_QUERY_OUTPUT_FORM, form.getOptionString()); } public void setTaintFactsDirectory(String path) { @@ -269,12 +276,16 @@ public class TaintOptions { public void setTaintHighlightStyle(Highlighter style) { this.taintHighlightStyle = style; - taintProvider.setOption(TAINT_HIGHLIGHT_STYLE, style.name()); + taintProvider.setOption(TAINT_HIGHLIGHT_STYLE, style.getOptionString()); taintProvider.changeHighlighter(style); } + public void setTaintDirection(TaintDirection direction) { + this.taintQueryDirection = direction; + taintProvider.setOption(OP_KEY_TAINT_QUERY_DIRECTION, direction.getOptionString()); + } + public void setTaintAllAccess(Boolean allAccess) { this.taintUseAllAccess = allAccess; - taintProvider.setAllAccess(TAINT_ALL_ACCESS, allAccess); } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java index 6fb111c88f..00e7ba157e 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintPlugin.java @@ -34,6 +34,7 @@ import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.plugin.core.decompile.DecompilerProvider; import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; +import ghidra.app.plugin.core.decompiler.taint.TaintState.TaskType; import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraState; import ghidra.app.services.ConsoleService; @@ -89,16 +90,66 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { private TaintDecompilerMarginProvider taintDecompMarginProvider; public static enum Highlighter { - ALL("Taint Variables"), LABELS("Taint Labels"), DEFAULT("Default"); + ALL("all", "variables"), LABELS("labels", "labels"), DEFAULT("default", "default"); - private String name; + private String label; + private String optionString; - private Highlighter(String name) { - this.name = name; + private Highlighter(String optString, String label) { + this.label = label; + this.optionString = optString; } - public String getName() { - return this.name; + public String getOptionString() { + return optionString; + } + + @Override + public String toString() { + return label; + } + } + + public static enum TaintFormat { + ALL("all", "sarif+all"), GRAPHS("graphs", "sarif+graphs"), + INSTS("insts", "sarif+instructions"), PATHS("paths", "sarif"); + + private String label; + private String optionString; + + private TaintFormat(String optString, String label) { + this.label = label; + this.optionString = optString; + } + + public String getOptionString() { + return optionString; + } + + @Override + public String toString() { + return label; + } + } + + public static enum TaintDirection { + BOTH("all", "both"), FORWARD("fwd", "forward"), BACKWARD("bwd", "backward"), DEFAULT("auto", "auto"); + + private String label; + private String optionString; + + private TaintDirection(String optString, String label) { + this.label = label; + this.optionString = optString; + } + + public String getOptionString() { + return optionString; + } + + @Override + public String toString() { + return label; } } @@ -638,12 +689,12 @@ public class TaintPlugin extends ProgramPlugin implements TaintService { } @Override - public void setVarnodeMap(Map> vmap, boolean clear) { + public void setVarnodeMap(Map> vmap, boolean clear, TaskType delta) { if (clear) { taintProvider.clearTaint(); } - state.setTaintVarnodeMap(vmap); - taintProvider.setTaint(); + state.setTaintVarnodeMap(vmap, delta); + taintProvider.setTaint(delta); } @Override diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java index ce63ee2033..34b605fdbe 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintProvider.java @@ -34,6 +34,7 @@ import ghidra.app.nav.Navigatable; import ghidra.app.plugin.core.decompile.DecompilerActionContext; import ghidra.app.plugin.core.decompile.DecompilerProvider; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin.Highlighter; +import ghidra.app.plugin.core.decompiler.taint.TaintState.TaskType; import ghidra.app.plugin.core.decompiler.taint.actions.*; import ghidra.app.services.CodeViewerService; import ghidra.framework.options.OptionsChangeListener; @@ -64,8 +65,6 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh private DecompilerHighlighter highlighter; - private Boolean allAccess; - private TaintCTokenHighlighterPalette highlightPalette; private int paletteIndex; @@ -250,12 +249,15 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh *

* TODO: We could limit our taint addresses to those in this function...? TODO: * We should reset the palette cache to start coloring from the start. + * + * @param taskType subtract previous result */ - public void setTaint() { + public void setTaint(TaskType taskType) { if (navigatable == null) { navigatable = tool.getService(CodeViewerService.class).getNavigatable(); } + state.setTaskType(taskType); AddressSet taintAddressSet = state.getTaintAddressSet(); Msg.info(this, "setTaint(): " + taintAddressSet.toString()); @@ -274,14 +276,16 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh // apply highlights to the decompiler window. highlighter.applyHighlights(); + state.setTaskType(TaskType.SET_TAINT); + } + + public void setTaint() { + setTaint(TaskType.SET_TAINT); } public boolean matchOn(ClangToken token) { - Map> taintVarnodeMap = state.getTaintVarnodeMap(); - - if (taintVarnodeMap == null || taintVarnodeMap.isEmpty() || - token instanceof ClangBreak || + if (token instanceof ClangBreak || token instanceof ClangTypeToken || token instanceof ClangSyntaxToken || token instanceof ClangCommentToken) { @@ -298,7 +302,8 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh Address tokenFuncEntryAddr = hf.getFunction().getEntryPoint(); // Just the tainted elements that are in this function. - Set funcTaintSet = taintVarnodeMap.get(tokenFuncEntryAddr); + Set funcTaintSet = state.getQuerySet(tokenFuncEntryAddr); + //Set funcTaintSet = taintVarnodeMap.get(tokenFuncEntryAddr); if (funcTaintSet == null || funcTaintSet.isEmpty()) { return false; } @@ -458,14 +463,6 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh plugin.changeHighlighter(hl); } - public boolean isAllAccess() { - return allAccess; - } - - public void setAllAccess(String taintAllAccess, Boolean allAccess) { - this.allAccess = allAccess; - } - public int getTokenCount() { return matchCount; } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintQueryResult.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintQueryResult.java index 623bf9c620..1c5719bdc4 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintQueryResult.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintQueryResult.java @@ -41,8 +41,8 @@ public record TaintQueryResult(String name,String fqname, Address iaddr, Address public TaintQueryResult(Map result, Run run, LogicalLocation ll) { this( - SarifUtils.extractDisplayName(fqnFromLoc(run, ll)), - fqnFromLoc(run, ll).getFullyQualifiedName(), + SarifUtils.extractDisplayName(ll), + ll.getFullyQualifiedName(), (Address) result.get("Address"), (Address) result.get("entry"), new ArrayList(), @@ -51,14 +51,6 @@ public record TaintQueryResult(String name,String fqname, Address iaddr, Address addLabel(value); } - private static LogicalLocation fqnFromLoc(Run run, LogicalLocation ll) { - String fqn = ll.getFullyQualifiedName(); - if (fqn == null) { - ll = SarifUtils.getLogicalLocation(run, ll.getIndex()); - } - return ll; - } - public String getLabel() { return this.labels.get(0); } @@ -111,8 +103,11 @@ public record TaintQueryResult(String name,String fqname, Address iaddr, Address } else { // if neither are function-level, the addresses must match + // NB: parameter/local use matches on the representative if (!iaddr.equals(vaddr)) { - return null; + if (!(hv instanceof HighParam) || !iaddr.equals(hv.getRepresentative().getPCAddress())) { + return null; + } } } if (hvName.startsWith(":")) { // fqname is FUN@FUN:name:vname diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintService.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintService.java index ce1c0054c3..c7c88ce744 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintService.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintService.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.decompiler.taint; import java.util.Map; import java.util.Set; +import ghidra.app.plugin.core.decompiler.taint.TaintState.TaskType; import ghidra.framework.plugintool.ServiceInfo; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; @@ -56,8 +57,9 @@ public interface TaintService { * * @param vmap tainted addresses * @param clear before setting + * @param taskType operation to be performed */ - public void setVarnodeMap(Map> vmap, boolean clear); + public void setVarnodeMap(Map> vmap, boolean clear, TaskType taskType); /** * Clear existing taint diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java index 828ba22c17..3c1b256f76 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/TaintState.java @@ -46,6 +46,10 @@ public interface TaintState { SRCSINK, DEFAULT, CUSTOM } + public enum TaskType { + SET_TAINT, SET_DELTA, APPLY_DELTA + } + public static TaintState newInstance(TaintPlugin plugin) { return new TaintStateCTADL(plugin); } @@ -93,10 +97,12 @@ public interface TaintState { public void setCancellation(boolean status); - public void setTaintVarnodeMap(Map> vmap); + public void setTaintVarnodeMap(Map> vmap, TaskType delta); public Map> getTaintVarnodeMap(); + public Set getQuerySet(Address addr); + public void buildIndex(List param_list, String engine_path, String facts_path, String index_directory); @@ -153,4 +159,6 @@ public interface TaintState { return false; } + public void setTaskType(TaskType taskType); + } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java index 64a2ea4eec..ef6d284040 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintAbstractQueryAction.java @@ -39,12 +39,13 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc protected TaintPlugin plugin; protected TaintState state; protected String desc; + protected String title; protected String executeTaintQueryIconString; protected Icon executeTaintQueryIcon; protected QueryType queryType; - public TaintAbstractQueryAction(TaintPlugin plugin, TaintState state, String desc, String cmd) { + public TaintAbstractQueryAction(TaintPlugin plugin, TaintState state, String desc, String title, String cmd) { super(cmd); setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, "Taint"+desc)); @@ -53,6 +54,7 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc this.plugin = plugin; this.state = state; this.desc = desc; + this.title = title; } /* @@ -68,7 +70,7 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc Program program = context.getProgram(); PluginTool tool = context.getTool(); - Task defaultQueryTask = new Task("Source-Sink Query Task", true, true, true, true) { + Task defaultQueryTask = new Task(title, true, true, true, true) { @Override public void run(TaskMonitor monitor) { state.setCancellation(false); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java index ba5e46e45b..2ada4ef297 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryAction.java @@ -28,13 +28,13 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryAction extends TaintAbstractQueryAction { public TaintQueryAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "Query", "Run taint query"); + super(plugin, state, "Query", "Source-Sink Taint Query", "Run taint query"); executeTaintQueryIconString = "icon.graph.default.display.program.graph"; executeTaintQueryIcon = new GIcon(executeTaintQueryIconString); queryType = QueryType.SRCSINK; setToolBarData(new ToolBarData(executeTaintQueryIcon)); - setKeyBindingData(new KeyBindingData(KeyEvent.VK_Q, 0)); + setKeyBindingData(new KeyBindingData(KeyEvent.VK_Y, 0)); } @Override diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java index 27f1d8fe44..eac8d0b915 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryCustomAction.java @@ -15,6 +15,10 @@ */ package ghidra.app.plugin.core.decompiler.taint.actions; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import docking.action.KeyBindingData; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; import ghidra.app.plugin.core.decompiler.taint.TaintState; import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; @@ -22,8 +26,9 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryCustomAction extends TaintAbstractQueryAction { public TaintQueryCustomAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "CustomQuery", "Run custom taint query"); + super(plugin, state, "CustomQuery", "Custom Taint Query", "Run custom taint query"); queryType = QueryType.CUSTOM; + setKeyBindingData(new KeyBindingData(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK)); } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java index d5922f28a1..27fa19078c 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/actions/TaintQueryDefaultAction.java @@ -15,6 +15,10 @@ */ package ghidra.app.plugin.core.decompiler.taint.actions; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import docking.action.KeyBindingData; import docking.action.ToolBarData; import generic.theme.GIcon; import ghidra.app.plugin.core.decompiler.taint.TaintPlugin; @@ -24,12 +28,13 @@ import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; public class TaintQueryDefaultAction extends TaintAbstractQueryAction { public TaintQueryDefaultAction(TaintPlugin plugin, TaintState state) { - super(plugin, state, "DefaultQuery", "Run default taint query"); + super(plugin, state, "DefaultQuery", "Default Taint Query", "Run default taint query"); executeTaintQueryIconString = "icon.version.tracking.markup.status.conflict"; executeTaintQueryIcon = new GIcon(executeTaintQueryIconString); queryType = QueryType.DEFAULT; setToolBarData(new ToolBarData(executeTaintQueryIcon)); + setKeyBindingData(new KeyBindingData(KeyEvent.VK_Y, InputEvent.SHIFT_DOWN_MASK)); } } diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java index 2da0418ad7..7781c41dc8 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/ctadl/TaintStateCTADL.java @@ -23,6 +23,7 @@ import java.util.List; import generic.jar.ResourceFile; import ghidra.app.decompiler.*; import ghidra.app.plugin.core.decompiler.taint.*; +import ghidra.app.plugin.core.decompiler.taint.TaintPlugin.TaintDirection; import ghidra.app.plugin.core.osgi.BundleHost; import ghidra.app.script.*; import ghidra.app.services.ConsoleService; @@ -48,8 +49,23 @@ public class TaintStateCTADL extends AbstractTaintState { paramList.add("--directory"); paramList.add(indexDirectory); paramList.add("query"); + Comparable direction = taintOptions.getTaintDirection(); + if (!direction.equals(TaintDirection.DEFAULT)) { + paramList.add("--compute-slices"); + switch (taintOptions.getTaintDirection()) { + case TaintDirection.BOTH -> + paramList.add("all"); + case TaintDirection.FORWARD -> + paramList.add("fwd"); + case TaintDirection.BACKWARD -> + paramList.add("bwd"); + default -> { + // No action + } + } + } paramList.add("-j8"); - paramList.add("--format=" + taintOptions.getTaintOutputForm()); + paramList.add("--format=" + taintOptions.getTaintOutputForm().toString()); } @Override @@ -132,6 +148,10 @@ public class TaintStateCTADL extends AbstractTaintState { if (!TaintState.isActualParam(token) && !(hv instanceof HighParam)) { writer.println("\tVNODE_PC_ADDRESS(vn, " + addr.getOffset() + "),"); } + else { // NB: we still want a local match + writer.println("\t(PCODE_INPUT(i, _, vn) ; PCODE_OUTPUT(i, vn)),"); + writer.println("\tPCODE_TARGET(i, " + addr.getOffset() + "),"); + } } if (mark.bySymbol()) { writer.println("\tSYMBOL_NAME(sym, \"" + token.getText() + "\"),"); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintCodeFlowResultHandler.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintCodeFlowResultHandler.java index 7f5c4a16b1..9ace7fc8d6 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintCodeFlowResultHandler.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintCodeFlowResultHandler.java @@ -41,12 +41,12 @@ public class SarifTaintCodeFlowResultHandler extends SarifResultHandler { } @Override - public void handle(SarifDataFrame dframe, Run run, Result result, Map map) { + public void handle(SarifDataFrame dframe, Run r, Result res, Map map) { this.df = dframe; this.controller = df.getController(); - this.run = run; - this.result = result; + this.run = r; + this.result = res; List> tableResults = df.getTableResults(); String ruleId = result.getRuleId(); diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java index 138e344b02..b2ec2bb5d0 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/plugin/core/decompiler/taint/sarif/SarifTaintResultHandler.java @@ -23,6 +23,7 @@ import com.contrastsecurity.sarif.*; import docking.ActionContext; import docking.action.*; import ghidra.app.plugin.core.decompiler.taint.*; +import ghidra.app.plugin.core.decompiler.taint.TaintState.TaskType; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; @@ -49,12 +50,12 @@ public class SarifTaintResultHandler extends SarifResultHandler { } @Override - public void handle(SarifDataFrame dframe, Run run, Result result, Map map) { + public void handle(SarifDataFrame dframe, Run r, Result res, Map map) { this.df = dframe; this.controller = df.getController(); - this.run = run; - this.result = result; + this.run = r; + this.result = res; String ruleId = result.getRuleId(); if (ruleId == null || ruleId.equals("C0001")) { @@ -199,6 +200,7 @@ public class SarifTaintResultHandler extends SarifResultHandler { private class ApplyTaintViaVarnodesTask extends ProgramTask { private SarifResultsTableProvider tableProvider; + protected TaskType taskType = TaskType.SET_TAINT; protected ApplyTaintViaVarnodesTask(SarifResultsTableProvider provider) { super(provider.getController().getProgram(), "ApplyTaintViaVarnodesTask", true, true, @@ -224,7 +226,7 @@ public class SarifTaintResultHandler extends SarifResultHandler { PluginTool tool = tableProvider.getController().getPlugin().getTool(); TaintService service = tool.getService(TaintService.class); if (service != null) { - service.setVarnodeMap(map, true); + service.setVarnodeMap(map, true, taskType); } } @@ -318,7 +320,7 @@ public class SarifTaintResultHandler extends SarifResultHandler { } } - service.setVarnodeMap(map, false); + service.setVarnodeMap(map, false, TaskType.SET_TAINT); service.setAddressSet(set, false); } @@ -363,4 +365,68 @@ public class SarifTaintResultHandler extends SarifResultHandler { return vset; } + // Saving these for later - on the fence re use case + /* + DockingAction applyDelta = new DockingAction("Apply delta", getKey()) { + @Override + public void actionPerformed(ActionContext context) { + provider.filterTable.getTable().selectAll(); + TaskLauncher.launch(new ApplyDeltaViaVarnodesTask(provider)); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return isEnabled; + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabled; + } + }; + applyDelta.setDescription("Apply delta"); + applyDelta.setToolBarData(new ToolBarData(Icons.COLLAPSE_ALL_ICON)); + provider.addLocalAction(applyDelta); + + DockingAction initDelta = new DockingAction("Set base for delta", getKey()) { + @Override + public void actionPerformed(ActionContext context) { + TaskLauncher.launch(new SetDeltaBaseTask(provider)); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + return isEnabled; + } + + @Override + public boolean isAddToPopup(ActionContext context) { + return isEnabled; + } + }; + initDelta.setDescription("Initialize delta"); + initDelta.setToolBarData(new ToolBarData(Icons.INFO_ICON)); + provider.addLocalAction(initDelta); + */ + + /* + private class ApplyDeltaViaVarnodesTask extends ApplyTaintViaVarnodesTask { + + protected ApplyDeltaViaVarnodesTask(SarifResultsTableProvider provider) { + super(provider); + this.delta = TaskType.APPLY_DELTA; + } + + } + + private class SetDeltaBaseTask extends ApplyTaintViaVarnodesTask { + + protected SetDeltaBaseTask(SarifResultsTableProvider provider) { + super(provider); + this.delta = TaskType.SET_DELTA; + } + + } + */ + } diff --git a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/decompiler/taint/DecompilerTaintTest.java b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/decompiler/taint/DecompilerTaintTest.java index 3f3a556d5f..67c2479e71 100644 --- a/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/decompiler/taint/DecompilerTaintTest.java +++ b/Ghidra/Features/DecompilerDependent/src/test/java/ghidra/app/plugin/core/decompiler/taint/DecompilerTaintTest.java @@ -32,8 +32,7 @@ import ghidra.app.decompiler.*; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.decompile.DecompilePlugin; import ghidra.app.plugin.core.decompile.DecompilerProvider; -import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType; -import ghidra.app.plugin.core.decompiler.taint.TaintState.QueryType; +import ghidra.app.plugin.core.decompiler.taint.TaintState.*; import ghidra.app.plugin.core.decompiler.taint.sarif.SarifTaintGraphRunHandler; import ghidra.app.services.CodeViewerService; import ghidra.framework.Application; @@ -186,7 +185,7 @@ public class DecompilerTaintTest extends AbstractGhidraHeadedIntegrationTest { for (Map result : df.getTableResults()) { processResult(map, result); } - taintService.setVarnodeMap(map, true); + taintService.setVarnodeMap(map, true, TaskType.SET_TAINT); validateResult(token, map); } diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java index 45bfe3a3a4..c79be0217f 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifPlugin.java @@ -139,6 +139,7 @@ public class SarifPlugin extends ProgramPlugin implements SarifService, OptionsC return io.readSarif(sarif); } + @Override public SarifController getController() { currentProgram = getCurrentProgram(); if (currentProgram != null) { @@ -156,9 +157,10 @@ public class SarifPlugin extends ProgramPlugin implements SarifService, OptionsC * Ultimately both selections end up calling this to actually show something on * the Ghidra gui * - * @param logName - * @param sarif + * @param logName display name + * @param sarif the raw sarif */ + @Override public void showSarif(String logName, SarifSchema210 sarif) { SarifController currentController = getController(); if (currentController != null) { diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java index 38b618bb33..b7270e8138 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/SarifUtils.java @@ -68,13 +68,17 @@ public class SarifUtils { // artifactLocation/uri <= the overlayED space name (typically OTHER) private static Run currentRun = null; + // llocs has indexed per run and is not valid across runs + // Attempts to access llocs outside the population phase will throw an error private static LogicalLocation[] llocs; + // All of the following have keys that are valid across queries private static List addresses; private static Map nameToOffset = new HashMap<>(); private static Map nodeLocs = new HashMap<>(); private static Map edgeSrcs = new HashMap<>(); private static Map edgeDsts = new HashMap<>(); private static Map edgeDescs = new HashMap<>(); + private static boolean populating = false; public static JsonArray setLocations(Address min, Address max) { AddressSet set = new AddressSet(min, max); @@ -141,6 +145,9 @@ public class SarifUtils { } public static Address locationToAddress(Location location, Program program, boolean useOverlays) { + if (!populating) { + throw new RuntimeException("Locations valid only during population phase"); + } Long addr = -1L; PhysicalLocation physicalLocation = location.getPhysicalLocation(); if (location.getPhysicalLocation() != null) { @@ -244,7 +251,9 @@ public class SarifUtils { public static Address extractFunctionEntryAddr(Program program, String fqname) { String addr = null; - if (fqname.contains("!")) { + // NB: ! can be used both as a delimiter and part of an operator + // TODO: This may eventually require a more complicated check + if (fqname.contains("!") && !fqname.contains("!=")) { fqname = fqname.substring(0, fqname.indexOf("!")); } String[] parts = fqname.split("@"); @@ -263,10 +272,6 @@ public class SarifUtils { return program.getAddressFactory().getAddress(addr); } - /** - * @param fqname - * @return - */ public static List

extractFQNameAddrPair(Program program, String fqname) { List
addr_pair = new ArrayList
(); String[] parts = fqname.split("@"); @@ -350,6 +355,9 @@ public class SarifUtils { } public static LogicalLocation getLogicalLocation(Run run, Location loc) { + if (!populating) { + throw new RuntimeException("Locations valid only during population phase"); + } Set llocset = loc.getLogicalLocations(); if (llocset == null) { return null; @@ -366,10 +374,6 @@ public class SarifUtils { return null; } - public static LogicalLocation getLogicalLocation(Run run, Long index) { - return llocs[index.intValue()]; - } - public static void validateRun(Run run) { if (!run.equals(currentRun) || llocs == null) { initRun(run); @@ -407,9 +411,18 @@ public class SarifUtils { Location loc = n.getLocation(); if (loc != null) { Set logicalLocations = loc.getLogicalLocations(); - LogicalLocation[] llocs = new LogicalLocation[logicalLocations.size()]; - logicalLocations.toArray(llocs); - nodeLocs.put(id, llocs); + LogicalLocation[] nodells = new LogicalLocation[logicalLocations.size()]; + int i = 0; + for (LogicalLocation ll : logicalLocations) { + // NB: These have to be derefenced immediately as they will be invalid for subsequent queries + if (ll.getFullyQualifiedName() != null) { + nodells[i++] = ll; + } + else { + nodells[i++] = llocs[ll.getIndex().intValue()]; + } + } + nodeLocs.put(id, nodells); } } } @@ -439,4 +452,8 @@ public class SarifUtils { return nodeLocs.get(id); } + public static void setPopulating(boolean b) { + populating = b; + } + } diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/model/SarifDataFrame.java b/Ghidra/Features/Sarif/src/main/java/sarif/model/SarifDataFrame.java index c61c27420c..a51ac2ff39 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/model/SarifDataFrame.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/model/SarifDataFrame.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -82,28 +82,34 @@ public class SarifDataFrame { columns.add(new SarifColumnKey(entry.getKey(), entry.getValue())); } SarifUtils.validateRun(run); - for (Result result : run.getResults()) { - compileTaxaMap(run, result); - - Map curTableResult = new HashMap<>(); - for (SarifResultHandler handler : resultHandlers) { + SarifUtils.setPopulating(true); + try { + for (Result result : run.getResults()) { + compileTaxaMap(run, result); + + Map curTableResult = new HashMap<>(); + for (SarifResultHandler handler : resultHandlers) { + if (handler.isEnabled(this)) { + handler.handle(this, run, result, curTableResult); + } + } + tableResults.add(curTableResult); + String ruleid = (String) curTableResult.get("RuleId"); + List> list = tableResultsAsMap.get(ruleid); + if (list == null) { + list = new ArrayList<>(); + tableResultsAsMap.put(ruleid, list); + } + list.add(curTableResult); + } + for (SarifRunHandler handler : controller.getSarifRunHandlers()) { if (handler.isEnabled(this)) { - handler.handle(this, run, result, curTableResult); + handler.handle(this, run); } } - tableResults.add(curTableResult); - String ruleid = (String) curTableResult.get("RuleId"); - List> list = tableResultsAsMap.get(ruleid); - if (list == null) { - list = new ArrayList<>(); - tableResultsAsMap.put(ruleid, list); - } - list.add(curTableResult); - } - for (SarifRunHandler handler : controller.getSarifRunHandlers()) { - if (handler.isEnabled(this)) { - handler.handle(this, run); - } + } + finally { + SarifUtils.setPopulating(false); } } } @@ -147,7 +153,7 @@ public class SarifDataFrame { Set taxa = result.getTaxa(); if (taxa != null) { for (ReportingDescriptorReference ref : taxa) { - long idx = (long) ref.getToolComponent().getIndex(); + long idx = ref.getToolComponent().getIndex(); if (idx >= 0 && idx < view.size()) { ToolComponent tc = view.get((int) idx); taxaMap.put(tc.getName(), ref);