commit 7c1107f4497ae86160dac8435951db3ecef4f367
parent f828c6ad5c3c3b0f108d069496529ecdeb72ec1c
Author: MTRNord <mtrnord1@gmail.com>
Date: Sun, 1 Oct 2023 13:33:44 +0200
Fix return types for functions and write proper tests. Also reduce required dependencies
Diffstat:
9 files changed, 218 insertions(+), 63 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -10,17 +10,10 @@ crate-type = ["cdylib"]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.12.2", default-features = false, features = [
"napi8",
- "async",
- "tokio_rt",
"error_anyhow",
- "serde-json",
] }
napi-derive = "2.12.2"
-serde_json = "1.0.107"
-yara = { version = "0.21.0", features = [
- "module-magic",
- "serde",
-] }
+yara = { version = "0.21.0" }
[build-dependencies]
napi-build = "2.0.1"
diff --git a/Makefile b/Makefile
@@ -25,10 +25,7 @@ yara:
LDFLAGS="$(LDFLAGS)" \
./configure \
$(CFGOPTS) \
- --enable-static \
- --disable-shared \
--with-pic \
- --without-crypto \
--prefix=$(BASE)/build/yara
cd $(BASE)/deps/yara-$(YARA) && make
cd $(BASE)/deps/yara-$(YARA) && make install
diff --git a/__test__/index.spec.mjs b/__test__/index.spec.mjs
@@ -1,7 +1,41 @@
import test from 'ava'
-import { sum } from '../index.js'
+import { YaraScanner } from '../index.js'
-test('sum from native', (t) => {
- t.is(sum(1, 2), 3)
+const TEST_RULE = "rule TestRule {\n condition:\n true\n}"
+
+test('can construct YaraScanner', (t) => {
+ t.plan(1)
+ t.notThrows(() => {
+ const scanner = new YaraScanner([], []);
+ });
+})
+
+test('can load string rules', (t) => {
+ t.plan(1)
+ t.notThrows(() => {
+ const scanner = new YaraScanner([{
+ string: TEST_RULE
+ }], []);
+ });
})
+
+
+test('can match string rules', (t) => {
+ t.plan(2)
+ t.notThrows(() => {
+ const scanner = new YaraScanner([{
+ string: TEST_RULE
+ }], []);
+ const result = scanner.scanString("");
+ t.deepEqual(result, [
+ {
+ identifier: "TestRule",
+ namespace: "default",
+ metadatas: [],
+ tags: [],
+ strings: []
+ }
+ ])
+ });
+})
+\ No newline at end of file
diff --git a/build.rs b/build.rs
@@ -1,5 +1,7 @@
extern crate napi_build;
fn main() {
+ println!("cargo:rerun-if-changed=build");
+ println!("cargo:rerun-if-changed=deps");
napi_build::setup();
}
diff --git a/index.d.ts b/index.d.ts
@@ -19,12 +19,7 @@ export interface YaraVariable {
/** Limitation of napi-rs which doesnt support any */
stringValue?: string
}
-export class YaraScanner {
- constructor(rules: Array<YaraRule>, variables: Array<YaraVariable>)
- scanBuffer(buffer: Buffer, timeout: number): Array<YaraRuleResult>
- scanString(input: string, timeout: number): Array<YaraRuleResult>
-}
-export class YaraRuleResult {
+export interface YaraRuleResult {
/** Name of the rule. */
identifier: string
/** Namespace of the rule. */
@@ -36,19 +31,19 @@ export class YaraRuleResult {
/** Matcher strings of the rule. */
strings: Array<YaraString>
}
-export class YaraRuleMetadata {
+export interface YaraRuleMetadata {
identifier: string
- integerValue: number
- stringValue: string
- boolValue: boolean
+ integerValue?: number
+ stringValue?: string
+ boolValue?: boolean
}
-export class YaraString {
+export interface YaraString {
/** Name of the string, with the '$'. */
identifier: string
/** Matches of the string for the scan. */
matches: Array<YaraMatch>
}
-export class YaraMatch {
+export interface YaraMatch {
base: number
/** Offset of the match within the scanning area. */
offset: number
@@ -57,3 +52,8 @@ export class YaraMatch {
/** Matched data. */
data: Array<number>
}
+export class YaraScanner {
+ constructor(rules: Array<YaraRule>, variables: Array<YaraVariable>)
+ scanBuffer(buffer: Buffer): Array<YaraRuleResult>
+ scanString(input: string): Array<YaraRuleResult>
+}
diff --git a/index.js b/index.js
@@ -252,10 +252,6 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
-const { YaraScanner, YaraRuleResult, YaraRuleMetadata, YaraString, YaraMatch } = nativeBinding
+const { YaraScanner } = nativeBinding
module.exports.YaraScanner = YaraScanner
-module.exports.YaraRuleResult = YaraRuleResult
-module.exports.YaraRuleMetadata = YaraRuleMetadata
-module.exports.YaraString = YaraString
-module.exports.YaraMatch = YaraMatch
diff --git a/package.json b/package.json
@@ -21,7 +21,7 @@
"timeout": "3m"
},
"engines": {
- "node": ">= 10"
+ "node": ">= 18"
},
"scripts": {
"artifacts": "napi artifacts",
diff --git a/src/lib.rs b/src/lib.rs
@@ -1,12 +1,13 @@
#![deny(clippy::all)]
use napi::{anyhow::Context, bindgen_prelude::Buffer, Result};
-use yara::{Compiler, Rule as ExtYaraRule, Rules};
+use yara::{Compiler, MetadataValue, Rule as ExtYaraRule, Rules};
#[macro_use]
extern crate napi_derive;
#[napi(object)]
+#[derive(Debug)]
pub struct YaraRule {
pub filename: Option<String>,
pub string: Option<String>,
@@ -14,6 +15,7 @@ pub struct YaraRule {
}
#[napi(object)]
+#[derive(Debug)]
pub struct YaraVariable {
pub id: String,
/// Limitation of napi-rs which doesnt support any
@@ -31,40 +33,40 @@ pub struct YaraScanner {
rules: Rules,
}
-#[napi]
+#[napi(object)]
+#[derive(Debug)]
pub struct YaraRuleResult {
/// Name of the rule.
pub identifier: String,
/// Namespace of the rule.
pub namespace: String,
/// Metadatas of the rule.
- #[napi(ts_type = "Array<YaraRuleMetadata>")]
- pub metadatas: Vec<serde_json::Value>,
+ pub metadatas: Vec<YaraRuleMetadata>,
/// Tags of the rule.
pub tags: Vec<String>,
/// Matcher strings of the rule.
- #[napi(ts_type = "Array<YaraString>")]
- pub strings: Vec<serde_json::Value>,
+ pub strings: Vec<YaraString>,
}
-#[napi]
+#[napi(object)]
+#[derive(Debug, Default)]
pub struct YaraRuleMetadata {
pub identifier: String,
- pub integer_value: i64,
- pub string_value: String,
- pub bool_value: bool,
+ pub integer_value: Option<i64>,
+ pub string_value: Option<String>,
+ pub bool_value: Option<bool>,
}
-#[napi]
+#[napi(object)]
+#[derive(Debug)]
pub struct YaraString {
/// Name of the string, with the '$'.
pub identifier: String,
/// Matches of the string for the scan.
- #[napi(ts_type = "Array<YaraMatch>")]
- pub matches: Vec<serde_json::Value>,
+ pub matches: Vec<YaraMatch>,
}
-#[napi]
+#[napi(object)]
#[derive(Debug, Clone)]
pub struct YaraMatch {
// base offset of the memory block in which the match occurred.
@@ -135,7 +137,7 @@ impl YaraScanner {
}
fn convert_yara_results(&self, rules: Vec<ExtYaraRule>) -> Vec<YaraRuleResult> {
- rules
+ let results = rules
.iter()
.map(|rule| YaraRuleResult {
identifier: rule.identifier.to_string(),
@@ -143,40 +145,61 @@ impl YaraScanner {
metadatas: rule
.metadatas
.iter()
- .map(|metadata| {
- serde_json::to_value(metadata).expect("Failed to serialize metadata in result")
+ .map(|metadata| match metadata.value {
+ MetadataValue::Integer(int) => YaraRuleMetadata {
+ identifier: metadata.identifier.to_string(),
+ integer_value: Some(int),
+ ..Default::default()
+ },
+ MetadataValue::String(string) => YaraRuleMetadata {
+ identifier: metadata.identifier.to_string(),
+ string_value: Some(string.to_string()),
+ ..Default::default()
+ },
+ MetadataValue::Boolean(boolean) => YaraRuleMetadata {
+ identifier: metadata.identifier.to_string(),
+ bool_value: Some(boolean),
+ ..Default::default()
+ },
})
.collect(),
tags: rule.tags.iter().map(ToString::to_string).collect(),
strings: rule
.strings
.iter()
- .map(|string| {
- serde_json::to_value(string).expect("Failed to serialize metadata in result")
+ .map(|string| YaraString {
+ identifier: string.identifier.to_string(),
+ matches: string
+ .matches
+ .iter()
+ .map(|matches| YaraMatch {
+ base: matches.base as i64,
+ offset: matches.offset as i64,
+ length: matches.length as i64,
+ data: matches.data.clone(),
+ })
+ .collect(),
})
.collect(),
})
- .collect()
+ .collect();
+ results
}
#[napi]
- pub fn scan_buffer(&self, buffer: Buffer, timeout: i32) -> Result<Vec<YaraRuleResult>> {
+ pub fn scan_buffer(&self, buffer: Buffer) -> Result<Vec<YaraRuleResult>> {
+ let mut scanner = self.rules.scanner().context("Failed to get scanner")?;
let buf: Vec<u8> = buffer.into();
- let results = self
- .rules
- .scan_mem(&buf, timeout)
- .context("Failed to scan buffer")?;
+ let results = scanner.scan_mem(&buf).context("Failed to scan buffer")?;
Ok(self.convert_yara_results(results))
}
#[napi]
- pub fn scan_string(&self, input: String, timeout: i32) -> Result<Vec<YaraRuleResult>> {
+ pub fn scan_string(&self, input: String) -> Result<Vec<YaraRuleResult>> {
+ let mut scanner = self.rules.scanner().context("Failed to get scanner")?;
let buf: &[u8] = input.as_bytes();
- let results = self
- .rules
- .scan_mem(buf, timeout)
- .context("Failed to scan buffer")?;
+ let results = scanner.scan_mem(buf).context("Failed to scan buffer")?;
Ok(self.convert_yara_results(results))
}
diff --git a/yarn.lock b/yarn.lock
@@ -5,6 +5,16 @@ __metadata:
version: 6
cacheKey: 8
+"@ava/typescript@npm:^4.1.0":
+ version: 4.1.0
+ resolution: "@ava/typescript@npm:4.1.0"
+ dependencies:
+ escape-string-regexp: ^5.0.0
+ execa: ^7.1.1
+ checksum: b0e0aa2a5d3bc8514dea73a2f8884bc6011c98d28e9f476dbfbdbab679f4ac17ac0d4ecb01ca9a6d6553db81a0e7786584625ec11cbd0b4e2743844470742160
+ languageName: node
+ linkType: hard
+
"@isaacs/cliui@npm:^8.0.2":
version: 8.0.2
resolution: "@isaacs/cliui@npm:8.0.2"
@@ -32,8 +42,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "@node_yara_rs/node-yara-rs@workspace:."
dependencies:
+ "@ava/typescript": ^4.1.0
"@napi-rs/cli": ^2.16.3
ava: ^5.1.1
+ typescript: ^5.2.2
languageName: unknown
linkType: soft
@@ -554,7 +566,7 @@ __metadata:
languageName: node
linkType: hard
-"cross-spawn@npm:^7.0.0":
+"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
dependencies:
@@ -700,6 +712,23 @@ __metadata:
languageName: node
linkType: hard
+"execa@npm:^7.1.1":
+ version: 7.2.0
+ resolution: "execa@npm:7.2.0"
+ dependencies:
+ cross-spawn: ^7.0.3
+ get-stream: ^6.0.1
+ human-signals: ^4.3.0
+ is-stream: ^3.0.0
+ merge-stream: ^2.0.0
+ npm-run-path: ^5.1.0
+ onetime: ^6.0.0
+ signal-exit: ^3.0.7
+ strip-final-newline: ^3.0.0
+ checksum: 14fd17ba0ca8c87b277584d93b1d9fc24f2a65e5152b31d5eb159a3b814854283eaae5f51efa9525e304447e2f757c691877f7adff8fde5746aae67eb1edd1cc
+ languageName: node
+ linkType: hard
+
"exponential-backoff@npm:^3.1.1":
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
@@ -842,6 +871,13 @@ __metadata:
languageName: node
linkType: hard
+"get-stream@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "get-stream@npm:6.0.1"
+ checksum: e04ecece32c92eebf5b8c940f51468cd53554dcbb0ea725b2748be583c9523d00128137966afce410b9b051eb2ef16d657cd2b120ca8edafcf5a65e81af63cad
+ languageName: node
+ linkType: hard
+
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
@@ -935,6 +971,13 @@ __metadata:
languageName: node
linkType: hard
+"human-signals@npm:^4.3.0":
+ version: 4.3.1
+ resolution: "human-signals@npm:4.3.1"
+ checksum: 6f12958df3f21b6fdaf02d90896c271df00636a31e2bbea05bddf817a35c66b38a6fdac5863e2df85bd52f34958997f1f50350ff97249e1dff8452865d5235d1
+ languageName: node
+ linkType: hard
+
"humanize-ms@npm:^1.2.1":
version: 1.2.1
resolution: "humanize-ms@npm:1.2.1"
@@ -1093,6 +1136,13 @@ __metadata:
languageName: node
linkType: hard
+"is-stream@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "is-stream@npm:3.0.0"
+ checksum: 172093fe99119ffd07611ab6d1bcccfe8bc4aa80d864b15f43e63e54b7abc71e779acd69afdb854c4e2a67fdc16ae710e370eda40088d1cfc956a50ed82d8f16
+ languageName: node
+ linkType: hard
+
"is-unicode-supported@npm:^1.2.0":
version: 1.3.0
resolution: "is-unicode-supported@npm:1.3.0"
@@ -1245,6 +1295,13 @@ __metadata:
languageName: node
linkType: hard
+"merge-stream@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "merge-stream@npm:2.0.0"
+ checksum: 6fa4dcc8d86629705cea944a4b88ef4cb0e07656ebf223fa287443256414283dd25d91c1cd84c77987f2aec5927af1a9db6085757cb43d90eb170ebf4b47f4f4
+ languageName: node
+ linkType: hard
+
"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
version: 1.4.1
resolution: "merge2@npm:1.4.1"
@@ -1447,6 +1504,15 @@ __metadata:
languageName: node
linkType: hard
+"npm-run-path@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "npm-run-path@npm:5.1.0"
+ dependencies:
+ path-key: ^4.0.0
+ checksum: dc184eb5ec239d6a2b990b43236845332ef12f4e0beaa9701de724aa797fe40b6bbd0157fb7639d24d3ab13f5d5cf22d223a19c6300846b8126f335f788bee66
+ languageName: node
+ linkType: hard
+
"npmlog@npm:^6.0.0":
version: 6.0.2
resolution: "npmlog@npm:6.0.2"
@@ -1468,6 +1534,15 @@ __metadata:
languageName: node
linkType: hard
+"onetime@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "onetime@npm:6.0.0"
+ dependencies:
+ mimic-fn: ^4.0.0
+ checksum: 0846ce78e440841335d4e9182ef69d5762e9f38aa7499b19f42ea1c4cd40f0b4446094c455c713f9adac3f4ae86f613bb5e30c99e52652764d06a89f709b3788
+ languageName: node
+ linkType: hard
+
"p-defer@npm:^1.0.0":
version: 1.0.0
resolution: "p-defer@npm:1.0.0"
@@ -1555,6 +1630,13 @@ __metadata:
languageName: node
linkType: hard
+"path-key@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "path-key@npm:4.0.0"
+ checksum: 8e6c314ae6d16b83e93032c61020129f6f4484590a777eed709c4a01b50e498822b00f76ceaf94bc64dbd90b327df56ceadce27da3d83393790f1219e07721d7
+ languageName: node
+ linkType: hard
+
"path-scurry@npm:^1.10.1":
version: 1.10.1
resolution: "path-scurry@npm:1.10.1"
@@ -1891,6 +1973,13 @@ __metadata:
languageName: node
linkType: hard
+"strip-final-newline@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "strip-final-newline@npm:3.0.0"
+ checksum: 23ee263adfa2070cd0f23d1ac14e2ed2f000c9b44229aec9c799f1367ec001478469560abefd00c5c99ee6f0b31c137d53ec6029c53e9f32a93804e18c201050
+ languageName: node
+ linkType: hard
+
"supertap@npm:^3.0.1":
version: 3.0.1
resolution: "supertap@npm:3.0.1"
@@ -1947,6 +2036,26 @@ __metadata:
languageName: node
linkType: hard
+"typescript@npm:^5.2.2":
+ version: 5.2.2
+ resolution: "typescript@npm:5.2.2"
+ bin:
+ tsc: bin/tsc
+ tsserver: bin/tsserver
+ checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c
+ languageName: node
+ linkType: hard
+
+"typescript@patch:typescript@^5.2.2#~builtin<compat/typescript>":
+ version: 5.2.2
+ resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin<compat/typescript>::version=5.2.2&hash=f3b441"
+ bin:
+ tsc: bin/tsc
+ tsserver: bin/tsserver
+ checksum: 0f4da2f15e6f1245e49db15801dbee52f2bbfb267e1c39225afdab5afee1a72839cd86000e65ee9d7e4dfaff12239d28beaf5ee431357fcced15fb08583d72ca
+ languageName: node
+ linkType: hard
+
"unique-filename@npm:^3.0.0":
version: 3.0.0
resolution: "unique-filename@npm:3.0.0"