Skip to content

Commit a30a751

Browse files
committed
feat: include file size in derived files view command
1 parent 1a272cf commit a30a751

3 files changed

Lines changed: 47 additions & 27 deletions

File tree

cmd/scanui.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func uploadFileCmd(index int, web webapi.Service, filename, token string) tea.Cm
107107
sha256: file.SHA256,
108108
size: file.Size,
109109
isArchive: file.IsArchive,
110-
childHashes: file.ArchiveFiles,
110+
childHashes: derivedHashes(file.DerivedFiles),
111111
}
112112
} else if forceRescanFlag {
113113
// Fetch the existing file to check if it's an archive.
@@ -116,19 +116,19 @@ func uploadFileCmd(index int, web webapi.Service, filename, token string) tea.Cm
116116
return fileUploadedMsg{index: index, err: fmt.Errorf("get file: %w", err)}
117117
}
118118

119-
if file.IsArchive && len(file.ArchiveFiles) > 0 {
119+
if file.IsArchive && len(file.DerivedFiles) > 0 {
120120
// Archive: rescan each child, not the container itself.
121-
for _, childHash := range file.ArchiveFiles {
122-
if err := web.Rescan(childHash, token, osFlag, enableDetonationFlag, timeoutFlag); err != nil {
123-
return fileUploadedMsg{index: index, err: fmt.Errorf("rescan child %s: %w", childHash[:12], err)}
121+
for _, df := range file.DerivedFiles {
122+
if err := web.Rescan(df.SHA256, token, osFlag, enableDetonationFlag, timeoutFlag); err != nil {
123+
return fileUploadedMsg{index: index, err: fmt.Errorf("rescan child %s: %w", df.SHA256[:12], err)}
124124
}
125125
}
126126
return fileUploadedMsg{
127127
index: index,
128128
sha256: sha256,
129129
size: file.Size,
130130
isArchive: true,
131-
childHashes: file.ArchiveFiles,
131+
childHashes: derivedHashes(file.DerivedFiles),
132132
}
133133
}
134134

@@ -181,18 +181,18 @@ func rescanFileCmd(index int, web webapi.Service, sha256, token string) tea.Cmd
181181
return fileUploadedMsg{index: index, err: fmt.Errorf("get file: %w", err)}
182182
}
183183

184-
if file.IsArchive && len(file.ArchiveFiles) > 0 {
185-
for _, childHash := range file.ArchiveFiles {
186-
if err := web.Rescan(childHash, token, osFlag, enableDetonationFlag, timeoutFlag); err != nil {
187-
return fileUploadedMsg{index: index, err: fmt.Errorf("rescan child %s: %w", childHash[:12], err)}
184+
if file.IsArchive && len(file.DerivedFiles) > 0 {
185+
for _, df := range file.DerivedFiles {
186+
if err := web.Rescan(df.SHA256, token, osFlag, enableDetonationFlag, timeoutFlag); err != nil {
187+
return fileUploadedMsg{index: index, err: fmt.Errorf("rescan child %s: %w", df.SHA256[:12], err)}
188188
}
189189
}
190190
return fileUploadedMsg{
191191
index: index,
192192
sha256: sha256,
193193
size: file.Size,
194194
isArchive: true,
195-
childHashes: file.ArchiveFiles,
195+
childHashes: derivedHashes(file.DerivedFiles),
196196
}
197197
}
198198

@@ -541,3 +541,11 @@ func truncSha(sha string) string {
541541
return sha
542542
}
543543

544+
func derivedHashes(files []entity.DerivedFile) []string {
545+
hashes := make([]string, len(files))
546+
for i, f := range files {
547+
hashes[i] = f.SHA256
548+
}
549+
return hashes
550+
}
551+

cmd/view.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ func printFileReport(file entity.File, webSvc webapi.Service) {
8888
printKV("Packer", strings.Join(file.Packer, ", "))
8989
}
9090
if file.IsArchive {
91-
printKV("Archive", fmt.Sprintf("yes (%d files)", len(file.ArchiveFiles)))
91+
printKV("Archive", fmt.Sprintf("yes (%d files)", len(file.DerivedFiles)))
9292
}
93-
if file.ArchiveSHA256 != "" {
94-
printKV("Parent", file.ArchiveSHA256)
93+
if file.ParentSHA256 != "" {
94+
printKV("Parent", file.ParentSHA256)
9595
}
9696
if file.FirstSeen != 0 {
9797
printKV("First Seen", formatTimestamp(file.FirstSeen))
@@ -103,8 +103,8 @@ func printFileReport(file entity.File, webSvc webapi.Service) {
103103

104104
if file.IsArchive {
105105
// Archives only scan their children, skip verdict and AV results.
106-
if len(file.ArchiveFiles) > 0 {
107-
printArchiveChildren(file.ArchiveFiles, webSvc)
106+
if len(file.DerivedFiles) > 0 {
107+
printArchiveChildren(file.DerivedFiles, webSvc)
108108
}
109109
} else {
110110
// Classification.
@@ -122,6 +122,7 @@ type childSummary struct {
122122
sha256 string
123123
classification string
124124
format string
125+
size int64
125126
positives int
126127
enginesCount int
127128
err error
@@ -136,6 +137,7 @@ func fetchChildSummary(sha256 string, webSvc webapi.Service) childSummary {
136137
sha256: sha256,
137138
classification: file.Classification,
138139
format: file.Format,
140+
size: file.Size,
139141
}
140142
if file.Extension != "" {
141143
cs.format += "/" + file.Extension
@@ -153,28 +155,30 @@ func fetchChildSummary(sha256 string, webSvc webapi.Service) childSummary {
153155
return cs
154156
}
155157

156-
func printArchiveChildren(archiveFiles []string, webSvc webapi.Service) {
157-
fmt.Println(headerStyle.Render(fmt.Sprintf("Archive Contents (%d files)", len(archiveFiles))))
158+
func printArchiveChildren(derivedFiles []entity.DerivedFile, webSvc webapi.Service) {
159+
fmt.Println(headerStyle.Render(fmt.Sprintf("Archive Contents (%d files)", len(derivedFiles))))
158160
fmt.Println()
159161

160162
// Table header.
161163
fmtCol := lipgloss.NewStyle().Width(16)
164+
sizeCol := lipgloss.NewStyle().Width(10)
162165
avCol := lipgloss.NewStyle().Width(14)
163166
clsCol := lipgloss.NewStyle().Width(12)
164167

165-
fmt.Printf(" %s %s %s %s\n",
168+
fmt.Printf(" %s %s %s %s %s\n",
166169
styleDim.Render(fmt.Sprintf("%-64s", "SHA256")),
167170
styleDim.Render(fmtCol.Render("FORMAT")),
171+
styleDim.Render(sizeCol.Render("SIZE")),
168172
styleDim.Render(avCol.Render("DETECTIONS")),
169173
styleDim.Render(clsCol.Render("VERDICT")),
170174
)
171-
fmt.Printf(" %s\n", styleDim.Render(strings.Repeat("─", 108)))
175+
fmt.Printf(" %s\n", styleDim.Render(strings.Repeat("─", 119)))
172176

173-
for _, sha := range archiveFiles {
174-
cs := fetchChildSummary(sha, webSvc)
177+
for _, df := range derivedFiles {
178+
cs := fetchChildSummary(df.SHA256, webSvc)
175179
if cs.err != nil {
176180
fmt.Printf(" %s %s\n",
177-
sha,
181+
df.SHA256,
178182
styleError.Render("error: "+cs.err.Error()),
179183
)
180184
continue
@@ -187,9 +191,10 @@ func printArchiveChildren(archiveFiles []string, webSvc webapi.Service) {
187191
detStr = cleanStyle.Render(detStr)
188192
}
189193

190-
fmt.Printf(" %s %s %s %s\n",
194+
fmt.Printf(" %s %s %s %s %s\n",
191195
cs.sha256,
192196
fmtCol.Render(cs.format),
197+
sizeCol.Render(formatSize(cs.size)),
193198
avCol.Render(detStr),
194199
clsCol.Render(renderClassification(cs.classification)),
195200
)

internal/entity/file.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ type File struct {
3434
BehaviorReportID string `json:"behavior_report_id,omitempty"`
3535
Status int `json:"status,omitempty"`
3636
Classification string `json:"classification,omitempty"`
37-
IsArchive bool `json:"is_archive,omitempty"`
38-
ArchiveFiles []string `json:"archive_files,omitempty"`
39-
ArchiveSHA256 string `json:"archive_sha256,omitempty"`
37+
IsArchive bool `json:"is_archive,omitempty"`
38+
DerivedFiles []DerivedFile `json:"derived_files,omitempty"`
39+
ParentSHA256 string `json:"parent_sha256,omitempty"`
40+
}
41+
42+
// DerivedFile is a child file produced during analysis of a parent — either a
43+
// member extracted from an archive or the decrypted payload of a protected document.
44+
type DerivedFile struct {
45+
Name string `json:"name"`
46+
SHA256 string `json:"sha256"`
4047
}
4148

4249
// Submission represents a file submission.

0 commit comments

Comments
 (0)