Files
nexus/audits/20260616-1111-securityAudit.md
2026-06-16 11:51:07 +01:00

6.0 KiB

Security Audit Report

1. Executive Summary

This fix-mode audit focused on file-reading and response-reading paths that could allocate complete files or unbounded remote bodies in memory.

The main risk fixed was local denial of service from large user-selected files, imported Leviathan artefacts, recordings, SSH key files, vault files, and remote update/GitHub API responses. Where the codebase already had a chunked document model, saves and exports now use it. Where a parser still requires a complete byte slice, the code now rejects oversized inputs before allocation.

Verification passed with targeted package tests, go test ./..., frontend regression tests, frontend build, and git diff --check.

2. Scope Reviewed

Reviewed and changed:

  • Leviathan file-backed document save, case export, spectrogram image/audio conversion, PDF parsing, and BinSnoop file helpers.
  • Wails API helpers that read SSH public keys and Leviathan image/audio inputs.
  • SSH and VNC tunnel private-key loading.
  • GitHub client and update-checker HTTP response handling.
  • Recording playback parsing.
  • Encrypted secrets vault loading.

Not live-tested:

  • A packaged Wails desktop binary.
  • Real remote SSH/VNC/GitHub endpoints.
  • Very large real-world media corpora beyond automated guard tests.

3. Methodology

I searched for ReadFile, ReadAll, and whole-document Range(0, size) patterns, then classified each as chunked, bounded, embedded/test-only, or unsafe. Fixes were kept to low-risk local changes:

  • Prefer Document.ForEachChunk for writing/exporting working file bytes.
  • Stream line-oriented recordings with bufio.Scanner.
  • Limit HTTP response reads with io.LimitReader.
  • Add os.Stat size checks before unavoidable bounded os.ReadFile calls.
  • Add parser-specific caps before whole-document reads.

4. Findings Summary

ID Severity Confidence Title Component Status
SEC-20260616-001 Medium High Leviathan save/export and spectrogram tools could materialize large files Leviathan API, spectrogram Fixed
SEC-20260616-002 Medium High Remote API/update responses were read without size limits GitHub client, updater Fixed
SEC-20260616-003 Medium High Local app data/key reads lacked size caps SSH keys, VNC keys, vault, BinSnoop Fixed
SEC-20260616-004 Low High Recording playback loaded the whole capped recording file Recording playback Fixed
SEC-20260616-005 Low Medium Some parsers still require complete bounded buffers PDF, cert/key scanners, media decoders Accepted with caps

5. Detailed Findings

SEC-20260616-001: Leviathan full-file allocation paths

Leviathan save/export and spectrogram operations could duplicate entire working files in memory. Case export also built the zip archive in memory before writing it.

Fixed in:

  • internal/api/leviathan.go
  • internal/leviathan/spectrogram/spectrogram.go
  • internal/leviathan/pdf/pdf.go

Changes:

  • Working-copy save writes via Document.ForEachChunk.
  • Case export streams working.bin directly into zip.Writer backed by the destination file.
  • Spectrogram encode/view/insert rejects oversized image/audio documents before bounded decode.
  • Image decoding checks compressed byte size and decoded pixel dimensions before full image decode.
  • PDF parsing rejects files above 128 MiB before whole-document structural parsing.
  • Long-running spectrogram/export tools no longer hold the Leviathan engine lock while doing heavy work or native save dialogs.

SEC-20260616-002: Unbounded remote response reads

GitHub and update-check response bodies could be read without an explicit maximum.

Fixed in:

  • internal/githubclient/githubclient.go
  • internal/updater/updater.go

Changes:

  • GitHub JSON responses are capped at 4 MiB.
  • GitHub error bodies are capped at 64 KiB.
  • Update release/manifest responses are capped at 1 MiB.
  • Repository archive downloads continue to stream to disk instead of memory.

SEC-20260616-003: Local key/vault/helper reads lacked caps

Several local helper paths read files directly even though the expected files are small.

Fixed in:

  • internal/api/api.go
  • internal/sshclient/sshclient.go
  • internal/sessions/manager.go
  • internal/secrets/secrets.go
  • internal/leviathan/binsnoop/common.go
  • internal/leviathan/binsnoop/binsnoop.go

Changes:

  • SSH public keys are capped at 64 KiB.
  • SSH private keys for SSH and VNC tunnel auth are capped at 1 MiB.
  • Secret vault files are capped at 10 MiB.
  • BinSnoop direct file helpers are capped at 64 MiB; the UI path already uses a bounded buffer.

SEC-20260616-004: Recording playback whole-file read

Recording playback had a 100 MiB file cap, but still read and split the full file in memory.

Fixed in:

  • internal/recording/recorder.go

Changes:

  • Playback now opens the recording and scans line-by-line.
  • Individual playback lines are capped at 8 MiB.
  • Existing malformed-event tolerance is preserved.

6. Residual Risk

Some operations still need bounded complete buffers because downstream parsers or decoders require byte slices: image/audio spectrogram conversion, PDF structural parsing, certificate/key scanning, and small SSH/vault/public-key parsing. These now fail before allocation when inputs exceed explicit caps.

Truly streaming versions of every binary parser would be a larger Leviathan architecture project. The current fixes remove the unbounded allocation behavior while preserving tool behavior for normal files.

7. Verification

Passed:

  • go test ./internal/api ./internal/leviathan/spectrogram ./internal/leviathan/pdf ./internal/leviathan/binsnoop ./internal/updater ./internal/githubclient ./internal/secrets ./internal/recording ./internal/sshclient ./internal/sessions -count=1
  • go test ./... -count=1
  • npm --prefix frontend test
  • npm --prefix frontend run build
  • git diff --check

New tests cover oversized GitHub/update bodies, oversized spectrogram images, bounded Leviathan document reads, chunked patched-file writes, oversized public keys, oversized vault files, oversized PDFs, and BinSnoop file caps.