88 "fmt"
99 "os"
1010 "path/filepath"
11+ "strings"
1112 "time"
1213
1314 "github.com/charmbracelet/bubbles/spinner"
@@ -107,7 +108,7 @@ func uploadFileCmd(index int, web webapi.Service, filename, token string) tea.Cm
107108 sha256 : file .SHA256 ,
108109 size : file .Size ,
109110 isArchive : file .IsArchive ,
110- childHashes : file .ArchiveFiles ,
111+ childHashes : derivedHashes ( file .DerivedFiles ) ,
111112 }
112113 } else if forceRescanFlag {
113114 // Fetch the existing file to check if it's an archive.
@@ -116,19 +117,19 @@ func uploadFileCmd(index int, web webapi.Service, filename, token string) tea.Cm
116117 return fileUploadedMsg {index : index , err : fmt .Errorf ("get file: %w" , err )}
117118 }
118119
119- if file .IsArchive && len (file .ArchiveFiles ) > 0 {
120+ if file .IsArchive && len (file .DerivedFiles ) > 0 {
120121 // 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 )}
122+ for _ , df := range file .DerivedFiles {
123+ if err := web .Rescan (df . SHA256 , token , osFlag , enableDetonationFlag , timeoutFlag ); err != nil {
124+ return fileUploadedMsg {index : index , err : fmt .Errorf ("rescan child %s: %w" , df . SHA256 [:12 ], err )}
124125 }
125126 }
126127 return fileUploadedMsg {
127128 index : index ,
128129 sha256 : sha256 ,
129130 size : file .Size ,
130131 isArchive : true ,
131- childHashes : file .ArchiveFiles ,
132+ childHashes : derivedHashes ( file .DerivedFiles ) ,
132133 }
133134 }
134135
@@ -181,18 +182,18 @@ func rescanFileCmd(index int, web webapi.Service, sha256, token string) tea.Cmd
181182 return fileUploadedMsg {index : index , err : fmt .Errorf ("get file: %w" , err )}
182183 }
183184
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 )}
185+ if file .IsArchive && len (file .DerivedFiles ) > 0 {
186+ for _ , df := range file .DerivedFiles {
187+ if err := web .Rescan (df . SHA256 , token , osFlag , enableDetonationFlag , timeoutFlag ); err != nil {
188+ return fileUploadedMsg {index : index , err : fmt .Errorf ("rescan child %s: %w" , df . SHA256 [:12 ], err )}
188189 }
189190 }
190191 return fileUploadedMsg {
191192 index : index ,
192193 sha256 : sha256 ,
193194 size : file .Size ,
194195 isArchive : true ,
195- childHashes : file .ArchiveFiles ,
196+ childHashes : derivedHashes ( file .DerivedFiles ) ,
196197 }
197198 }
198199
@@ -307,11 +308,12 @@ func (m scanModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
307308 m .files [i ].sha256 = msg .sha256
308309
309310 if msg .isArchive && len (msg .childHashes ) > 0 {
310- // Archive container: mark it as done immediately and track children.
311- m .files [i ].state = stateDone
311+ // Archive container: poll parent for completion and track children.
312+ m .files [i ].state = stateScanning
312313 m .files [i ].isArchive = true
313314 m .files [i ].childCount = len (msg .childHashes )
314315 m .files [i ].size = msg .size
316+ cmds = append (cmds , pollStatusCmd (i , m .web , msg .sha256 ))
315317
316318 archiveName := filepath .Base (m .files [i ].filename )
317319 for _ , childHash := range msg .childHashes {
@@ -502,6 +504,9 @@ func (m scanModel) View() string {
502504 f .result .MultiAV .Positives , f .result .MultiAV .EnginesCount )
503505 }
504506 }
507+ if f .result != nil && f .result .Encrypted {
508+ line += renderEncryptionStatus (f .result )
509+ }
505510 doneRows = append (doneRows , doneRow {line })
506511 case stateError :
507512 line := styleError .Render ("✗" ) + " " + name + " " + styleError .Render (f .err .Error ())
@@ -541,3 +546,29 @@ func truncSha(sha string) string {
541546 return sha
542547}
543548
549+ func derivedHashes (files []entity.DerivedFile ) []string {
550+ hashes := make ([]string , len (files ))
551+ for i , f := range files {
552+ hashes [i ] = f .SHA256
553+ }
554+ return hashes
555+ }
556+
557+ func renderEncryptionStatus (s * scanSummary ) string {
558+ if s .DecryptionSuccess == nil {
559+ return " " + styleWarning .Render ("encrypted" )
560+ }
561+ if * s .DecryptionSuccess {
562+ out := " " + styleSuccess .Render ("decrypted" )
563+ if s .SuccessfulPassword != "" {
564+ out += " " + styleDim .Render ("(pwd: " + s .SuccessfulPassword + ")" )
565+ }
566+ return out
567+ }
568+ out := " " + styleError .Render ("decryption failed" )
569+ if len (s .AttemptedPasswords ) > 0 {
570+ out += " " + styleDim .Render ("(tried: " + strings .Join (s .AttemptedPasswords , ", " )+ ")" )
571+ }
572+ return out
573+ }
574+
0 commit comments