Skip to content

Commit f7c44b4

Browse files
committed
Fix APK handler spinning on closed temp file after context timeout
The APK handler's processAPK loop did not check for context cancellation, causing it to iterate through every file in a zip archive even after the 60s maxTimeout fires and the underlying temp file is closed. This produces thousands of "file already closed" error logs per APK and wastes scanner resources, starving co-located scan jobs. Add a common.IsDone(ctx) check at the top of the file iteration loop, matching the established pattern in archive.go, ar.go, and rpm.go. Made-with: Cursor
1 parent bff3d26 commit f7c44b4

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

pkg/handlers/apk.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/avast/apkparser"
1818
dextk "github.com/csnewman/dextk"
1919

20+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
2021
logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
2122
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/defaults"
2223
"github.com/trufflesecurity/trufflehog/v3/pkg/iobuf"
@@ -205,6 +206,9 @@ func (h *apkHandler) processAPK(ctx logContext.Context, input fileReader, apkCha
205206

206207
// Process all files for secrets
207208
for _, file := range zipReader.File {
209+
if common.IsDone(ctx) {
210+
return ctx.Err()
211+
}
208212
if err := h.processFile(ctx, file, resTable, apkChan); err != nil {
209213
ctx.Logger().V(2).Info(fmt.Sprintf("failed to process file: %s", file.Name), "error", err)
210214
}

pkg/handlers/apk_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package handlers
22

33
import (
4+
"archive/zip"
5+
"bytes"
6+
stdCtx "context"
7+
"fmt"
48
"io"
59
"net/http"
610
"regexp"
711
"strings"
812
"testing"
13+
"time"
914

1015
"github.com/stretchr/testify/assert"
1116

1217
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
18+
"github.com/trufflesecurity/trufflehog/v3/pkg/iobuf"
1319
)
1420

1521
func TestAPKHandler(t *testing.T) {
@@ -108,3 +114,79 @@ func TestOpenValidZipInvalidAPK(t *testing.T) {
108114
err = handler.processAPK(ctx, newReader, archiveChan)
109115
assert.Contains(t, err.Error(), "resources.arsc file not found")
110116
}
117+
118+
// buildMinimalAPK creates a zip archive containing a minimal valid resources.arsc
119+
// (empty resource table with 0 packages) and the specified number of dummy XML files.
120+
func buildMinimalAPK(t *testing.T, fileCount int) []byte {
121+
t.Helper()
122+
123+
var buf bytes.Buffer
124+
zw := zip.NewWriter(&buf)
125+
126+
// Minimal resources.arsc: RES_TABLE_TYPE header with 0 packages.
127+
w, err := zw.Create("resources.arsc")
128+
if err != nil {
129+
t.Fatal(err)
130+
}
131+
resArsc := []byte{
132+
0x02, 0x00, // id = RES_TABLE_TYPE
133+
0x0c, 0x00, // headerSize = 12
134+
0x0c, 0x00, 0x00, 0x00, // totalSize = 12
135+
0x00, 0x00, 0x00, 0x00, // packageCount = 0
136+
}
137+
if _, err := w.Write(resArsc); err != nil {
138+
t.Fatal(err)
139+
}
140+
141+
for i := 0; i < fileCount; i++ {
142+
fw, err := zw.Create(fmt.Sprintf("res/layout/file_%04d.xml", i))
143+
if err != nil {
144+
t.Fatal(err)
145+
}
146+
if _, err := fw.Write([]byte("<xml>data</xml>")); err != nil {
147+
t.Fatal(err)
148+
}
149+
}
150+
151+
if err := zw.Close(); err != nil {
152+
t.Fatal(err)
153+
}
154+
return buf.Bytes()
155+
}
156+
157+
func TestProcessAPK_ExitsOnContextCancellation(t *testing.T) {
158+
apkData := buildMinimalAPK(t, 500)
159+
160+
rdr := iobuf.NewBufferedReaderSeeker(bytes.NewReader(apkData))
161+
defer rdr.Close()
162+
163+
handler := newAPKHandler()
164+
apkChan := make(chan DataOrErr, defaultBufferSize)
165+
166+
ctx, cancel := context.WithCancel(context.Background())
167+
cancel()
168+
169+
err := handler.processAPK(context.AddLogger(ctx), fileReader{BufferedReadSeeker: rdr}, apkChan)
170+
if err != stdCtx.Canceled {
171+
t.Fatalf("expected context.Canceled, got: %v", err)
172+
}
173+
}
174+
175+
func TestProcessAPK_ExitsOnDeadlineExceeded(t *testing.T) {
176+
apkData := buildMinimalAPK(t, 500)
177+
178+
rdr := iobuf.NewBufferedReaderSeeker(bytes.NewReader(apkData))
179+
defer rdr.Close()
180+
181+
handler := newAPKHandler()
182+
apkChan := make(chan DataOrErr, defaultBufferSize)
183+
184+
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
185+
defer cancel()
186+
time.Sleep(time.Millisecond)
187+
188+
err := handler.processAPK(context.AddLogger(ctx), fileReader{BufferedReadSeeker: rdr}, apkChan)
189+
if err != stdCtx.DeadlineExceeded {
190+
t.Fatalf("expected context.DeadlineExceeded, got: %v", err)
191+
}
192+
}

0 commit comments

Comments
 (0)