Make yara work on OpenBSD, using system yara
Index: Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java
--- Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java.orig
+++ Core/src/org/sleuthkit/autopsy/modules/yara/YaraIngestHelper.java
@@ -19,7 +19,12 @@
 package org.sleuthkit.autopsy.modules.yara;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -40,6 +45,7 @@ import static org.sleuthkit.datamodel.BlackboardAttrib
 import org.sleuthkit.datamodel.BlackboardAttribute;
 import org.sleuthkit.datamodel.Score;
 import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
 
 /**
  * Methods for scanning files for yara rule matches.
@@ -48,12 +54,31 @@ final class YaraIngestHelper {
     
     private static final String YARA_DIR = "yara";
     private static final String YARA_C_EXE = "yarac64.exe";
+    private static final String YARA_C_EXE_UNIX = "yarac";
+    private static final String YARA_EXE_UNIX = "yara";
     private static final String MODULE_NAME = YaraIngestModuleFactory.getModuleName();
 
+    // Cache the resolved YARA executable binary location across thousands of scan invocations
+    private static volatile File cachedYaraExe = null;
+
     private YaraIngestHelper() {
     }
 
     /**
+     * Retrieves the cached YARA scanner executable or resolves it if not yet found.
+     */
+    private static File getYaraExecutable() throws FileNotFoundException {
+        if (cachedYaraExe == null) {
+            synchronized (YaraIngestHelper.class) {
+                if (cachedYaraExe == null) {
+                    cachedYaraExe = locateExecutable(null, YARA_EXE_UNIX);
+                }
+            }
+        }
+        return cachedYaraExe;
+    }
+
+    /**
      * Uses the yarac tool to compile the rules in the given rule sets.
      *
      * @param ruleSetNames List of names of the selected rule sets.
@@ -66,13 +91,12 @@ final class YaraIngestHelper {
             throw new IngestModule.IngestModuleException(Bundle.YaraIngestModule_no_ruleSets());
         }
 
-        // Find javac
-        File exeFile = InstalledFileLocator.getDefault().locate(
-                Paths.get(YARA_DIR, YARA_C_EXE).toString(),
-                YaraIngestModule.class.getPackage().getName(), false);
-
-        if (exeFile == null) {
-            throw new IngestModuleException(Bundle.YaraIngestModule_yarac_not_found());
+        // Find jarac
+        File exeFile = null;
+        try {
+            exeFile = locateExecutable(YARA_C_EXE, YARA_C_EXE_UNIX);
+        } catch (FileNotFoundException ex) {
+            throw new IngestModuleException(Bundle.YaraIngestModule_yarac_not_found(), ex);
         }
 
         for (RuleSet set : getRuleSetsForNames(ruleSetNames)) {
@@ -150,17 +174,46 @@ final class YaraIngestHelper {
      * @throws TskCoreException
      */
     private static List<String> scanFileForMatches(byte[] fileBytes, int fileSize, File ruleSetDirectory, int timeout) throws YaraWrapperException {
-        List<String> matchingRules = new ArrayList<>();
+    List<String> matchingRules = new ArrayList<>();
+    File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
 
-        File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
+    if (ruleSetCompiledFileList == null) {
+        return matchingRules;
+    }
 
+    if (PlatformUtil.isWindowsOS()) {
+        // Windows: Use the JNI native wrapper
         for (File ruleFile : ruleSetCompiledFileList) {
             matchingRules.addAll(YaraJNIWrapper.findRuleMatch(ruleFile.getAbsolutePath(), fileBytes, fileSize, timeout));
         }
+    } else {
+        // Non-Windows (OpenBSD / Linux): Write byte array to temp file, then delegate to scanFileForMatch
+        Path tempScanFile = null;
+        try {
+            tempScanFile = Files.createTempFile("autopsy_yara_scan_", ".tmp");
+            try (OutputStream os = Files.newOutputStream(tempScanFile)) {
+                os.write(fileBytes, 0, fileSize);
+            }
 
-        return matchingRules;
+            // Reuse scanFileForMatch with the temporary file
+            matchingRules.addAll(scanFileForMatch(tempScanFile.toFile(), ruleSetDirectory, timeout));
+
+        } catch (Exception ex) {
+            System.err.println("[YARA] Error processing file byte array scan: " + ex.getMessage());
+        } finally {
+            // Clean up temporary scan file
+            if (tempScanFile != null) {
+                try {
+                    Files.deleteIfExists(tempScanFile);
+                } catch (Exception ignored) {
+                }
+            }
+        }
     }
 
+    return matchingRules;
+    }
+
     /**
      * Scan the given file for rules that match from the given rule set
      * directory.
@@ -174,18 +227,64 @@ final class YaraIngestHelper {
      *
      * @throws YaraWrapperException
      */
-    private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException {
-        List<String> matchingRules = new ArrayList<>();
+private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException {
+    List<String> matchingRules = new ArrayList<>();
 
-        File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
+    File[] ruleSetCompiledFileList = ruleSetDirectory.listFiles();
+    if (ruleSetCompiledFileList == null) {
+        return matchingRules;
+    }
 
+    if (PlatformUtil.isWindowsOS()) {
+        // Windows: Use JNI native wrapper
         for (File ruleFile : ruleSetCompiledFileList) {
             matchingRules.addAll(YaraJNIWrapper.findRuleMatchFile(ruleFile.getAbsolutePath(), scanFile.getAbsolutePath(), timeout));
         }
+    } else {
+        // Non-Windows (OpenBSD / Linux): Locate yara CLI and scan via ProcessBuilder
+        File yaraExe = null;
+        try {
+            yaraExe = getYaraExecutable();
+        } catch (FileNotFoundException ex) {
+            System.err.println("[YARA] Error: " + ex.getMessage());
+            return matchingRules;
+        }
 
-        return matchingRules;
+        for (File ruleFile : ruleSetCompiledFileList) {
+            List<String> command = new ArrayList<>();
+            command.add(yaraExe.getAbsolutePath());
+            command.add("-C"); // Expects compiled rules file
+            if (timeout > 0) {
+                command.add("-a");
+                command.add(String.valueOf(timeout));
+            }
+            command.add(ruleFile.getAbsolutePath());
+            command.add(scanFile.getAbsolutePath());
+
+            try {
+                ProcessBuilder pb = new ProcessBuilder(command);
+                Process process = pb.start();
+
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        // Output format from YARA CLI: "RuleName /path/to/target/file"
+                        String[] parts = line.trim().split("\\s+");
+                        if (parts.length > 0 && !parts[0].isEmpty()) {
+                            matchingRules.add(parts[0]);
+                        }
+                    }
+                }
+                process.waitFor();
+            } catch (Exception ex) {
+                System.err.println("[YARA] Error executing yara CLI: " + ex.getMessage());
+            }
+        }
     }
 
+    return matchingRules;
+    }
+
     /**
      * Create a list of Blackboard Artifacts, one for each matching rule.
      *
@@ -219,7 +318,50 @@ final class YaraIngestHelper {
         "YaraIngestModule_no_ruleSets=Unable to run YARA ingest, list of YARA rule sets was empty."
     })
 
+
     /**
+     * Locates an executable binary by checking the installed module path on Windows 
+     * or searching the system PATH environment variable on non-Windows systems.
+     *
+     * @param winExeName  The target executable name relative to YARA_DIR on Windows (e.g., "yarac64.exe").
+     * @param unixExeName The executable name on UNIX/Linux systems (e.g., "yarac" or "yara").
+     *
+     * @return File representing the executable binary.
+     * @throws FileNotFoundException If the binary cannot be found or is not executable.
+     */
+     private static File locateExecutable(String winExeName, String unixExeName) throws FileNotFoundException {
+         File exeFile = null;
+
+        if (PlatformUtil.isWindowsOS()) {
+            if (winExeName == null) {
+                exeFile = null;
+            } else {
+                exeFile = InstalledFileLocator.getDefault().locate(
+                    Paths.get(YARA_DIR, winExeName).toString(),
+                    YaraIngestModule.class.getPackage().getName(), false);
+            }
+         } else {
+            String pathEnv = System.getenv("PATH");
+            if (pathEnv != null) {
+                String[] pathDirs = pathEnv.split(File.pathSeparator);
+                for (String dir : pathDirs) {
+                    File candidate = new File(dir, unixExeName);
+                    if (candidate.exists() && candidate.canExecute()) {
+                        exeFile = candidate;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (exeFile == null || !exeFile.canExecute()) {
+            throw new FileNotFoundException((PlatformUtil.isWindowsOS() ? winExeName : unixExeName) + " executable not found.");
+        }
+
+        return exeFile;
+    }
+
+    /**
      * Compiles the rule files in the given rule set.
      *
      * The compiled rule files are created in outputDir\RuleSetName.
@@ -239,13 +381,21 @@ final class YaraIngestHelper {
         List<File> fileList = set.getRuleFiles();
         for (File file : fileList) {
             List<String> commandList = new ArrayList<>();
-            commandList.add(String.format("\"%s\"", yarac.toString()));
-            commandList.add(String.format("\"%s\"", file.toString()));
-            commandList.add(String.format("\"%s\"", Paths.get(tempFolder.getAbsolutePath(), "compiled_" + file.getName())));
+            if (PlatformUtil.isWindowsOS()) {
+                commandList.add(String.format("\"%s\"", yarac.toString()));
+                commandList.add(String.format("\"%s\"", file.toString()));
+                commandList.add(String.format("\"%s\"", Paths.get(tempFolder.getAbsolutePath(), "compiled_" + file.getName())));
+            } else {
+                commandList.add(yarac.getAbsolutePath());
+                commandList.add(file.getAbsolutePath());
+                commandList.add(Paths.get(tempFolder.getAbsolutePath(), "compiled_" + file.getName()).toString());
+            }
 
+            System.out.println("[YARA DEBUG] Executing: " + String.join(" ", commandList));
             ProcessBuilder builder = new ProcessBuilder(commandList);
             try {
                 int result = ExecUtil.execute(builder);
+                System.out.println("[YARA DEBUG] Executed: result was: " + result);
                 if (result != 0) {
                     throw new IngestModuleException(String.format("Failed to compile Yara rules file %s. Compile error %d", file.toString(), result));
                 }
