@litko/yara-x 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +34 -28
  2. package/index.d.ts +95 -226
  3. package/index.js +352 -271
  4. package/package.json +27 -19
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # @litko/yara-x
2
2
 
3
- **v0.1.1**
4
-
5
3
  ## Features
6
4
 
7
5
  - High Performance: Built with [napi-rs](https://napi-rs.com) and [VirusTotal/yara-x](https://github.com/VirusTotal/yara-x)
@@ -363,31 +361,42 @@ if (warnings.length > 0) {
363
361
 
364
362
  ## Performance Benchmarks
365
363
 
366
- **Test Setup:**
364
+ `node-yara-x` delivers exceptional performance through intelligent scanner caching and optimized Rust implementation.
365
+
366
+ ### Benchmark Results
367
+
368
+ Test Environment: MacBook Pro M3 Max, 36GB RAM, Release build with LTO
369
+ Methodology: Statistical analysis across multiple iterations with percentile reporting
370
+
371
+ #### Scanner Creation Performance
367
372
 
368
- - **Hardware:** MacBook Pro (M3 Max, 36GB RAM)
369
- - **Test Data:** Generated data of varying sizes (small: 64 bytes, medium: 100KB, large: 10MB). See `__test__/benchmark.mjs` for data generation and benchmarking code.
370
- - The Large test file (10MB) is auto-generated, to prevent bloating the size of the repository.
373
+ | Rule Type | Mean | p50 | p95 | p99 |
374
+ | -------------- | ------ | ------ | ------ | ------ |
375
+ | Simple Rule | 2.44ms | 2.42ms | 2.67ms | 3.00ms |
376
+ | Complex Rule | 2.79ms | 2.78ms | 3.00ms | 3.26ms |
377
+ | Regex Rule | 8.63ms | 8.63ms | 8.91ms | 9.34ms |
378
+ | Multiple Rules | 2.42ms | 2.39ms | 2.69ms | 3.07ms |
371
379
 
372
- **Key Metrics (Averages):**
380
+ #### Scanning Performance by Data Size
373
381
 
374
- | Operation | Average Time | Iterations | p50 | p95 | p99 |
375
- | :---------------------------------------------- | -----------: | ---------: | -------: | -------: | -------: |
376
- | Scanner Creation (Simple Rule) | 1.675 ms | 100 | 1.547 ms | 2.318 ms | 2.657 ms |
377
- | Scanner Creation (Complex Rule) | 1.878 ms | 100 | 1.848 ms | 2.005 ms | 2.865 ms |
378
- | Scanner Creation (Regex Rule) | 2.447 ms | 100 | 2.444 ms | 2.473 ms | 2.569 ms |
379
- | Scanner Creation (Multiple Rules) | 1.497 ms | 100 | 1.488 ms | 1.547 ms | 1.819 ms |
380
- | Scanning Small Data (64 bytes, Simple Rule) | 0.145 ms | 1000 | 0.143 ms | 0.156 ms | 0.169 ms |
381
- | Scanning Medium Data (100KB, Simple Rule) | 0.151 ms | 100 | 0.146 ms | 0.179 ms | 0.205 ms |
382
- | Scanning Large Data (10MB, Simple Rule) | 0.347 ms | 10 | 0.340 ms | 0.394 ms | 0.394 ms |
383
- | Scanning Medium Data (100KB, Complex Rule) | 0.219 ms | 100 | 0.215 ms | 0.254 ms | 0.269 ms |
384
- | Scanning Medium Data (100KB, Regex Rule) | 0.156 ms | 100 | 0.152 ms | 0.182 ms | 0.210 ms |
385
- | Scanning Medium Data (100KB, Multiple Rules) | 0.218 ms | 100 | 0.212 ms | 0.261 ms | 0.353 ms |
386
- | Async Scanning Medium Data (100KB, Simple Rule) | 0.012 ms | 100 | 0.011 ms | 0.016 ms | 0.027ms |
387
- | Scanning with Variables | 0.143 ms | 1000 | 0.140 ms | 0.155 ms | 0.166 ms |
388
- | Scanning with Variables (Override at Scan Time) | 0.144 ms | 1000 | 0.142 ms | 0.158 ms | 0.175 ms |
382
+ | Data Size | Rule Type | Mean | Throughput |
383
+ | --------- | -------------- | ----- | ---------- |
384
+ | 64 bytes | Simple | 3μs | ~21 MB/s |
385
+ | 100KB | Simple | 6μs | ~16.7 GB/s |
386
+ | 100KB | Complex | 71μs | ~1.4 GB/s |
387
+ | 100KB | Regex | 7μs | ~14.3 GB/s |
388
+ | 100KB | Multiple Rules | 71μs | ~1.4 GB/s |
389
+ | 10MB | Simple | 196μs | ~51 GB/s |
389
390
 
390
- # API Reference
391
+ #### Advanced Features Performance
392
+
393
+ | Feature | Mean | Notes |
394
+ | ----------------- | ---- | -------------------------- |
395
+ | Variable Scanning | 1μs | Pre-compiled variables |
396
+ | Runtime Variables | 2μs | Variables set at scan time |
397
+ | Async Scanning | 12μs | Non-blocking operations |
398
+
399
+ ## API Reference
391
400
 
392
401
  ### Functions
393
402
 
@@ -395,7 +404,7 @@ if (warnings.length > 0) {
395
404
  - `compileToWasm(ruleSource: string, outputPath: string, options?: CompilerOptions)` - Compiles yara rules from a string to WASM file.
396
405
  - `compileFileToWasm(rulesPath: string, outputPath: string, options?: CompilerOptions)` - Compiles yara rules from a file to WASM file.
397
406
  - `validate(ruleSource: string, options?: CompilerOptions)` - Validates yara rules without executing them.
398
- - `create(options?: CompilerOptions)` - Creates an empty rules scanner to add rules incrementally.
407
+ - `create()` - Creates an empty rules scanner to add rules incrementally.
399
408
  - `fromFile(rulePath: string, options?: CompilerOptions)` - Compiles yara rules from a file.
400
409
 
401
410
  ### yarax Methods
@@ -409,10 +418,7 @@ if (warnings.length > 0) {
409
418
  - `emitWasmFileAsync(filePath: string)` - Emit compiled rules to WASM file asynchronously.
410
419
  - `addRuleSource(rules: string)` - Add rules from a string to an existing scanner.
411
420
  - `addRuleFile(filePath: string)` - Add rules from a file to an existing scanner.
412
-
413
- ### Rule Validation
414
-
415
- - `validate(rules: string, options?: CompilerOptions)` - Validate yara rules without executing them.
421
+ - `defineVariable(name: string, value: string)` - Define a variable for the YARA compiler.
416
422
 
417
423
  ## Licenses
418
424
 
package/index.d.ts CHANGED
@@ -1,42 +1,67 @@
1
- /* tslint:disable */
1
+ /* auto-generated by NAPI-RS */
2
2
  /* eslint-disable */
3
+ /** YaraX struct represents the YARA rules and their associated data. */
4
+ export declare class YaraX {
5
+ getWarnings(): Array<CompilerWarning>
6
+ /** Scans the provided data using the compiled YARA rules. */
7
+ scan(data: Buffer, variables?: Record<string, string | number>): Array<RuleMatch>
8
+ /** Scans a file using the compiled YARA rules. */
9
+ scanFile(filePath: string, variables?: Record<string, string | number>): Array<RuleMatch>
10
+ /** Emits a WASM file from the compiled YARA rules. */
11
+ emitWasmFile(outputPath: string): void
12
+ /** Scans the provided data asynchronously using the compiled YARA rules. */
13
+ scanAsync(data: Buffer, variables?: object | undefined | null): Promise<unknown>
14
+ /** Scans a file asynchronously using the compiled YARA rules. */
15
+ scanFileAsync(filePath: string, variables?: object | undefined | null): Promise<unknown>
16
+ /** Emits a WASM file asynchronously from the compiled YARA rules. */
17
+ emitWasmFileAsync(outputPath: string): Promise<unknown>
18
+ /** Adds a rule source to the YARA compiler. */
19
+ addRuleSource(ruleSource: string): void
20
+ /** Adds a rule file to the YARA compiler. */
21
+ addRuleFile(filePath: string): void
22
+ /** Defines a variable for the YARA compiler. */
23
+ defineVariable(name: string, value: string): void
24
+ }
3
25
 
4
- /* auto-generated by NAPI-RS */
26
+ /** BannedModule struct represents a module that is banned from being used in YARA rules. */
27
+ export interface BannedModule {
28
+ /** The name of the banned module. */
29
+ name: string
30
+ /** The title of the error message if the module is used. */
31
+ errorTitle: string
32
+ /** The error message if the module is used. */
33
+ errorMessage: string
34
+ }
5
35
 
6
- /** MatchData struct represents a match found by a YARA rule. */
7
- export interface MatchData {
8
- /** The offset of the match in the scanned data. */
9
- offset: number
10
- /** The length of the matched data. */
11
- length: number
12
- /** The matched data as a string. */
13
- data: string
14
- /** The identifier of the pattern that matched. */
15
- identifier: string
36
+ /** Compiles a YARA rule source string and returns a YaraX instance with the compiled rules. */
37
+ export declare function compile(ruleSource: string, options?: CompilerOptions | undefined | null): YaraX
38
+
39
+ /** Compiles a YARA rule file to a WASM file. */
40
+ export declare function compileFileToWasm(rulePath: string, outputPath: string, options?: CompilerOptions | undefined | null): void
41
+
42
+ /** CompilerError struct represents an error generated by the YARA compiler. */
43
+ export interface CompilerError {
44
+ /** The code of the error. */
45
+ code: string
46
+ /** The message of the error. */
47
+ message: string
48
+ /** The source of the error, if available. */
49
+ source?: string
50
+ /** The line number where the error occurred, if available. */
51
+ line?: number
52
+ /** The column number where the error occurred, if available. */
53
+ column?: number
16
54
  }
17
- /**
18
- * RuleMatch struct represents a matching rule found during scanning.
19
- *
20
- * See [yara_::Match](https://docs.rs/yara-x/latest/yara_x/struct.Match.html) for more details.
21
- */
22
- export interface RuleMatch {
23
- /** The identifier of the rule that matched. */
24
- ruleIdentifier: string
25
- /** The namespace of the rule that matched. */
26
- namespace: string
27
- /** The metadata associated with the rule that matched. */
28
- meta: object
29
- /** The tags associated with the rule that matched. */
30
- tags: Array<string>
31
- /** The matches found by the rule. */
32
- matches: Array<MatchData>
55
+
56
+ /** CompileResult struct represents the result of compiling YARA rules. */
57
+ export interface CompileResult {
58
+ /** Any warnings generated during the compilation process. */
59
+ warnings: Array<CompilerWarning>
60
+ /** Any errors generated during the compilation process. */
61
+ errors: Array<CompilerError>
33
62
  }
34
- /**
35
- * CompilerOptions struct represents the options for the YARA compiler.
36
- *
37
- * See [yara_x::Compiler](https://docs.rs/yara-x/latest/yara_x/struct.Compiler.html) for more
38
- * details.
39
- */
63
+
64
+ /** CompilerOptions struct represents the options for the YARA compiler. */
40
65
  export interface CompilerOptions {
41
66
  /** Defines global variables for the YARA rules. */
42
67
  defineVariables?: object
@@ -55,21 +80,8 @@ export interface CompilerOptions {
55
80
  /** Whether to raise an error on slow loops. */
56
81
  errorOnSlowLoop?: boolean
57
82
  }
58
- /** BannedModule struct represents a module that is banned from being used in YARA rules. */
59
- export interface BannedModule {
60
- /** The name of the banned module. */
61
- name: string
62
- /** The title of the error message if the module is used. */
63
- errorTitle: string
64
- /** The error message if the module is used. */
65
- errorMessage: string
66
- }
67
- /**
68
- * CompilerWarning struct represents a warning generated by the YARA compiler.
69
- *
70
- * See [yara_x::CompilerWarning](https://docs.rs/yara-x/latest/yara_x/warnings/enum.Warning.html)
71
- * for more details.
72
- */
83
+
84
+ /** CompilerWarning struct represents a warning generated by the YARA compiler. */
73
85
  export interface CompilerWarning {
74
86
  /** The code of the warning. */
75
87
  code: string
@@ -82,187 +94,44 @@ export interface CompilerWarning {
82
94
  /** The column number where the warning occurred, if available. */
83
95
  column?: number
84
96
  }
85
- /**
86
- * CompilerError struct represents an error generated by the YARA compiler.
87
- *
88
- * See
89
- * [yara_x::CompileError](https://docs.rs/yara-x/latest/yara_x/errors/enum.CompileError.html)
90
- * for more details.
91
- */
92
- export interface CompilerError {
93
- /** The code of the error. */
94
- code: string
95
- /** The message of the error. */
96
- message: string
97
- /** The source of the error, if available. */
98
- source?: string
99
- /** The line number where the error occurred, if available. */
100
- line?: number
101
- /** The column number where the error occurred, if available. */
102
- column?: number
97
+
98
+ /** Compiles a YARA rule source string to a WASM file. */
99
+ export declare function compileToWasm(ruleSource: string, outputPath: string, options?: CompilerOptions | undefined | null): void
100
+
101
+ /** Creates a new YaraX instance with empty rules and no source code. */
102
+ export declare function create(): YaraX
103
+
104
+ /** Creates a new YaraX instance from a file containing YARA rules. */
105
+ export declare function fromFile(rulePath: string, options?: CompilerOptions | undefined | null): YaraX
106
+
107
+ /** MatchData struct represents a match found by a YARA rule. */
108
+ export interface MatchData {
109
+ /** The offset of the match in the scanned data. */
110
+ offset: number
111
+ /** The length of the matched data. */
112
+ length: number
113
+ /** The matched data as a string. */
114
+ data: string
115
+ /** The identifier of the pattern that matched. */
116
+ identifier: string
103
117
  }
104
- /**
105
- * CompileResult struct represents the result of compiling YARA rules.
106
- * It contains any warnings or errors generated during the compilation process.
107
- */
108
- export interface CompileResult {
109
- /** Any warnings generated during the compilation process. */
110
- warnings: Array<CompilerWarning>
111
- /** Any errors generated during the compilation process. */
112
- errors: Array<CompilerError>
118
+
119
+ /** RuleMatch struct represents a matching rule found during scanning. */
120
+ export interface RuleMatch {
121
+ /** The identifier of the rule that matched. */
122
+ ruleIdentifier: string
123
+ /** The namespace of the rule that matched. */
124
+ namespace: string
125
+ /** The metadata associated with the rule that matched. */
126
+ meta: object
127
+ /** The tags associated with the rule that matched. */
128
+ tags: Array<string>
129
+ /** The matches found by the rule. */
130
+ matches: Array<MatchData>
113
131
  }
132
+
114
133
  /**
115
134
  * Compiles a YARA rule source string and returns any warnings or errors generated during the
116
135
  * compilation process.
117
- *
118
- * Exported as `validate` in the NAPI interface.
119
- *
120
- * Example
121
- *
122
- * ```javascript
123
- * const { validate } = require('your_yara_module');
124
- * const result = validate('rule example { strings: $a = "example" condition: $a }');
125
- * ```
126
136
  */
127
137
  export declare function validate(ruleSource: string, options?: CompilerOptions | undefined | null): CompileResult
128
- /**
129
- * Compiles a YARA rule source string and returns a YaraX instance with the compiled rules.
130
- *
131
- * Exported as `compile` in the NAPI interface.
132
- *
133
- * Example
134
- *
135
- * ```javascript
136
- * const { compile } = require('your_yara_module');
137
- * const yarax = compile('rule example { strings: $a = "example" condition: $a }');
138
- * ```
139
- */
140
- export declare function compile(ruleSource: string, options?: CompilerOptions | undefined | null): YaraX
141
- /**
142
- * Creates a new YaraX instance with empty rules and no source code.
143
- *
144
- * Exported as `create` in the NAPI interface.
145
- *
146
- * Example
147
- *
148
- * ```javascript
149
- * const { create } = require('your_yara_module');
150
- * const yarax = create();
151
- *
152
- * // Now you can add rules or compile them later
153
- *
154
- * yarax.addRuleSource('rule example { strings: $a = "example" condition: $a }');
155
- * yarax.addRuleFile('path/to/rule_file.yar');
156
- * yarax.defineVariable('myVar', 'myValue');
157
- * ```
158
- */
159
- export declare function create(): YaraX
160
- /**
161
- * Creates a new YaraX instance from a file containing YARA rules.
162
- *
163
- * Exported as `fromFile` in the NAPI interface.
164
- *
165
- * Example
166
- *
167
- * ```javascript
168
- * const { fromFile } = require('your_yara_module');
169
- * const yarax = fromFile('path/to/rule_file.yar');
170
- * ```
171
- */
172
- export declare function fromFile(rulePath: string, options?: CompilerOptions | undefined | null): YaraX
173
- /**
174
- * Compiles a YARA rule source string to a WASM file.
175
- *
176
- * Exported as `compileToWasm` in the NAPI interface.
177
- *
178
- * Example
179
- *
180
- * ```javascript
181
- * const { compileToWasm } = require('your_yara_module');
182
- * compileToWasm('rule example { strings: $a = "example" condition: $a }', 'output.wasm');
183
- * ```
184
- */
185
- export declare function compileToWasm(ruleSource: string, outputPath: string, options?: CompilerOptions | undefined | null): void
186
- /**
187
- * Compiles a YARA rule file to a WASM file.
188
- *
189
- * Exported as `compileFileToWasm` in the NAPI interface.
190
- *
191
- * Example
192
- *
193
- * ```javascript
194
- * const { compileFileToWasm } = require('your_yara_module');
195
- * compileFileToWasm('path/to/rule_file.yar', 'output.wasm');
196
- * ```
197
- */
198
- export declare function compileFileToWasm(rulePath: string, outputPath: string, options?: CompilerOptions | undefined | null): void
199
- /**
200
- * YaraX struct represents the YARA rules and their associated data.
201
- * It contains the compiled rules, source code, warnings, and variables.
202
- *
203
- * See [yara_x::Rules](https://docs.rs/yara-x/latest/yara_x/struct.Rules.html) for more details.
204
- */
205
- export declare class YaraX {
206
- getWarnings(): Array<CompilerWarning>
207
- /**
208
- * Scans the provided data using the compiled YARA rules.
209
- * This function takes the scanned data and an optional object of variables,
210
- * and returns a vector of RuleMatch representing the matching rules found during the scan.
211
- */
212
- scan(data: Buffer, variables?: Record<string, string | number>): Array<RuleMatch>
213
- /**
214
- * Scans a file using the compiled YARA rules.
215
- * This function takes the file path and an optional object of variables,
216
- * and returns a vector of RuleMatch representing the matching rules found during the scan.
217
- */
218
- scanFile(filePath: string, variables?: Record<string, string | number>): Array<RuleMatch>
219
- /**
220
- * Emits a WASM file from the compiled YARA rules.
221
- * This function takes the output path and writes the compiled rules to a WASM file.
222
- */
223
- emitWasmFile(outputPath: string): void
224
- /**
225
- * Scans the provided data asynchronously using the compiled YARA rules.
226
- * This function takes the scanned data and an optional object of variables,
227
- * and returns an AsyncTask that will resolve to a vector of RuleMatch representing the matching
228
- * rules found during the scan.
229
- *
230
- * This allows for non-blocking scanning of data, which can be useful for large datasets or
231
- * performance-critical applications.
232
- */
233
- scanAsync(data: Buffer, variables?: object | undefined | null): Promise<unknown>
234
- /**
235
- * Scans a file asynchronously using the compiled YARA rules.
236
- * This function takes the file path and an optional object of variables,
237
- * and returns an AsyncTask that will resolve to a vector of RuleMatch representing the matching
238
- * rules found during the scan.
239
- *
240
- * This allows for non-blocking scanning of files, which can be useful for large files or
241
- * performance-critical applications.
242
- */
243
- scanFileAsync(filePath: string, variables?: object | undefined | null): Promise<unknown>
244
- /**
245
- * Emits a WASM file asynchronously from the compiled YARA rules.
246
- * This function takes the output path
247
- * and returns an AsyncTask that will resolve when the WASM file is successfully emitted.
248
- */
249
- emitWasmFileAsync(outputPath: string): Promise<unknown>
250
- /**
251
- * Adds a rule source to the YARA compiler.
252
- * This function takes a rule source string,
253
- * compiles it, and updates the YaraX instance with the new rules.
254
- */
255
- addRuleSource(ruleSource: string): void
256
- /**
257
- * Adds a rule file to the YARA compiler.
258
- * This function takes a file path,
259
- * reads the file content, and adds it to the YaraX instance.
260
- */
261
- addRuleFile(filePath: string): void
262
- /**
263
- * Defines a variable for the YARA compiler.
264
- * This function takes a variable name and value,
265
- * and adds it to the YaraX instance.
266
- */
267
- defineVariable(name: string, value: string): void
268
- }
package/index.js CHANGED
@@ -1,321 +1,402 @@
1
- /* tslint:disable */
1
+ // prettier-ignore
2
2
  /* eslint-disable */
3
- /* prettier-ignore */
4
-
3
+ // @ts-nocheck
5
4
  /* auto-generated by NAPI-RS */
6
5
 
7
- const { existsSync, readFileSync } = require('fs')
8
- const { join } = require('path')
9
-
10
- const { platform, arch } = process
6
+ const { createRequire } = require('node:module')
7
+ require = createRequire(__filename)
11
8
 
9
+ const { readFileSync } = require('node:fs')
12
10
  let nativeBinding = null
13
- let localFileExisted = false
14
- let loadError = null
11
+ const loadErrors = []
15
12
 
16
- function isMusl() {
17
- // For Node 10
18
- if (!process.report || typeof process.report.getReport !== 'function') {
19
- try {
20
- const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
- return readFileSync(lddPath, 'utf8').includes('musl')
22
- } catch (e) {
13
+ const isMusl = () => {
14
+ let musl = false
15
+ if (process.platform === 'linux') {
16
+ musl = isMuslFromFilesystem()
17
+ if (musl === null) {
18
+ musl = isMuslFromReport()
19
+ }
20
+ if (musl === null) {
21
+ musl = isMuslFromChildProcess()
22
+ }
23
+ }
24
+ return musl
25
+ }
26
+
27
+ const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
28
+
29
+ const isMuslFromFilesystem = () => {
30
+ try {
31
+ return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
32
+ } catch {
33
+ return null
34
+ }
35
+ }
36
+
37
+ const isMuslFromReport = () => {
38
+ let report = null
39
+ if (typeof process.report?.getReport === 'function') {
40
+ process.report.excludeNetwork = true
41
+ report = process.report.getReport()
42
+ }
43
+ if (!report) {
44
+ return null
45
+ }
46
+ if (report.header && report.header.glibcVersionRuntime) {
47
+ return false
48
+ }
49
+ if (Array.isArray(report.sharedObjects)) {
50
+ if (report.sharedObjects.some(isFileMusl)) {
23
51
  return true
24
52
  }
25
- } else {
26
- const { glibcVersionRuntime } = process.report.getReport().header
27
- return !glibcVersionRuntime
28
53
  }
54
+ return false
29
55
  }
30
56
 
31
- switch (platform) {
32
- case 'android':
33
- switch (arch) {
34
- case 'arm64':
35
- localFileExisted = existsSync(join(__dirname, 'yara-x.android-arm64.node'))
57
+ const isMuslFromChildProcess = () => {
58
+ try {
59
+ return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
60
+ } catch (e) {
61
+ // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
62
+ return false
63
+ }
64
+ }
65
+
66
+ function requireNative() {
67
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
68
+ try {
69
+ nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
70
+ } catch (err) {
71
+ loadErrors.push(err)
72
+ }
73
+ } else if (process.platform === 'android') {
74
+ if (process.arch === 'arm64') {
75
+ try {
76
+ return require('./yara-x.android-arm64.node')
77
+ } catch (e) {
78
+ loadErrors.push(e)
79
+ }
80
+ try {
81
+ return require('@litko/yara-x-android-arm64')
82
+ } catch (e) {
83
+ loadErrors.push(e)
84
+ }
85
+ } else if (process.arch === 'arm') {
86
+ try {
87
+ return require('./yara-x.android-arm-eabi.node')
88
+ } catch (e) {
89
+ loadErrors.push(e)
90
+ }
91
+ try {
92
+ return require('@litko/yara-x-android-arm-eabi')
93
+ } catch (e) {
94
+ loadErrors.push(e)
95
+ }
96
+ } else {
97
+ loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
98
+ }
99
+ } else if (process.platform === 'win32') {
100
+ if (process.arch === 'x64') {
101
+ try {
102
+ return require('./yara-x.win32-x64-msvc.node')
103
+ } catch (e) {
104
+ loadErrors.push(e)
105
+ }
106
+ try {
107
+ return require('@litko/yara-x-win32-x64-msvc')
108
+ } catch (e) {
109
+ loadErrors.push(e)
110
+ }
111
+ } else if (process.arch === 'ia32') {
112
+ try {
113
+ return require('./yara-x.win32-ia32-msvc.node')
114
+ } catch (e) {
115
+ loadErrors.push(e)
116
+ }
117
+ try {
118
+ return require('@litko/yara-x-win32-ia32-msvc')
119
+ } catch (e) {
120
+ loadErrors.push(e)
121
+ }
122
+ } else if (process.arch === 'arm64') {
123
+ try {
124
+ return require('./yara-x.win32-arm64-msvc.node')
125
+ } catch (e) {
126
+ loadErrors.push(e)
127
+ }
128
+ try {
129
+ return require('@litko/yara-x-win32-arm64-msvc')
130
+ } catch (e) {
131
+ loadErrors.push(e)
132
+ }
133
+ } else {
134
+ loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
135
+ }
136
+ } else if (process.platform === 'darwin') {
137
+ try {
138
+ return require('./yara-x.darwin-universal.node')
139
+ } catch (e) {
140
+ loadErrors.push(e)
141
+ }
142
+ try {
143
+ return require('@litko/yara-x-darwin-universal')
144
+ } catch (e) {
145
+ loadErrors.push(e)
146
+ }
147
+ if (process.arch === 'x64') {
148
+ try {
149
+ return require('./yara-x.darwin-x64.node')
150
+ } catch (e) {
151
+ loadErrors.push(e)
152
+ }
153
+ try {
154
+ return require('@litko/yara-x-darwin-x64')
155
+ } catch (e) {
156
+ loadErrors.push(e)
157
+ }
158
+ } else if (process.arch === 'arm64') {
159
+ try {
160
+ return require('./yara-x.darwin-arm64.node')
161
+ } catch (e) {
162
+ loadErrors.push(e)
163
+ }
164
+ try {
165
+ return require('@litko/yara-x-darwin-arm64')
166
+ } catch (e) {
167
+ loadErrors.push(e)
168
+ }
169
+ } else {
170
+ loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
171
+ }
172
+ } else if (process.platform === 'freebsd') {
173
+ if (process.arch === 'x64') {
174
+ try {
175
+ return require('./yara-x.freebsd-x64.node')
176
+ } catch (e) {
177
+ loadErrors.push(e)
178
+ }
179
+ try {
180
+ return require('@litko/yara-x-freebsd-x64')
181
+ } catch (e) {
182
+ loadErrors.push(e)
183
+ }
184
+ } else if (process.arch === 'arm64') {
185
+ try {
186
+ return require('./yara-x.freebsd-arm64.node')
187
+ } catch (e) {
188
+ loadErrors.push(e)
189
+ }
190
+ try {
191
+ return require('@litko/yara-x-freebsd-arm64')
192
+ } catch (e) {
193
+ loadErrors.push(e)
194
+ }
195
+ } else {
196
+ loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
197
+ }
198
+ } else if (process.platform === 'linux') {
199
+ if (process.arch === 'x64') {
200
+ if (isMusl()) {
36
201
  try {
37
- if (localFileExisted) {
38
- nativeBinding = require('./yara-x.android-arm64.node')
39
- } else {
40
- nativeBinding = require('@litko/yara-x-android-arm64')
41
- }
202
+ return require('./yara-x.linux-x64-musl.node')
42
203
  } catch (e) {
43
- loadError = e
204
+ loadErrors.push(e)
44
205
  }
45
- break
46
- case 'arm':
47
- localFileExisted = existsSync(join(__dirname, 'yara-x.android-arm-eabi.node'))
48
206
  try {
49
- if (localFileExisted) {
50
- nativeBinding = require('./yara-x.android-arm-eabi.node')
51
- } else {
52
- nativeBinding = require('@litko/yara-x-android-arm-eabi')
53
- }
207
+ return require('@litko/yara-x-linux-x64-musl')
54
208
  } catch (e) {
55
- loadError = e
209
+ loadErrors.push(e)
56
210
  }
57
- break
58
- default:
59
- throw new Error(`Unsupported architecture on Android ${arch}`)
60
- }
61
- break
62
- case 'win32':
63
- switch (arch) {
64
- case 'x64':
65
- localFileExisted = existsSync(
66
- join(__dirname, 'yara-x.win32-x64-msvc.node')
67
- )
211
+ } else {
68
212
  try {
69
- if (localFileExisted) {
70
- nativeBinding = require('./yara-x.win32-x64-msvc.node')
71
- } else {
72
- nativeBinding = require('@litko/yara-x-win32-x64-msvc')
73
- }
213
+ return require('./yara-x.linux-x64-gnu.node')
74
214
  } catch (e) {
75
- loadError = e
215
+ loadErrors.push(e)
76
216
  }
77
- break
78
- case 'ia32':
79
- localFileExisted = existsSync(
80
- join(__dirname, 'yara-x.win32-ia32-msvc.node')
81
- )
82
217
  try {
83
- if (localFileExisted) {
84
- nativeBinding = require('./yara-x.win32-ia32-msvc.node')
85
- } else {
86
- nativeBinding = require('@litko/yara-x-win32-ia32-msvc')
87
- }
218
+ return require('@litko/yara-x-linux-x64-gnu')
88
219
  } catch (e) {
89
- loadError = e
220
+ loadErrors.push(e)
90
221
  }
91
- break
92
- case 'arm64':
93
- localFileExisted = existsSync(
94
- join(__dirname, 'yara-x.win32-arm64-msvc.node')
95
- )
222
+ }
223
+ } else if (process.arch === 'arm64') {
224
+ if (isMusl()) {
96
225
  try {
97
- if (localFileExisted) {
98
- nativeBinding = require('./yara-x.win32-arm64-msvc.node')
99
- } else {
100
- nativeBinding = require('@litko/yara-x-win32-arm64-msvc')
101
- }
226
+ return require('./yara-x.linux-arm64-musl.node')
102
227
  } catch (e) {
103
- loadError = e
228
+ loadErrors.push(e)
229
+ }
230
+ try {
231
+ return require('@litko/yara-x-linux-arm64-musl')
232
+ } catch (e) {
233
+ loadErrors.push(e)
104
234
  }
105
- break
106
- default:
107
- throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
- }
109
- break
110
- case 'darwin':
111
- localFileExisted = existsSync(join(__dirname, 'yara-x.darwin-universal.node'))
112
- try {
113
- if (localFileExisted) {
114
- nativeBinding = require('./yara-x.darwin-universal.node')
115
235
  } else {
116
- nativeBinding = require('@litko/yara-x-darwin-universal')
236
+ try {
237
+ return require('./yara-x.linux-arm64-gnu.node')
238
+ } catch (e) {
239
+ loadErrors.push(e)
240
+ }
241
+ try {
242
+ return require('@litko/yara-x-linux-arm64-gnu')
243
+ } catch (e) {
244
+ loadErrors.push(e)
245
+ }
117
246
  }
118
- break
119
- } catch {}
120
- switch (arch) {
121
- case 'x64':
122
- localFileExisted = existsSync(join(__dirname, 'yara-x.darwin-x64.node'))
247
+ } else if (process.arch === 'arm') {
248
+ if (isMusl()) {
123
249
  try {
124
- if (localFileExisted) {
125
- nativeBinding = require('./yara-x.darwin-x64.node')
126
- } else {
127
- nativeBinding = require('@litko/yara-x-darwin-x64')
128
- }
250
+ return require('./yara-x.linux-arm-musleabihf.node')
129
251
  } catch (e) {
130
- loadError = e
252
+ loadErrors.push(e)
131
253
  }
132
- break
133
- case 'arm64':
134
- localFileExisted = existsSync(
135
- join(__dirname, 'yara-x.darwin-arm64.node')
136
- )
137
254
  try {
138
- if (localFileExisted) {
139
- nativeBinding = require('./yara-x.darwin-arm64.node')
140
- } else {
141
- nativeBinding = require('@litko/yara-x-darwin-arm64')
142
- }
255
+ return require('@litko/yara-x-linux-arm-musleabihf')
143
256
  } catch (e) {
144
- loadError = e
257
+ loadErrors.push(e)
145
258
  }
146
- break
147
- default:
148
- throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
- }
150
- break
151
- case 'freebsd':
152
- if (arch !== 'x64') {
153
- throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
- }
155
- localFileExisted = existsSync(join(__dirname, 'yara-x.freebsd-x64.node'))
156
- try {
157
- if (localFileExisted) {
158
- nativeBinding = require('./yara-x.freebsd-x64.node')
159
259
  } else {
160
- nativeBinding = require('@litko/yara-x-freebsd-x64')
161
- }
162
- } catch (e) {
163
- loadError = e
164
- }
165
- break
166
- case 'linux':
167
- switch (arch) {
168
- case 'x64':
169
- if (isMusl()) {
170
- localFileExisted = existsSync(
171
- join(__dirname, 'yara-x.linux-x64-musl.node')
172
- )
173
- try {
174
- if (localFileExisted) {
175
- nativeBinding = require('./yara-x.linux-x64-musl.node')
176
- } else {
177
- nativeBinding = require('@litko/yara-x-linux-x64-musl')
178
- }
179
- } catch (e) {
180
- loadError = e
181
- }
182
- } else {
183
- localFileExisted = existsSync(
184
- join(__dirname, 'yara-x.linux-x64-gnu.node')
185
- )
186
- try {
187
- if (localFileExisted) {
188
- nativeBinding = require('./yara-x.linux-x64-gnu.node')
189
- } else {
190
- nativeBinding = require('@litko/yara-x-linux-x64-gnu')
191
- }
192
- } catch (e) {
193
- loadError = e
194
- }
260
+ try {
261
+ return require('./yara-x.linux-arm-gnueabihf.node')
262
+ } catch (e) {
263
+ loadErrors.push(e)
195
264
  }
196
- break
197
- case 'arm64':
198
- if (isMusl()) {
199
- localFileExisted = existsSync(
200
- join(__dirname, 'yara-x.linux-arm64-musl.node')
201
- )
202
- try {
203
- if (localFileExisted) {
204
- nativeBinding = require('./yara-x.linux-arm64-musl.node')
205
- } else {
206
- nativeBinding = require('@litko/yara-x-linux-arm64-musl')
207
- }
208
- } catch (e) {
209
- loadError = e
210
- }
211
- } else {
212
- localFileExisted = existsSync(
213
- join(__dirname, 'yara-x.linux-arm64-gnu.node')
214
- )
215
- try {
216
- if (localFileExisted) {
217
- nativeBinding = require('./yara-x.linux-arm64-gnu.node')
218
- } else {
219
- nativeBinding = require('@litko/yara-x-linux-arm64-gnu')
220
- }
221
- } catch (e) {
222
- loadError = e
223
- }
265
+ try {
266
+ return require('@litko/yara-x-linux-arm-gnueabihf')
267
+ } catch (e) {
268
+ loadErrors.push(e)
224
269
  }
225
- break
226
- case 'arm':
227
- if (isMusl()) {
228
- localFileExisted = existsSync(
229
- join(__dirname, 'yara-x.linux-arm-musleabihf.node')
230
- )
231
- try {
232
- if (localFileExisted) {
233
- nativeBinding = require('./yara-x.linux-arm-musleabihf.node')
234
- } else {
235
- nativeBinding = require('@litko/yara-x-linux-arm-musleabihf')
236
- }
237
- } catch (e) {
238
- loadError = e
239
- }
240
- } else {
241
- localFileExisted = existsSync(
242
- join(__dirname, 'yara-x.linux-arm-gnueabihf.node')
243
- )
244
- try {
245
- if (localFileExisted) {
246
- nativeBinding = require('./yara-x.linux-arm-gnueabihf.node')
247
- } else {
248
- nativeBinding = require('@litko/yara-x-linux-arm-gnueabihf')
249
- }
250
- } catch (e) {
251
- loadError = e
252
- }
270
+ }
271
+ } else if (process.arch === 'riscv64') {
272
+ if (isMusl()) {
273
+ try {
274
+ return require('./yara-x.linux-riscv64-musl.node')
275
+ } catch (e) {
276
+ loadErrors.push(e)
253
277
  }
254
- break
255
- case 'riscv64':
256
- if (isMusl()) {
257
- localFileExisted = existsSync(
258
- join(__dirname, 'yara-x.linux-riscv64-musl.node')
259
- )
260
- try {
261
- if (localFileExisted) {
262
- nativeBinding = require('./yara-x.linux-riscv64-musl.node')
263
- } else {
264
- nativeBinding = require('@litko/yara-x-linux-riscv64-musl')
265
- }
266
- } catch (e) {
267
- loadError = e
268
- }
269
- } else {
270
- localFileExisted = existsSync(
271
- join(__dirname, 'yara-x.linux-riscv64-gnu.node')
272
- )
273
- try {
274
- if (localFileExisted) {
275
- nativeBinding = require('./yara-x.linux-riscv64-gnu.node')
276
- } else {
277
- nativeBinding = require('@litko/yara-x-linux-riscv64-gnu')
278
- }
279
- } catch (e) {
280
- loadError = e
281
- }
278
+ try {
279
+ return require('@litko/yara-x-linux-riscv64-musl')
280
+ } catch (e) {
281
+ loadErrors.push(e)
282
282
  }
283
- break
284
- case 's390x':
285
- localFileExisted = existsSync(
286
- join(__dirname, 'yara-x.linux-s390x-gnu.node')
287
- )
283
+ } else {
288
284
  try {
289
- if (localFileExisted) {
290
- nativeBinding = require('./yara-x.linux-s390x-gnu.node')
291
- } else {
292
- nativeBinding = require('@litko/yara-x-linux-s390x-gnu')
293
- }
285
+ return require('./yara-x.linux-riscv64-gnu.node')
294
286
  } catch (e) {
295
- loadError = e
287
+ loadErrors.push(e)
296
288
  }
297
- break
298
- default:
299
- throw new Error(`Unsupported architecture on Linux: ${arch}`)
289
+ try {
290
+ return require('@litko/yara-x-linux-riscv64-gnu')
291
+ } catch (e) {
292
+ loadErrors.push(e)
293
+ }
294
+ }
295
+ } else if (process.arch === 'ppc64') {
296
+ try {
297
+ return require('./yara-x.linux-ppc64-gnu.node')
298
+ } catch (e) {
299
+ loadErrors.push(e)
300
+ }
301
+ try {
302
+ return require('@litko/yara-x-linux-ppc64-gnu')
303
+ } catch (e) {
304
+ loadErrors.push(e)
305
+ }
306
+ } else if (process.arch === 's390x') {
307
+ try {
308
+ return require('./yara-x.linux-s390x-gnu.node')
309
+ } catch (e) {
310
+ loadErrors.push(e)
311
+ }
312
+ try {
313
+ return require('@litko/yara-x-linux-s390x-gnu')
314
+ } catch (e) {
315
+ loadErrors.push(e)
316
+ }
317
+ } else {
318
+ loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
319
+ }
320
+ } else if (process.platform === 'openharmony') {
321
+ if (process.arch === 'arm64') {
322
+ try {
323
+ return require('./yara-x.linux-arm64-ohos.node')
324
+ } catch (e) {
325
+ loadErrors.push(e)
326
+ }
327
+ try {
328
+ return require('@litko/yara-x-linux-arm64-ohos')
329
+ } catch (e) {
330
+ loadErrors.push(e)
331
+ }
332
+ } else if (process.arch === 'x64') {
333
+ try {
334
+ return require('./yara-x.linux-x64-ohos.node')
335
+ } catch (e) {
336
+ loadErrors.push(e)
337
+ }
338
+ try {
339
+ return require('@litko/yara-x-linux-x64-ohos')
340
+ } catch (e) {
341
+ loadErrors.push(e)
342
+ }
343
+ } else if (process.arch === 'arm') {
344
+ try {
345
+ return require('./yara-x.linux-arm-ohos.node')
346
+ } catch (e) {
347
+ loadErrors.push(e)
348
+ }
349
+ try {
350
+ return require('@litko/yara-x-linux-arm-ohos')
351
+ } catch (e) {
352
+ loadErrors.push(e)
353
+ }
354
+ } else {
355
+ loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
300
356
  }
301
- break
302
- default:
303
- throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
357
+ } else {
358
+ loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
359
+ }
360
+ }
361
+
362
+ nativeBinding = requireNative()
363
+
364
+ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
365
+ try {
366
+ nativeBinding = require('./yara-x.wasi.cjs')
367
+ } catch (err) {
368
+ if (process.env.NAPI_RS_FORCE_WASI) {
369
+ loadErrors.push(err)
370
+ }
371
+ }
372
+ if (!nativeBinding) {
373
+ try {
374
+ nativeBinding = require('@litko/yara-x-wasm32-wasi')
375
+ } catch (err) {
376
+ if (process.env.NAPI_RS_FORCE_WASI) {
377
+ loadErrors.push(err)
378
+ }
379
+ }
380
+ }
304
381
  }
305
382
 
306
383
  if (!nativeBinding) {
307
- if (loadError) {
308
- throw loadError
384
+ if (loadErrors.length > 0) {
385
+ throw new Error(
386
+ `Cannot find native binding. ` +
387
+ `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
388
+ 'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
389
+ { cause: loadErrors }
390
+ )
309
391
  }
310
392
  throw new Error(`Failed to load native binding`)
311
393
  }
312
394
 
313
- const { YaraX, validate, compile, create, fromFile, compileToWasm, compileFileToWasm } = nativeBinding
314
-
315
- module.exports.YaraX = YaraX
316
- module.exports.validate = validate
317
- module.exports.compile = compile
318
- module.exports.create = create
319
- module.exports.fromFile = fromFile
320
- module.exports.compileToWasm = compileToWasm
321
- module.exports.compileFileToWasm = compileFileToWasm
395
+ module.exports = nativeBinding
396
+ module.exports.YaraX = nativeBinding.YaraX
397
+ module.exports.compile = nativeBinding.compile
398
+ module.exports.compileFileToWasm = nativeBinding.compileFileToWasm
399
+ module.exports.compileToWasm = nativeBinding.compileToWasm
400
+ module.exports.create = nativeBinding.create
401
+ module.exports.fromFile = nativeBinding.fromFile
402
+ module.exports.validate = nativeBinding.validate
package/package.json CHANGED
@@ -1,40 +1,50 @@
1
1
  {
2
2
  "name": "@litko/yara-x",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
+ "packageManager": "pnpm@10.13.1",
6
7
  "napi": {
7
- "name": "yara-x",
8
- "triples": {
9
- "additional": [
10
- "x86_64-apple-darwin",
11
- "aarch64-apple-darwin",
12
- "x86_64-unknown-linux-gnu",
13
- "aarch64-unknown-linux-gnu"
14
- ]
15
- }
8
+ "binaryName": "yara-x",
9
+ "targets": [
10
+ "x86_64-apple-darwin",
11
+ "aarch64-apple-darwin",
12
+ "x86_64-unknown-linux-gnu"
13
+ ]
16
14
  },
17
15
  "license": "MIT",
18
16
  "devDependencies": {
19
- "@napi-rs/cli": "^2.18.4",
20
- "@napi-rs/package-template": "^1.0.0",
17
+ "@napi-rs/cli": "^3.0.0",
18
+ "@napi-rs/wasm-runtime": "^1.0.0",
21
19
  "@types/node": "^22.13.10"
22
20
  },
23
21
  "engines": {
24
22
  "node": ">= 20"
25
23
  },
24
+ "homepage": "https://github.com/cawalch/node-yara-x",
25
+ "bugs": {
26
+ "url": "https://github.com/cawalch/node-yara-x/issues"
27
+ },
26
28
  "publishConfig": {
27
29
  "registry": "https://registry.npmjs.org/",
28
30
  "access": "public"
29
31
  },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/cawalch/node-yara-x.git"
35
+ },
30
36
  "scripts": {
31
37
  "artifacts": "napi artifacts",
32
38
  "build": "napi build --platform --release",
33
39
  "build:debug": "napi build --platform",
40
+ "prepublishOnly": "napi prepublish -t npm",
34
41
  "universal": "napi universal",
35
42
  "version": "napi version",
36
43
  "test": "node --test __test__/index.spec.mjs",
37
- "benchmark": "node __test__/benchmark.mjs"
44
+ "benchmark": "node __test__/benchmark.mjs",
45
+ "profile": "node --expose-gc __test__/run-profiling.mjs",
46
+ "profile:baseline": "node --expose-gc __test__/run-profiling.mjs --baseline",
47
+ "profile:compare": "node --expose-gc __test__/run-profiling.mjs --compare __test__/baseline-performance.json"
38
48
  },
39
49
  "files": [
40
50
  "index.js",
@@ -49,10 +59,8 @@
49
59
  "rust"
50
60
  ],
51
61
  "optionalDependencies": {
52
- "@litko/yara-x-win32-x64-msvc": "0.1.2",
53
- "@litko/yara-x-darwin-x64": "0.1.2",
54
- "@litko/yara-x-linux-x64-gnu": "0.1.2",
55
- "@litko/yara-x-darwin-arm64": "0.1.2",
56
- "@litko/yara-x-linux-arm64-gnu": "0.1.2"
62
+ "@litko/yara-x-darwin-x64": "0.2.1",
63
+ "@litko/yara-x-darwin-arm64": "0.2.1",
64
+ "@litko/yara-x-linux-x64-gnu": "0.2.1"
57
65
  }
58
- }
66
+ }