Skip to content

Commit b7f806a

Browse files
committed
Bug fixes
1 parent ff1454b commit b7f806a

11 files changed

Lines changed: 91 additions & 66 deletions

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ resolver = "2"
44

55
[package]
66
name = "anya-security-core"
7-
version = "2.0.2"
7+
version = "2.0.3"
88
edition = "2024"
99
rust-version = "1.85"
1010
authors = ["Daniel Iwugo <daniel@themalwarefiles.com>"]

README.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Anya analyses files without executing them. Drop a PE, ELF, Mach-O, PDF, Office
3737

3838
| Platform | GUI | CLI |
3939
|---|---|---|
40-
| **Windows** | `.msi` installer | `.zip` |
40+
| **Windows** | `.exe` installer (NSIS) | `.zip` |
4141
| **macOS** | `.dmg` (Intel + Apple Silicon) | Universal binary (`.tar.gz`) |
4242
| **Linux** | `.AppImage` / `.deb` / `.rpm` | Static musl binary (`.tar.gz`) |
4343

@@ -75,25 +75,21 @@ Full flag reference: `anya --help`
7575

7676
## GUI
7777

78-
Launch Anya, drag a file or folder onto the drop zone — or use the **+** button for single file, batch analysis, or file comparison.
78+
Drag a file or folder onto the window, or use the **+** button.
7979

80-
| Tab | What it shows |
81-
|---|---|
82-
| Overview | Risk score ring, file metadata, hashes, KSD match, forensic fragment |
83-
| Entropy | Section entropy chart, byte histogram, distribution flatness analysis |
84-
| Imports | DLL tree with expandable function lists and inline explanations |
85-
| Sections | Section permissions, per-section entropy, characteristics |
86-
| Strings | Extracted strings with IOC extraction, classification, and filtering |
87-
| Security | ASLR, DEP, Authenticode, overlay, debug artifacts, toolchain detection, certificate reputation |
88-
| Format | Format-specific analysis for JS, PowerShell, VBS, OLE, ZIP, HTML, XML, LNK, ISO, and more |
89-
| MITRE | Mapped ATT&CK techniques with tactic tagging and real-world attack examples |
90-
| Graph | IOC/Import evidence web (single file) or TLSH relationship graph (batch) with Obsidian-style interactions |
80+
- **Overview** — risk score, hashes, verdict, notes
81+
- **Entropy** — section chart, byte histogram, flatness
82+
- **Imports** — DLL tree with inline explanations
83+
- **Sections** — permissions, entropy, characteristics
84+
- **Strings** — extracted strings with IOC classification
85+
- **Security** — ASLR, DEP, Authenticode, toolchain, certificates
86+
- **Format** — deep analysis for 20+ file types
87+
- **MITRE** — mapped techniques with tactic grouping
88+
- **Graph** — evidence web (single file) or relationship graph (batch)
9189

92-
**Batch Analysis** drop a folder to scan all executables. Files appear in a searchable sidebar with colour-coded verdicts. The Graph tab shows an interactive relationship graph with TLSH similarity edges, cluster halos, and hover spotlight.
90+
**Batch mode:** drop a folder to scan everything. Searchable sidebar, interactive relationship graph.
9391

94-
**Single-File Graph** — the Graph tab visualises DLLs, suspicious APIs, IOCs, and behavioural categories as an interconnected evidence web. Hover any node to spotlight its connections.
95-
96-
**Teacher Mode** (toggle in Settings) surfaces contextual lessons as you navigate. Click any DLL, security card, IOC, or MITRE technique for beginner-friendly explanations with real-world examples.
92+
**Teacher Mode:** toggle in Settings for contextual explanations on every finding.
9793

9894
---
9995

@@ -122,17 +118,17 @@ Anya's scoring engine is calibrated against real malware and benign samples. Eve
122118
```mermaid
123119
xychart-beta
124120
title "Detection & False Positive Rate"
125-
x-axis ["v1.0", "v1.1", "v1.2", "v2.0"]
121+
x-axis ["v1.0", "v1.1", "v1.2", "v2.0", "v2.0.3"]
126122
y-axis "%" 0 --> 100
127-
line "Detection" [73.0, 82.0, 87.5, 99.9]
128-
line "FP rate (x10)" [27.0, 15.0, 3.0, 1.0]
123+
line "Detection" [73.0, 82.0, 87.5, 99.9, 99.9]
124+
line "FP rate (x10)" [27.0, 15.0, 3.0, 1.0, 0.0]
129125
```
130126

131127
*FP rate scaled 10x for visibility on the same axis.*
132128

133129
| Version | Malware | Benign | Total | Detection | FP Rate |
134130
|---|---|---|---|---|---|
135-
| **v2.0** | **~5,100** | **~5,300** | **~10,300** | **99.9%** | **0.1%** |
131+
| **v2.0.3** | **~9,100** | **~11,300** | **~21,700** | **99.9%** | **0.0%** |
136132

137133
> **Verify independently:** `anya benchmark ./your-samples/ --ground-truth malware --json`
138134

docs/ARCHITECTURE.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,9 @@ anya/
8989
│ └── lib/
9090
│ ├── db.ts # SQLite with versioned migration system
9191
│ └── tauri-bridge.ts # Typed IPC wrappers (api_version aware)
92-
├── anya-stubs/ # Stub crates (public repo)
93-
│ ├── scoring/ # Scoring interface stubs
94-
│ └── data/ # Data interface stubs
95-
└── anya-proprietary/ # Git submodule
96-
├── scoring/ # Scoring crate
97-
└── data/ # Data crate
92+
└── anya-stubs/ # Stub crates (public repo)
93+
├── scoring/ # Scoring interface stubs
94+
└── data/ # Data interface stubs
9895
```
9996

10097
---
@@ -207,6 +204,11 @@ invoke("get_ksd_neighborhood", { … }) → { neighbors: KsdNeighbor[] }
207204
invoke("install_bundled_yara_rules") → copies rules from app resources
208205
invoke("is_first_run") → "first_run" | "upgrade" | "current"
209206
invoke("complete_setup", { … }) → writes version marker + installs YARA rules
207+
invoke("get_dll_explanations") → DLL descriptions JSON
208+
invoke("get_function_explanations") → API function descriptions JSON
209+
invoke("get_technique_explanations") → MITRE technique explanations JSON
210+
invoke("get_mitre_attack_data") → MITRE ATT&CK catalogue JSON
211+
invoke("get_category_explanations") → API category descriptions JSON
210212
```
211213

212214
---
@@ -242,8 +244,10 @@ FileAnalysisResult
242244
├── confidence::extract_signals() → SignalSet (80+ signals)
243245
│ │
244246
│ ▼
245-
anya_scoring::score_signals() → weighted verdict + findings
247+
│ score_signals() → weighted verdict + findings
246248
249+
├── Known sample lookup (tools, test files, known clean binaries)
250+
├── Family annotation lookup (malware family context)
247251
├── KSD lookup (TLSH similarity to known malware)
248252
├── Forensic fragment annotation (sub-100B files)
249253
@@ -306,7 +310,7 @@ AnalysisResult (schema_version: "2.0.0")
306310
| `react`, `react-dom` | UI framework |
307311
| `@tauri-apps/api`, `plugin-sql`, `plugin-dialog`, `plugin-fs` | Tauri IPC |
308312
| `force-graph` | 2D force-directed graph (canvas, d3-force) |
309-
| `recharts` | Charts (entropy, donut) |
313+
| `recharts` | Charts (entropy) |
310314
| `radix-ui` | UI primitives |
311315
| `tailwindcss` | Utility CSS |
312316
| `lucide-react` | Icons |
@@ -317,14 +321,14 @@ AnalysisResult (schema_version: "2.0.0")
317321

318322
| Suite | Count | Runner |
319323
|---|---|---|
320-
| Rust unit tests | 263 | `cargo test -p anya-security-core` |
324+
| Rust unit tests | 23 | `cargo test -p anya-security-core` |
321325
| Frontend tests | 46 | `npx vitest run` |
322-
| Integration tests | 298 | `private/scripts/integration_test.sh` |
323-
| **Total** | **607** | |
326+
| Integration tests | 304 | `scripts/integration_test.sh` |
327+
| **Total** | **373** | |
324328

325329
Integration tests cover: CLI flags, edge cases (unicode paths, malformed input, 1-byte files, all-zeros, random data), format parsers, report generation (HTML/PDF/Markdown), batch analysis, case management, YARA engine, schema versioning, and output invariants.
326330

327331
---
328332

329-
**Last updated:** 2026-03-30 (v2.0.0-beta)
333+
**Last updated:** 2026-04-03 (v2.0.3)
330334
**Maintainer:** Daniel Iwugo — daniel@themalwarefiles.com

docs/CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## [2.0.3] - 2026-04-03
4+
5+
### Detection
6+
- Calibrated against 21,700 samples: 99.9% detection, 0.0% false positives
7+
- Known sample verdicts for security tools, development utilities, and test files
8+
- Malware family context with descriptions and aliases for 50+ families
9+
10+
### GUI
11+
- Notes section on Overview tab for known sample and fragment annotations
12+
- Teacher Mode guidance for Graph tab with interactive node explanations
13+
- Threat path highlighting in batch graph (click to trace connected clusters)
14+
- Expanded DLL and API category explanations in Teacher Mode
15+
16+
### CLI
17+
- Coloured verdict output for TOOL, PUP, and TEST classifications
18+
- Known sample and family context annotations in analysis output
19+
20+
### Other
21+
- Bug fixes and improvements
22+
323
## [2.0.2] - 2026-04-01
424

525
### Detection

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "anya",
33
"private": true,
4-
"version": "2.0.2",
4+
"version": "2.0.3",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "anya-gui"
3-
version = "2.0.2"
3+
version = "2.0.3"
44
edition = "2021"
55
description = "Anya — PE malware analysis desktop app (Tauri GUI)"
66

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"productName": "Anya",
3-
"version": "2.0.2",
3+
"version": "2.0.3",
44
"identifier": "com.anya.app",
55
"build": {
66
"frontendDist": "../dist",

src/__tests__/mitre-tab.test.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
import { describe, it, expect, vi } from "vitest";
1+
import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";
22
import { render, screen, fireEvent } from "@testing-library/react";
33
import { TeacherModeContext, type TeacherModeContextValue } from "../../ui/hooks/useTeacherMode";
44
import MitreTab from "../../ui/components/tabs/MitreTab";
5+
6+
// Suppress expected console.warn from MitreTab when MITRE data isn't loaded via IPC in test env
7+
const originalWarn = console.warn;
8+
beforeAll(() => { console.warn = (...args: unknown[]) => { if (typeof args[0] === "string" && args[0].includes("[MitreTab]")) return; originalWarn(...args); }; });
9+
afterAll(() => { console.warn = originalWarn; });
510
import type { AnalysisResult } from "../../ui/types/analysis";
611

712
const teacherCtx: TeacherModeContextValue = {

src/__tests__/settings-toggles.test.tsx

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, vi, beforeEach } from "vitest";
2-
import { render, screen, fireEvent } from "@testing-library/react";
2+
import { render, screen, fireEvent, act } from "@testing-library/react";
33
import { TeacherModeContext, type TeacherModeContextValue } from "../../ui/hooks/useTeacherMode";
44
import SettingsModal from "../../ui/components/SettingsModal";
55

@@ -28,7 +28,7 @@ const defaultProps = {
2828
onClose: vi.fn(),
2929
};
3030

31-
function renderSettingsModal(
31+
async function renderSettingsModal(
3232
contextOverrides: Partial<TeacherModeContextValue> = {},
3333
propOverrides: Partial<typeof defaultProps> = {}
3434
) {
@@ -41,51 +41,50 @@ function renderSettingsModal(
4141
...contextOverrides,
4242
};
4343
const props = { ...defaultProps, ...propOverrides };
44-
return render(
45-
<TeacherModeContext.Provider value={contextValue}>
46-
<SettingsModal {...props} />
47-
</TeacherModeContext.Provider>
48-
);
44+
let result: ReturnType<typeof render>;
45+
await act(async () => {
46+
result = render(
47+
<TeacherModeContext.Provider value={contextValue}>
48+
<SettingsModal {...props} />
49+
</TeacherModeContext.Provider>
50+
);
51+
});
52+
return result!;
4953
}
5054

5155
describe("SettingsModal toggles", () => {
5256
beforeEach(() => {
5357
vi.clearAllMocks();
5458
});
5559

56-
it("renders the Bible Verses label", () => {
57-
renderSettingsModal();
60+
it("renders the Bible Verses label", async () => {
61+
await renderSettingsModal();
5862
expect(screen.getByText("Bible Verses")).toBeInTheDocument();
5963
});
6064

61-
it("renders the Teacher Mode label", () => {
62-
renderSettingsModal();
65+
it("renders the Teacher Mode label", async () => {
66+
await renderSettingsModal();
6367
expect(screen.getByText("Teacher Mode")).toBeInTheDocument();
6468
});
6569

66-
it("Teacher Mode toggle reflects context enabled state via aria-checked", () => {
67-
renderSettingsModal({ enabled: true });
70+
it("Teacher Mode toggle reflects context enabled state via aria-checked", async () => {
71+
await renderSettingsModal({ enabled: true });
6872
const toggles = screen.getAllByRole("switch");
69-
const teacherToggle = toggles.find(
70-
(btn) => btn.getAttribute("aria-checked") !== null
71-
);
72-
// The Teacher Mode toggle is the first switch; aria-checked should match context
7373
expect(toggles[0].getAttribute("aria-checked")).toBe("true");
7474
});
7575

76-
it("Bible Verses toggle reflects bibleVersesEnabled prop via aria-checked", () => {
77-
renderSettingsModal({}, { bibleVersesEnabled: false });
76+
it("Bible Verses toggle reflects bibleVersesEnabled prop via aria-checked", async () => {
77+
await renderSettingsModal({}, { bibleVersesEnabled: false });
7878
const toggles = screen.getAllByRole("switch");
79-
// Bible Verses is the second switch in the Learning section
8079
const bibleToggle = toggles[1];
8180
expect(bibleToggle.getAttribute("aria-checked")).toBe("false");
8281
});
8382

84-
it("clicking the Bible Verses toggle calls onSetBibleVerses", () => {
83+
it("clicking the Bible Verses toggle calls onSetBibleVerses", async () => {
8584
const onSetBibleVerses = vi.fn();
86-
renderSettingsModal({}, { bibleVersesEnabled: true, onSetBibleVerses });
85+
await renderSettingsModal({}, { bibleVersesEnabled: true, onSetBibleVerses });
8786
const toggles = screen.getAllByRole("switch");
88-
fireEvent.click(toggles[1]);
87+
await act(async () => { fireEvent.click(toggles[1]); });
8988
expect(onSetBibleVerses).toHaveBeenCalledWith(false);
9089
});
9190
});

0 commit comments

Comments
 (0)