v3 fixes and styling
This commit is contained in:
@@ -200,6 +200,24 @@ func TestWriteLeviathanDocumentFileWritesPatchedBytes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLeviathanDocumentRangeFileWritesSelectedPatchedBytes(t *testing.T) {
|
||||
doc := document.New("patched.bin", []byte("abcdef"))
|
||||
if _, err := doc.Set(3, 'Z'); err != nil {
|
||||
t.Fatalf("Set() failed: %v", err)
|
||||
}
|
||||
path := filepath.Join(t.TempDir(), "range.bin")
|
||||
if err := writeLeviathanDocumentRangeFile(path, doc, 2, 3); err != nil {
|
||||
t.Fatalf("writeLeviathanDocumentRangeFile() failed: %v", err)
|
||||
}
|
||||
got, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile() failed: %v", err)
|
||||
}
|
||||
if string(got) != "cZe" {
|
||||
t.Fatalf("written bytes = %q, want %q", string(got), "cZe")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadLeviathanSmallFileRejectsOversizedFile(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "big.img")
|
||||
if err := os.WriteFile(path, []byte("abcd"), 0600); err != nil {
|
||||
|
||||
+118
-7
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -236,6 +237,44 @@ func (s *Service) LeviathanExportJSON(name string, value map[string]any) (string
|
||||
return path, os.WriteFile(path, b, 0600)
|
||||
}
|
||||
|
||||
// LeviathanExportRange prompts for a destination and streams a document range to disk.
|
||||
func (s *Service) LeviathanExportRange(name string, start, length int64) (string, error) {
|
||||
engine := s.leviathanEngine()
|
||||
engine.mu.Lock()
|
||||
if engine.doc == nil {
|
||||
engine.mu.Unlock()
|
||||
return "", errors.New("open a file first")
|
||||
}
|
||||
doc := engine.doc
|
||||
engine.mu.Unlock()
|
||||
|
||||
if s.ctx == nil {
|
||||
return "", fmt.Errorf("application context not set")
|
||||
}
|
||||
if start < 0 {
|
||||
return "", errors.New("export start must be non-negative")
|
||||
}
|
||||
if length <= 0 {
|
||||
return "", errors.New("this finding does not have a known exportable length")
|
||||
}
|
||||
size := doc.Size()
|
||||
if start >= size {
|
||||
return "", errors.New("export start is outside the document")
|
||||
}
|
||||
if length > size-start {
|
||||
length = size - start
|
||||
}
|
||||
defaultName := fmt.Sprintf("leviathan-%08x.bin", uint64(start))
|
||||
path, err := wailsRuntime.SaveFileDialog(s.ctx, wailsRuntime.SaveDialogOptions{
|
||||
Title: "Export Found File",
|
||||
DefaultFilename: safeExportName(name, defaultName),
|
||||
})
|
||||
if err != nil || path == "" {
|
||||
return "", err
|
||||
}
|
||||
return path, writeLeviathanDocumentRangeFile(path, doc, start, length)
|
||||
}
|
||||
|
||||
func (l *leviathanState) stateLocked() leviathanDocState {
|
||||
state := leviathanDocState{}
|
||||
if l.doc != nil {
|
||||
@@ -323,12 +362,8 @@ func (l *leviathanState) runToolLocked(tool string, params map[string]any) (map[
|
||||
entries, err := fs.Walk()
|
||||
return map[string]any{"superblock": fs.Superblock(), "compression": fs.CompressionName(), "entries": entries}, err
|
||||
case "binsnoop":
|
||||
buf, err := l.rangeBytes(0, min64(l.doc.Size(), int64Param(params, "maxBytes", 64<<20)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scanner := binsnoop.New()
|
||||
return map[string]any{"results": scanner.AnalyzeBytes(buf, l.doc.Name(), false), "signatures": binsnoop.ListSignatures()}, nil
|
||||
results, err := leviathanBinsnoopDocument(l.doc, intParam(params, "max", 10000))
|
||||
return map[string]any{"results": results, "signatures": binsnoop.ListSignatures()}, err
|
||||
case "rules":
|
||||
rules, err := analysis.ParseRules(stringParam(params, "source", ""))
|
||||
if err != nil {
|
||||
@@ -565,12 +600,16 @@ func (s *Service) leviathanSpectrogramInsert(doc *document.Document, params map[
|
||||
}
|
||||
|
||||
func writeLeviathanDocumentFile(path string, doc *document.Document) error {
|
||||
return writeLeviathanDocumentRangeFile(path, doc, 0, doc.Size())
|
||||
}
|
||||
|
||||
func writeLeviathanDocumentRangeFile(path string, doc *document.Document, start, length int64) error {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := doc.ForEachChunk(0, doc.Size(), func(_ int64, chunk []byte) error {
|
||||
if err := doc.ForEachChunk(start, length, func(_ int64, chunk []byte) error {
|
||||
_, err := f.Write(chunk)
|
||||
return err
|
||||
}); err != nil {
|
||||
@@ -856,6 +895,78 @@ func boolParam(m map[string]any, key string, def bool) bool {
|
||||
return def
|
||||
}
|
||||
|
||||
func leviathanBinsnoopDocument(d *document.Document, maxResults int) (map[string]any, error) {
|
||||
if maxResults <= 0 {
|
||||
maxResults = 10000
|
||||
}
|
||||
const windowSize int64 = 32 << 20
|
||||
const overlapSize int64 = 1 << 20
|
||||
|
||||
scanner := binsnoop.New()
|
||||
size := d.Size()
|
||||
seen := make(map[string]bool)
|
||||
fileMap := make([]binsnoop.SignatureResult, 0, 128)
|
||||
truncated := false
|
||||
|
||||
maxInt := int64(^uint(0) >> 1)
|
||||
for off := int64(0); off < size; {
|
||||
readLen := windowSize + overlapSize
|
||||
if off+readLen > size {
|
||||
readLen = size - off
|
||||
}
|
||||
buf, err := d.Range(off, readLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, hit := range scanner.Scan(buf) {
|
||||
abs := off + int64(hit.Offset)
|
||||
if abs < 0 || abs > maxInt || abs >= size {
|
||||
continue
|
||||
}
|
||||
if hit.Size < 0 || int64(hit.Size) > size-abs {
|
||||
continue
|
||||
}
|
||||
hit.Offset = int(abs)
|
||||
key := fmt.Sprintf("%d:%s", hit.Offset, hit.Name)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
fileMap = append(fileMap, hit)
|
||||
if len(fileMap) >= maxResults {
|
||||
truncated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if truncated || off+windowSize >= size {
|
||||
break
|
||||
}
|
||||
off += windowSize
|
||||
}
|
||||
|
||||
sort.Slice(fileMap, func(i, j int) bool {
|
||||
if fileMap[i].Offset == fileMap[j].Offset {
|
||||
return fileMap[i].Confidence > fileMap[j].Confidence
|
||||
}
|
||||
return fileMap[i].Offset < fileMap[j].Offset
|
||||
})
|
||||
|
||||
return map[string]any{
|
||||
"file_path": d.Name(),
|
||||
"file_map": fileMap,
|
||||
"scanned_bytes": size,
|
||||
"window_bytes": windowSize,
|
||||
"overlap_bytes": overlapSize,
|
||||
"whole_file": true,
|
||||
"windowed": true,
|
||||
"truncated": truncated,
|
||||
"max_results": maxResults,
|
||||
"extractions": map[string]any{},
|
||||
"result_count": len(fileMap),
|
||||
"document_bytes": size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func leviathanSpectrogramEncodeOptions(params map[string]any) spectrogram.EncodeOptions {
|
||||
return spectrogram.EncodeOptions{
|
||||
DurationSec: floatParam(params, "durationSec", 5),
|
||||
|
||||
@@ -110,8 +110,6 @@ func (a *App) Startup(ctx context.Context) {
|
||||
// Wire the API service with all backing stores now that they exist.
|
||||
*a.Service = *api.NewService(store, a.SecretStore, a.KeychainStore, a.SessionManager, a.AuditLog, a.Prefs)
|
||||
a.Service.SetContext(ctx)
|
||||
|
||||
log.Printf("Nexus Terminal initialised – data directory: %s", cfg.DataDir)
|
||||
}
|
||||
|
||||
// Shutdown is called by Wails when the application is closing. It ensures all
|
||||
@@ -134,8 +132,6 @@ func (a *App) Shutdown(ctx context.Context) {
|
||||
if a.SecretStore != nil {
|
||||
a.SecretStore.Lock()
|
||||
}
|
||||
|
||||
log.Println("Nexus Terminal shut down cleanly")
|
||||
}
|
||||
|
||||
// DomReady is called by Wails once the frontend DOM is fully loaded. It emits
|
||||
@@ -144,7 +140,6 @@ func (a *App) DomReady(ctx context.Context) {
|
||||
runtime.EventsEmit(ctx, "backend:ready", map[string]interface{}{
|
||||
"message": "Backend is ready",
|
||||
})
|
||||
log.Println("DOM ready – backend:ready event emitted")
|
||||
}
|
||||
|
||||
// EmitOpenAbout tells the frontend to open the About tab.
|
||||
|
||||
Reference in New Issue
Block a user