Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Deploy EVTX Viewer to GitHub Pages

on:
push:
branches: [ wasm-viewer ]
# Allow manual trigger
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

# ----------------------
# Install build tooling
# ----------------------
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Set up Rust toolchain (stable) with wasm target
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
override: true

- name: Install & cache wasm-pack
uses: jetli/wasm-pack-action@v0.4.0
with:
version: v0.13.1

# ----------------------
# Build the WASM crate
# ----------------------
- name: Build evtx-wasm crate (release)
run: |
wasm-pack build evtx-wasm \
--target web \
--out-dir evtx-viewer/src/wasm \
--out-name evtx_wasm \
--release

# ----------------------
# Build the React viewer
# ----------------------
- name: Install JS/TS dependencies
working-directory: evtx-wasm/evtx-viewer
run: bun install --frozen-lockfile

# Copy built-in sample to public folder before build
- name: Add sample EVTX to viewer public folder
run: |
mkdir -p evtx-wasm/evtx-viewer/public/samples
cp samples/security.evtx evtx-wasm/evtx-viewer/public/samples/

- name: Build viewer
working-directory: evtx-wasm/evtx-viewer
run: bun run build

# ----------------------
# Upload artifact for Pages
# ----------------------
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: evtx-wasm/evtx-viewer/dist

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ bazel-*
result*

repomix-output.txt

# WASM artefacts generated at runtime – do not commit
evtx-wasm/evtx-viewer/public/pkg
# Samples are being copied by build scripts before deploying
**/public/samples/
2 changes: 2 additions & 0 deletions evtx-wasm/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
46 changes: 46 additions & 0 deletions evtx-wasm/.cursor/rules/evtx-state-architecture.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
description:
globs:
alwaysApply: true
---
# EVTX Viewer – State Architecture Guide

This project uses a **single global React reducer store** defined in
[`state/rootReducer.ts`](mdc:evtx-wasm/evtx-viewer/src/state/rootReducer.ts) and
provided via [`state/store.tsx`](mdc:evtx-wasm/evtx-viewer/src/state/store.tsx).

## Global store (authoritative app-wide data)
* **Slices live under** `state/<slice>/` – e.g.
* `filters/filtersSlice.ts`
* `columns/columnsSlice.ts`
* `ingest/ingestSlice.ts`
* `rootReducer.ts` combines slices; `globalInitialState` holds defaults.
* `GlobalProvider` (exported from `store.tsx`) wraps the React tree and exposes:
* `useGlobalState(selector)` – read‐only selector
* `useGlobalDispatch()` – dispatch actions
* Convenience hooks `useFiltersState`, `useColumnsState`, `useIngestState`.
* **Put data here if** it is shared by multiple top-level features or must be
persisted / serialised (e.g. filters, columns, ingest progress).

## Feature-local state & selectors
* Hooks that only concern a *single* UI feature stay next to that component,
not in `state/`.
* Examples in *FilterSidebar*:
* [`useFacetCounts.ts`](mdc:evtx-wasm/evtx-viewer/src/components/FilterSidebar/useFacetCounts.ts)
* [`useActiveFilterChips.ts`](mdc:evtx-wasm/evtx-viewer/src/components/FilterSidebar/useActiveFilterChips.ts)
* These hooks pull global slices via the selector helpers, do derivations, and
return UI-ready data. No other feature should import them.
* **Put logic here if** it is purely presentational or scoped to one feature
(collapse toggles, local search terms, derived chip arrays, etc.).

## Legacy wrapper – `AppStateProvider`
`AppStateProvider` now simply:
1. Wraps the tree in `GlobalProvider`.
2. Seeds initial filters / columns once on mount.
3. Exposes compatibility hooks (`useAppState`, `useEvtxState`) until all code
migrates.

When adding new state:
1. Ask “is this referenced by more than one feature?” If *yes* → new slice.
2. Otherwise co-locate the hook with the feature directory.

This rule helps future threads recognise where to place or fetch stateful logic.
94 changes: 94 additions & 0 deletions evtx-wasm/.cursor/rules/wasm-bindings.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
description: when working on wasm bindings, or importing wasm bindings in TS.
alwaysApply: false
---
# WASM Bindings & Regeneration Guide

This project uses `wasm-pack` to compile the Rust crate under
[`evtx-wasm/`](mdc:evtx-wasm/src/lib.rs) into the viewer. The output lives in
[`src/wasm/`](mdc:evtx-wasm/evtx-viewer/src/wasm/) and contains:

* `evtx_wasm.js` – JS glue
* `evtx_wasm_bg.wasm` – compiled WebAssembly
* `evtx_wasm.d.ts` – **typings used by TypeScript**

Whenever you add or rename a function in the Rust `#[wasm_bindgen]` interface
(e.g. `parse_chunk_records`) you **MUST** regenerate the pkg so the `.d.ts`
reflects the change; otherwise TS won’t see the method.

## Regeneration steps
```bash
# from repo root
cd evtx-wasm # workspace with Cargo.toml
wasm-pack build --target web --release \
--out-dir ../evtx-wasm/evtx-viewer/src/wasm
```
The helper script [`evtx-wasm/evtx-viewer/setup-dev.sh`](mdc:evtx-wasm/evtx-viewer/setup-dev.sh)
performs a similar copy using symlinks during dev.

## Importing in TS
Use a **type-only** import for static typing and a dynamic import for runtime:
```ts
// type side
type WasmBindings = typeof import('../wasm/evtx_wasm');

// runtime side (inside an async function)
const wasm: WasmBindings = await import('../wasm/evtx_wasm.js') as WasmBindings;
const parser = new wasm.EvtxWasmParser(bytes);
```
Do **NOT** cast to `any`; rely on the generated typings. If TypeScript complains
that a new method doesn’t exist, first verify you rebuilt the pkg and that
`evtx_wasm.d.ts` lists the method.

## Linting / ESLint
The generated `*_bg.wasm.d.ts` contains disable directives ESLint trips over.
We ignore those globally via `globalIgnores` in `eslint.config.js`:
```js
globalIgnores(['**/*_bg.wasm.d.ts']);
```
Never edit generated files; update the Rust code and rebuild instead.
# WASM Bindings & Regeneration Guide

This project uses `wasm-pack` to compile the Rust crate under
[`evtx-wasm/`](mdc:evtx-wasm/src/lib.rs) into the viewer. The output lives in
[`src/wasm/`](mdc:evtx-wasm/evtx-viewer/src/wasm/) and contains:

* `evtx_wasm.js` – JS glue
* `evtx_wasm_bg.wasm` – compiled WebAssembly
* `evtx_wasm.d.ts` – **typings used by TypeScript**

Whenever you add or rename a function in the Rust `#[wasm_bindgen]` interface
(e.g. `parse_chunk_records`) you **MUST** regenerate the pkg so the `.d.ts`
reflects the change; otherwise TS won’t see the method.

## Regeneration steps
```bash
# from repo root
cd evtx-wasm # workspace with Cargo.toml
wasm-pack build --target web --release \
--out-dir ../evtx-wasm/evtx-viewer/src/wasm
```
The helper script [`evtx-wasm/evtx-viewer/setup-dev.sh`](mdc:evtx-wasm/evtx-viewer/setup-dev.sh)
performs a similar copy using symlinks during dev.

## Importing in TS
Use a **type-only** import for static typing and a dynamic import for runtime:
```ts
// type side
type WasmBindings = typeof import('../wasm/evtx_wasm');

// runtime side (inside an async function)
const wasm: WasmBindings = await import('../wasm/evtx_wasm.js') as WasmBindings;
const parser = new wasm.EvtxWasmParser(bytes);
```
Do **NOT** cast to `any`; rely on the generated typings. If TypeScript complains
that a new method doesn’t exist, first verify you rebuilt the pkg and that
`evtx_wasm.d.ts` lists the method.

## Linting / ESLint
The generated `*_bg.wasm.d.ts` contains disable directives ESLint trips over.
We ignore those globally via `globalIgnores` in `eslint.config.js`:
```js
globalIgnores(['**/*_bg.wasm.d.ts']);
```
Never edit generated files; update the Rust code and rebuild instead.
27 changes: 27 additions & 0 deletions evtx-wasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dependencies
node_modules/

# Build outputs
dist/
public/pkg/
public/assets/*.js
public/assets/*.js.map

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

# Logs
*.log

# Temp files
*.tmp
*.temp

# WASM build artifacts
target/
Cargo.lock
23 changes: 23 additions & 0 deletions evtx-wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "evtx-wasm"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
evtx = { path = "..", default-features = false, features = [] }
wasm-bindgen = "0.2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde-wasm-bindgen = "0.6"
web-sys = { version = "0.3", features = ["console"] }
js-sys = "0.3"
console_error_panic_hook = "0.1"
arrow2 = { version = "0.18", features = ["io_ipc"] }
getrandom = { version = "0.3", features = ["wasm_js"] }

[profile.release]
opt-level = "z"
lto = true
Loading
Loading