Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
8dd5c9f
feat(parser): add the EvtxECmd map engine
adamgell Aug 9, 2026
23dca7b
fix(parser): join repeated elements, matching verified EvtxECmd behav…
adamgell Aug 9, 2026
4fe3ab7
feat(event-log): load EvtxECmd .map files from disk
adamgell Aug 9, 2026
6f819fd
fix(parser): render map templates in a single pass
adamgell Aug 9, 2026
9ed4e3e
feat(parser): build XPath queries so the service does the filtering
adamgell Aug 9, 2026
2976833
perf(event-log): filter in the service and query channels concurrently
adamgell Aug 9, 2026
c91d4fc
feat(event-log): give the live view a server-side time window
adamgell Aug 9, 2026
f0f2e94
feat(event-log): convert rendered event XML into the map engine's nod…
adamgell Aug 9, 2026
5867298
fix(event-log): take a slice in the XML close helper
adamgell Aug 9, 2026
80a57c2
feat(event-log): surface the System block fields every event carries
adamgell Aug 9, 2026
462c976
feat(event-log): export the events on screen to CSV, TSV, JSON, or ev…
adamgell Aug 9, 2026
4ec7066
feat(event-log): apply loaded maps to events and surface the columns
adamgell Aug 9, 2026
99e810b
fix(parser): emit queries the Event Log service actually accepts
adamgell Aug 9, 2026
9e448d0
fix(event-log): gate the new commands behind the event-log feature
adamgell Aug 9, 2026
50fb050
fix(parser): quote by delimiter choice and budget by expressions, not…
adamgell Aug 9, 2026
ff7020c
test(event-log): exercise the live query path against a real service
adamgell Aug 9, 2026
cc36318
feat(parser): render event descriptions from captured provider metadata
adamgell Aug 9, 2026
43b9bab
feat(event-log): read captured provider metadata from a provider data…
adamgell Aug 9, 2026
a233344
test(event-log): read a real provider database, not just synthetic fi…
adamgell Aug 9, 2026
11f34b1
fix(parser): accept the signed integers the provider metadata actuall…
adamgell Aug 9, 2026
bd031f8
feat(event-log): render provider descriptions for events read from files
adamgell Aug 9, 2026
41f6407
test(event-log): prove the description chain against a real database
adamgell Aug 9, 2026
30f30e0
feat(event-log): group the event list by level, provider, channel, id…
adamgell Aug 9, 2026
16d9ea3
feat(event-log): wire grouping into the list and add a group-by control
adamgell Aug 9, 2026
f1e82bd
feat(event-log): add a saved filter library
adamgell Aug 9, 2026
212463e
feat(event-log): persist the filter library and apply saved filters f…
adamgell Aug 9, 2026
5830712
feat(event-log): add a column model with visibility, order, and widths
adamgell Aug 9, 2026
84a78bd
feat(event-log): render the list from the column config and add a cho…
adamgell Aug 9, 2026
d3dec44
feat(parser): merge Windows events and text logs into one timeline
adamgell Aug 9, 2026
c4805ea
feat(event-log): place events on the unified timeline and expose it o…
adamgell Aug 9, 2026
3309070
feat(event-log): frontend model for the unified timeline
adamgell Aug 9, 2026
8de9b26
feat(event-log): render the unified timeline
adamgell Aug 9, 2026
adfeb11
feat(event-log): decode .etl payloads and fix the dead file-open path
adamgell Aug 9, 2026
70db7bd
test(event-log): cover the .evtx file path against a real capture
adamgell Aug 9, 2026
4784b3f
fix(parser): give every split query node an Id and pin the structured…
adamgell Aug 9, 2026
f655a9e
fix(event-log): stop the map loader hiding gaps and depending on read…
adamgell Aug 9, 2026
7ebe1f3
docs(eventmap): a format conversion cannot be byte-faithful
adamgell Aug 9, 2026
1338145
fix(event-log): import the shared timestamp parser in the live path
adamgell Aug 9, 2026
174767e
fix(event-log): explain what is missing from a parse instead of count…
adamgell Aug 9, 2026
f7bb7b0
feat(event-log): show what is missing from the loaded events
adamgell Aug 9, 2026
ee3ff9c
fix(event-log): show event times in one stated zone instead of three …
adamgell Aug 9, 2026
a35403b
feat(event-log): let map-derived values be columns, not just detail-p…
adamgell Aug 9, 2026
eac274e
fix(event-log): stop dropping fields from events that carry no Data e…
adamgell Aug 10, 2026
c9961b1
fix(parser): cost the time window correctly and record the real expre…
adamgell Aug 10, 2026
9ce3e90
fix(event-log): stop misattributing values in rendered descriptions
adamgell Aug 10, 2026
55b0294
refactor(event-log): own the map and provider registries in AppState
adamgell Aug 10, 2026
033e8f5
fix(event-log): pass the registry in the live service tests
adamgell Aug 10, 2026
d6ffbbd
fix(event-log): remove a duplicated argument in two service tests
adamgell Aug 10, 2026
8b3d950
fix(event-log): stop an oversized exclusion filter silently returning…
adamgell Aug 10, 2026
722e79c
fix(parser): bound the terms that repeat in every node, not just the …
adamgell Aug 10, 2026
d44afcb
test(event-log): drive coverage gaps through the store, not just the …
adamgell Aug 10, 2026
21a0c05
fix(event-log): label positional fields by slot, and use the one filt…
adamgell Aug 10, 2026
9dae0fc
fix(event-log): send event origins over the wire with the keys the UI…
adamgell Aug 10, 2026
79de6e2
test(event-log): give the timeline fixture distinguishable ids, and d…
adamgell Aug 10, 2026
cb2b13c
fix(event-log): apply the time window on refresh, and stop emitting o…
adamgell Aug 10, 2026
48dce30
fix(event-log): close four saved-filter findings I had misread as alr…
adamgell Aug 10, 2026
a57ec6a
fix(event-log): stop a wide id range hanging the tab, and name classi…
adamgell Aug 10, 2026
9133042
fix(event-log): export the columns the grid showed, and stop reopenin…
adamgell Aug 10, 2026
6f988c6
fix(event-log): make saving and reordering work for macOS and keyboar…
adamgell Aug 10, 2026
3cbfe51
fix(event-log): gate the event-log-only dependencies, and tighten wea…
adamgell Aug 10, 2026
2965fbf
fix(parser): make provider metadata able to read its own output back
adamgell Aug 10, 2026
270b557
perf(event-log): stop shipping every record's XML for a delimited export
adamgell Aug 10, 2026
91273d8
fix(event-log): verify the reader's reply at the boundary, once
adamgell Aug 10, 2026
ba55802
fix(event-log): keep the empty live region in the accessibility tree
adamgell Aug 10, 2026
20c01be
fix(event-log): refuse malformed value paths, and declare the grouped…
adamgell Aug 10, 2026
6c546d5
fix(event-log): clear a selection collapse hides, and stop two tests …
adamgell Aug 10, 2026
d3d18dc
perf(eventmap): parse each map's paths once instead of once per record
adamgell Aug 11, 2026
306a251
Merge remote-tracking branch 'origin/main' into feat/eventmap-engine
adamgell Aug 11, 2026
01e1d3d
fix(clipboard): let the mirror tests wait for the write they assert
adamgell Aug 11, 2026
baaafc6
Merge remote-tracking branch 'origin/main' into feat/eventmap-engine
adamgell Aug 11, 2026
4ad735d
fix(eventmap): make the memoized cache impossible to outlive its source
adamgell Aug 12, 2026
d72096c
docs(eventmap): say what compiled() actually does
adamgell Aug 12, 2026
282b90c
fix(event-log): read the live path's fields from the parsed tree
adamgell Aug 12, 2026
05ae283
test(event-log): add a timing harness for the live scan
adamgell Aug 12, 2026
38f66be
fix(event-log): query channels one at a time, not all in one request
adamgell Aug 12, 2026
8964b2d
docs(event-log): stop claiming a default event cap that does not exist
adamgell Aug 12, 2026
5086b7e
test(event-log): account for where a scan's memory goes
adamgell Aug 12, 2026
fd6fdbf
fix(event-log): stop reporting a truncated channel as a complete one
adamgell Aug 12, 2026
3c9cca1
refactor(event-log): move the fetch-failure decision where CI can tes…
adamgell Aug 12, 2026
41b8ec6
feat(event-log): deliver a channel's records as they are read
adamgell Aug 12, 2026
f008ac9
fix(event-log): count render failures and slice previews by character
adamgell Aug 12, 2026
6a47c0f
fix(event-log): build the file path's summary the way the live path does
adamgell Aug 12, 2026
9b7c5a4
fix(event-log): guard channel processing and bound the sequence scan
adamgell Aug 12, 2026
2de996e
fix(event-log): reject a --days value that overflows the time window
adamgell Aug 12, 2026
ee5af28
fix(event-log): report a channel whose refresh failed as a gap
adamgell Aug 12, 2026
1a631b6
fix(event-log): count channels with gaps separately from gap entries
adamgell Aug 12, 2026
4b2bbdb
fix(event-log): clear loadError on refresh and pin the failure state
adamgell Aug 12, 2026
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
116 changes: 116 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/cmtraceopen-parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ base64 = "0.22"
sha2 = "0.11"

[dev-dependencies]
# Benchmarks only. A dev-dependency, so it never reaches the wasm32 build of the library.
criterion = "0.8"

[[bench]]
name = "eventmap_apply"
harness = false
87 changes: 87 additions & 0 deletions crates/cmtraceopen-parser/benches/eventmap_apply.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! How long applying one map to one event takes.
//!
//! The map engine runs once per record, so this is multiplied by the channel size: a million-record
//! scan pays it a million times. The benchmark exists because the path expressions and `%Name%`
//! placeholders in a map are constant for its whole life, and the applier used to rebuild both for
//! every record.
//!
//! Measured on an M-series mac against the Security 4624 fixture, comparing re-parsing per record
//! against the compiled cache:
//!
//! | | per record | per 10k records |
//! |---|---|---|
//! | re-parsing each time | 5.20 us | 49.0 ms |
//! | compiled once | 1.91 us | 18.9 ms |
//!
//! Run with `cargo bench --bench eventmap_apply` from `crates/cmtraceopen-parser`.

use criterion::{criterion_group, criterion_main, Criterion};
use std::hint::black_box;

use cmtraceopen_parser::eventmap::{apply_map, EventMap, EventNode};

/// A map shaped like the upstream Security 4624: several bindings, multi-placeholder templates.
const SECURITY_4624: &str = include_str!("../tests/fixtures/eventmap/security-4624.json");

fn event() -> EventNode {
EventNode::new("Event").with_child(
EventNode::new("EventData")
.with_child(
EventNode::new("Data")
.with_attribute("Name", "SubjectUserName")
.with_text("adam"),
)
.with_child(
EventNode::new("Data")
.with_attribute("Name", "SubjectDomainName")
.with_text("CONTOSO"),
)
.with_child(
EventNode::new("Data")
.with_attribute("Name", "TargetUserName")
.with_text("svc-backup"),
)
.with_child(
EventNode::new("Data")
.with_attribute("Name", "IpAddress")
.with_text("10.0.0.7"),
)
.with_child(
EventNode::new("Data")
.with_attribute("Name", "LogonType")
.with_text("10"),
),
)
}

fn apply_one_record(c: &mut Criterion) {
let map: EventMap = serde_json::from_str(SECURITY_4624).expect("fixture parses");
let event = event();

// Warm the compiled cache the way a real scan does: the first record pays for the parse, and
// every record after it reuses the result. Measuring from cold would report the one-off cost
// rather than the per-record cost that actually multiplies.
let _ = apply_map(&map, &event);

c.bench_function("apply_map/security-4624/one record", |b| {
b.iter(|| black_box(apply_map(black_box(&map), black_box(&event))));
});
}

fn apply_a_channel(c: &mut Criterion) {
let map: EventMap = serde_json::from_str(SECURITY_4624).expect("fixture parses");
let event = event();
let _ = apply_map(&map, &event);

// Ten thousand records is a small channel; the point is the shape of the curve, not the total.
c.bench_function("apply_map/security-4624/10k records", |b| {
b.iter(|| {
for _ in 0..10_000 {
black_box(apply_map(black_box(&map), black_box(&event)));
}
});
});
}

criterion_group!(benches, apply_one_record, apply_a_channel);
criterion_main!(benches);
Loading