diff --git a/js-wasm/README.md b/js-wasm/README.md index dbae45065..18ca097e6 100644 --- a/js-wasm/README.md +++ b/js-wasm/README.md @@ -1,130 +1,132 @@ -# yara-x-wasm +# @virustotal/yara-x -Browser-focused WebAssembly packaging for [`yara-x`](../lib) with an -object-oriented JavaScript API built around `Compiler`, `Rules`, and -`Scanner`. +JavaScript bindings for [YARA-X](https://github.com/VirusTotal/yara-x) using WebAssembly. -## Package surface - -- `Compiler`, `Rules`, and `Scanner` - -## Build - -From [`js-wasm`](.) run: - -```bash -cargo install --locked wasm-pack --version 0.14.0 -cargo install --locked wasm-bindgen-cli --version 0.2.113 -wasm-pack build --target web --release --mode no-install --no-pack -``` - -This produces the standard browser package in `pkg/`. - -For the full package layout, including the preserved no-modules bundles in -`dist/`: - -```bash -cargo run --release --features release-tools --bin build_web_release -- -``` - -or: +## Installation ```bash -cargo build-web-release +npm install @virustotal/yara-x ``` -This produces: +## Quick Start -- `pkg/yara-x-wasm.js` -- `pkg/yara-x-wasm_bg.wasm` -- `dist/yara-x-wasm-bundle.js` -- `dist/yara-x-wasm-bundle.min.js` +```js +import init, { Compiler } from "@virustotal/yara-x"; -The no-modules bundle reuses the shared `pkg/yara-x-wasm_bg.wasm` binary -instead of shipping a second copy under `dist/`. +// Initialize the WebAssembly module +await init(); -SIMD is enabled for `wasm32-unknown-unknown` via `js-wasm/.cargo/config.toml`. +// Compile a rule +const compiler = new Compiler(); +compiler.addSource('rule test { strings: $a = "abc" condition: $a }'); -## Tests +const rules = compiler.build(); -Rust/browser API coverage: +// Scan data +const payload = new Uint8Array([0x61, 0x62, 0x63, 0x64]); // "abcd" +const result = rules.scan(payload); -```bash -npm run test:wasm-node +if (result.valid && result.matches.length > 0) { + console.log("Rule matched!"); +} ``` -Headless browser coverage for browser-specific behavior such as console output: - -```bash -CHROME_BIN=/path/to/chrome \ -CHROMEDRIVER=/path/to/chromedriver \ -npm run test:wasm-browser +## API Reference + +### `Compiler` + +The `Compiler` translates YARA-X rules into a executable format. + +- `addSource(source: string)`: Compiles a rule string. Throws an error if compilation fails. +- `build(): Rules`: Finalizes compilation and returns a `Rules` object. +- `defineGlobal(identifier: string, value: any)`: Defines an external variable used in conditions (boolean, number, string). +- `newNamespace(namespace: string)`: Scopes subsequent rules to a specific namespace. +- `errors: string[]`: Array of compilation error messages. +- `warnings: string[]`: Array of compilation warning messages. + +### `Rules` + +The `Rules` object represents the compiled set of rules ready for scanning. + +- `scan(payload: Uint8Array): ScanResult`: Scans the payload using the default scanner profile. +- `scanner(): Scanner`: Spawns a dedicated `Scanner` for advanced configuration. +- `warnings: string[]`: Warnings inherited from the compiler. + +### `Scanner` + +The `Scanner` provides fine-grained control over scanning operations. + +- `scan(payload: Uint8Array): ScanResult`: Scans the payload. +- `setGlobal(identifier: string, value: any)`: Overrides values defined by `compiler.defineGlobal()`. +- `setMaxMatchesPerPattern(n: number)`: Limits reporting to first `n` matches per pattern. +- `setTimeoutMs(timeout_ms: number)`: Sets a hard timeout for scanning. + +--- + +## Scan Results Structure + +The `.scan(...)` methods return an object detailing matches and diagnostics: + +```json +{ + "valid": true, + "matches": [ + { + "identifier": "rule_name", + "namespace": "default", + "isPrivate": false, + "isGlobal": false, + "tags": ["tag_a"], + "metadata": [ + { "identifier": "key", "value": "value" } + ], + "patterns": [ + { + "identifier": "$a", + "kind": "text", + "isPrivate": false, + "matches": [ + { "offset": 2, "length": 3 } + ] + } + ] + } + ], + "warnings": [] +} ``` -JS end-to-end coverage against the generated `pkg/` + `dist/` outputs: +--- -```bash -npm run test:js -``` +## Resource Management -Package validation: +Objects generated in WebAssembly live in the Wasm heap, which is separate from the JavaScript garbage-collected +heap. If you do not manually free these objects, they will leak memory in the WebAssembly space. -```bash -npm run pack:dry-run -``` - -## Usage - -### ES module usage: +Ensure you call `.free()` on objects when no longer needed: ```js -import init, { Compiler } from "yara-x-wasm"; - -await init(); - const compiler = new Compiler(); -compiler.addSource('rule x { strings: $a = "abc" condition: $a }'); - -const rules = compiler.build(); -const result = rules.scan(new Uint8Array([0x61, 0x62, 0x63])); +try { + compiler.addSource('rule x { condition: true }'); + const rules = compiler.build(); + const payload = new Uint8Array([0x00]); // Self-contained payload + rules.scan(payload); + rules.free(); +} finally { + compiler.free(); +} ``` -### No-modules bundle usage: - -```html - - -``` - -Use `dist/yara-x-wasm-bundle.min.js` instead of `dist/yara-x-wasm-bundle.js` for -production deployments. By default the bundle loads its wasm from -`pkg/yara-x-wasm_bg.wasm`, so keep the standard package layout intact when -serving it. - -### Object-style API with scanner configuration: +If your environment supports the new JavaScript `using` keyword (Explicit Resource Management), you can let the +runtime handle it automatically because the types implement `[Symbol.dispose]`: ```js -import init, { Compiler, Scanner } from "yara-x-wasm"; - -await init(); - -const compiler = new Compiler(); -compiler.defineGlobal("threshold", 7); -compiler.addSource('rule x { condition: threshold == 7 }'); - -const rules = compiler.build(); -const scanner = new Scanner(rules); -scanner.setGlobal("threshold", 9); +{ + using compiler = new Compiler(); + compiler.addSource('rule x { condition: true }'); +} // compiler.free() is called automatically here! ``` +