commit d11da2946b0c3f2c68fd2f2bf05348196ae3c079
parent 61827781fd3f14ab614827031a76ed0612792668
Author: MTRNord <mtrnord1@gmail.com>
Date: Sun, 1 Oct 2023 16:47:32 +0200
Restructure the code to reflect the truth a bit better
Diffstat:
6 files changed, 178 insertions(+), 18 deletions(-)
diff --git a/__test__/index.spec.mjs b/__test__/index.spec.mjs
@@ -1,20 +1,20 @@
import test from 'ava'
-import { YaraScanner } from '../index.js'
+import { YaraCompiler } from '../index.js'
const TEST_RULE = "rule TestRule {\n condition:\n true\n}"
-test('can construct YaraScanner', (t) => {
+test('can construct YaraCompiler', (t) => {
t.plan(1)
t.notThrows(() => {
- const scanner = new YaraScanner([], []);
+ const compiler = new YaraCompiler([], []);
});
})
test('can load string rules', (t) => {
t.plan(1)
t.notThrows(() => {
- const scanner = new YaraScanner([{
+ const compiler = new YaraCompiler([{
string: TEST_RULE
}], []);
});
@@ -24,9 +24,10 @@ test('can load string rules', (t) => {
test('can match string rules', (t) => {
t.plan(2)
t.notThrows(() => {
- const scanner = new YaraScanner([{
+ const compiler = new YaraCompiler([{
string: TEST_RULE
}], []);
+ const scanner = compiler.newScanner();
const result = scanner.scanString("");
t.deepEqual(result, [
{
diff --git a/index.d.ts b/index.d.ts
@@ -52,8 +52,68 @@ export interface YaraMatch {
/** Matched data. */
data: Array<number>
}
-export class YaraScanner {
+/**
+ * An interface to use yara with node in a stable manner using Rust
+ * @public
+ */
+export class YaraCompiler {
+ /**
+ * Constructs a new Yara instance and compiles the provided rules and variables.
+ *
+ * @param rules - The rules which shall be compiled.
+ * @param variables - The variables you want to pass to the rules.
+ * @throws This can throw if there is an unexpected error.
+ *
+ * @returns A new instance of a YaraScanner which can be used to scan data
+ */
constructor(rules: Array<YaraRule>, variables: Array<YaraVariable>)
+ /**
+ * Creates a new yara scanner for the rules defined earlier.
+ * This can be called multiple times
+ *
+ * @returns A {@link YaraScanner} instance
+ */
+ newScanner(): YaraScanner
+}
+/**
+ * An interface to use yara with node in a stable manner using Rust
+ * @public
+ */
+export class YaraScanner {
+ /**
+ * Scan a buffer of data with yara
+ *
+ * @param buffer - The data which shall be scanned by yara.
+ * @throws This can throw if there is an unexpected error.
+ *
+ * @returns The results of yara scan_mem.
+ */
scanBuffer(buffer: Buffer): Array<YaraRuleResult>
+ /**
+ * Scan a string of data with yara
+ *
+ * @param input - The data which shall be scanned by yara.
+ * @throws This can throw if there is an unexpected error.
+ *
+ * @returns The results of yara scan_mem.
+ */
scanString(input: string): Array<YaraRuleResult>
+ /**
+ * Scan a file with yara
+ *
+ * @param filepath - The path to the file yara shall scan
+ * @throws This can throw if there is an unexpected error.
+ *
+ * @returns The results of yara scan_mem.
+ */
+ scanFile(filepath: string): Array<YaraRuleResult>
+ /**
+ * Scan a process with yara
+ *
+ * @param pid - The process id of the process that shall be scanned.
+ * @throws This can throw if there is an unexpected error.
+ *
+ * @returns The results of yara scan_mem.
+ */
+ scanProcess(pid: number): Array<YaraRuleResult>
}
diff --git a/index.js b/index.js
@@ -252,6 +252,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
-const { YaraScanner } = nativeBinding
+const { YaraCompiler, YaraScanner } = nativeBinding
+module.exports.YaraCompiler = YaraCompiler
module.exports.YaraScanner = YaraScanner
diff --git a/package.json b/package.json
@@ -15,6 +15,7 @@
"license": "MIT",
"devDependencies": {
"@napi-rs/cli": "^2.16.3",
+ "@types/node": "^20.8.0",
"ava": "^5.1.1"
},
"ava": {
diff --git a/src/lib.rs b/src/lib.rs
@@ -1,7 +1,11 @@
#![deny(clippy::all)]
-use napi::{anyhow::Context, bindgen_prelude::Buffer, Result};
-use yara::{Compiler, MetadataValue, Rule as ExtYaraRule, Rules};
+use napi::{
+ anyhow::Context,
+ bindgen_prelude::{Buffer, Reference, SharedReference},
+ Env, Result,
+};
+use yara::{Compiler, MetadataValue, Rule as ExtYaraRule, Rules, Scanner};
#[macro_use]
extern crate napi_derive;
@@ -28,11 +32,20 @@ pub struct YaraVariable {
pub string_value: Option<String>,
}
+/// An interface to use yara with node in a stable manner using Rust
+/// @public
#[napi]
-pub struct YaraScanner {
+pub struct YaraCompiler {
rules: Rules,
}
+/// An interface to use yara with node in a stable manner using Rust
+/// @public
+#[napi]
+pub struct YaraScanner {
+ scanner: SharedReference<YaraCompiler, Scanner<'static>>,
+}
+
#[napi(object)]
#[derive(Debug)]
pub struct YaraRuleResult {
@@ -80,7 +93,14 @@ pub struct YaraMatch {
}
#[napi]
-impl YaraScanner {
+impl YaraCompiler {
+ /// Constructs a new Yara instance and compiles the provided rules and variables.
+ ///
+ /// @param rules - The rules which shall be compiled.
+ /// @param variables - The variables you want to pass to the rules.
+ /// @throws This can throw if there is an unexpected error.
+ ///
+ /// @returns A new instance of a YaraScanner which can be used to scan data
#[napi(constructor)]
pub fn new(rules: Vec<YaraRule>, variables: Vec<YaraVariable>) -> napi::Result<Self> {
let mut compiler = Compiler::new().context("Failed to create yara compiler")?;
@@ -133,9 +153,30 @@ impl YaraScanner {
.compile_rules()
.context("Failed to compile rules")?;
- Ok(YaraScanner { rules })
+ Ok(YaraCompiler { rules })
+ }
+
+ /// Creates a new yara scanner for the rules defined earlier.
+ /// This can be called multiple times
+ ///
+ /// @returns A {@link YaraScanner} instance
+ #[napi]
+ pub fn new_scanner(&self, reference: Reference<YaraCompiler>, env: Env) -> Result<YaraScanner> {
+ YaraScanner::new(reference, env)
+ }
+}
+
+#[napi]
+impl YaraScanner {
+ pub fn new(reference: Reference<YaraCompiler>, env: Env) -> Result<Self> {
+ let scanner = reference.share_with(env, |compiler| {
+ Ok(compiler.rules.scanner().context("Failed to get scanner")?)
+ })?;
+ Ok(YaraScanner { scanner })
}
+ /// Converts the yara-rs types to types which we can return to napi-rs.
+ /// Ideally we dont need this but sadly for now this is required.
fn convert_yara_results(&self, rules: Vec<ExtYaraRule>) -> Vec<YaraRuleResult> {
let results = rules
.iter()
@@ -186,20 +227,68 @@ impl YaraScanner {
results
}
+ /// Scan a buffer of data with yara
+ ///
+ /// @param buffer - The data which shall be scanned by yara.
+ /// @throws This can throw if there is an unexpected error.
+ ///
+ /// @returns The results of yara scan_mem.
#[napi]
- pub fn scan_buffer(&self, buffer: Buffer) -> Result<Vec<YaraRuleResult>> {
- let mut scanner = self.rules.scanner().context("Failed to get scanner")?;
+ pub fn scan_buffer(&mut self, buffer: Buffer) -> Result<Vec<YaraRuleResult>> {
let buf: Vec<u8> = buffer.into();
- let results = scanner.scan_mem(&buf).context("Failed to scan buffer")?;
+ let results = self
+ .scanner
+ .scan_mem(&buf)
+ .context("Failed to scan buffer")?;
Ok(self.convert_yara_results(results))
}
+ /// Scan a string of data with yara
+ ///
+ /// @param input - The data which shall be scanned by yara.
+ /// @throws This can throw if there is an unexpected error.
+ ///
+ /// @returns The results of yara scan_mem.
#[napi]
- pub fn scan_string(&self, input: String) -> Result<Vec<YaraRuleResult>> {
- let mut scanner = self.rules.scanner().context("Failed to get scanner")?;
+ pub fn scan_string(&mut self, input: String) -> Result<Vec<YaraRuleResult>> {
let buf: &[u8] = input.as_bytes();
- let results = scanner.scan_mem(buf).context("Failed to scan buffer")?;
+ let results = self
+ .scanner
+ .scan_mem(buf)
+ .context("Failed to scan string")?;
+
+ Ok(self.convert_yara_results(results))
+ }
+
+ /// Scan a file with yara
+ ///
+ /// @param filepath - The path to the file yara shall scan
+ /// @throws This can throw if there is an unexpected error.
+ ///
+ /// @returns The results of yara scan_mem.
+ #[napi]
+ pub fn scan_file(&mut self, filepath: String) -> Result<Vec<YaraRuleResult>> {
+ let results = self
+ .scanner
+ .scan_file(filepath)
+ .context("Failed to scan file")?;
+
+ Ok(self.convert_yara_results(results))
+ }
+
+ /// Scan a process with yara
+ ///
+ /// @param pid - The process id of the process that shall be scanned.
+ /// @throws This can throw if there is an unexpected error.
+ ///
+ /// @returns The results of yara scan_mem.
+ #[napi]
+ pub fn scan_process(&mut self, pid: u32) -> Result<Vec<YaraRuleResult>> {
+ let results = self
+ .scanner
+ .scan_process(pid)
+ .context("Failed to scan file")?;
Ok(self.convert_yara_results(results))
}
diff --git a/yarn.lock b/yarn.lock
@@ -33,6 +33,7 @@ __metadata:
resolution: "@node_yara_rs/node-yara-rs@workspace:."
dependencies:
"@napi-rs/cli": ^2.16.3
+ "@types/node": ^20.8.0
ava: ^5.1.1
languageName: unknown
linkType: soft
@@ -87,6 +88,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/node@npm:^20.8.0":
+ version: 20.8.0
+ resolution: "@types/node@npm:20.8.0"
+ checksum: ebad6342d54238a24bf980d7750117a5d67749c9b72cbb7a974a1e932c39034aa3a810d669e007e8a5071782a253aa069a187b614407a382403c9826e837c849
+ languageName: node
+ linkType: hard
+
"abbrev@npm:^1.0.0":
version: 1.1.1
resolution: "abbrev@npm:1.1.1"