@@ -14,6 +14,26 @@ import (
1414 "github.com/stretchr/testify/require"
1515)
1616
17+ func TestBundle_Ensure_CacheDirIsPrivate (t * testing.T ) {
18+ t .Parallel ()
19+
20+ // The cache holds an executable binary that will later be spawned. A
21+ // world- or group-writable cache permits local code injection; the
22+ // directory must be 0o700.
23+ cacheDir := filepath .Join (t .TempDir (), "cache" )
24+ b := NewBundle ("v-private" , []File {
25+ {Name : "f" , Content : []byte ("x" ), Mode : 0o644 },
26+ })
27+
28+ _ , err := b .Ensure (cacheDir )
29+ require .NoError (t , err )
30+
31+ info , err := os .Stat (cacheDir )
32+ require .NoError (t , err )
33+ assert .Equal (t , os .FileMode (0o700 ), info .Mode ().Perm (),
34+ "cache dir must be 0o700; got %o" , info .Mode ().Perm ())
35+ }
36+
1737func TestBundle_Ensure_ExtractsFiles (t * testing.T ) {
1838 t .Parallel ()
1939
@@ -132,9 +152,9 @@ func TestBundle_Ensure_EmptyBundle(t *testing.T) {
132152 require .NoError (t , err )
133153 assert .True (t , info .IsDir ())
134154
135- versionData , err := os .ReadFile (filepath .Join (dir , ".version" ))
155+ manifestData , err := os .ReadFile (filepath .Join (dir , manifestName ))
136156 require .NoError (t , err )
137- assert .NotEmpty (t , versionData )
157+ assert .NotEmpty (t , manifestData )
138158}
139159
140160func TestBundle_Ensure_ConcurrentAccess (t * testing.T ) {
@@ -261,15 +281,15 @@ func TestBundle_ComputeHash_EmptyBundle(t *testing.T) {
261281func TestBundle_IsValid_MatchingHash (t * testing.T ) {
262282 t .Parallel ()
263283
284+ // End-to-end through Ensure: the manifest it wrote must satisfy
285+ // isValid on the same inputs.
264286 b := NewBundle ("v1" , []File {
265287 {Name : "x.txt" , Content : []byte ("data" ), Mode : 0o644 },
266288 })
267- hash := b .computeHash ()
268-
269- dir := t .TempDir ()
270- require .NoError (t , os .WriteFile (filepath .Join (dir , ".version" ), []byte (hash ), 0o644 ))
289+ dir , err := b .Ensure (t .TempDir ())
290+ require .NoError (t , err )
271291
272- assert .True (t , b .isValid (dir , hash ))
292+ assert .True (t , b .isValid (dir , b . computeHash () ))
273293}
274294
275295func TestBundle_IsValid_WrongHash (t * testing.T ) {
@@ -278,25 +298,51 @@ func TestBundle_IsValid_WrongHash(t *testing.T) {
278298 b := NewBundle ("v1" , []File {
279299 {Name : "x.txt" , Content : []byte ("data" ), Mode : 0o644 },
280300 })
281- hash := b .computeHash ()
282-
283- dir := t .TempDir ()
284- require .NoError (t , os .WriteFile (filepath .Join (dir , ".version" ), []byte ("wronghash" ), 0o644 ))
301+ dir , err := b .Ensure (t .TempDir ())
302+ require .NoError (t , err )
285303
286- assert .False (t , b .isValid (dir , hash ))
304+ assert .False (t , b .isValid (dir , "wronghash" ))
287305}
288306
289- func TestBundle_IsValid_MissingVersionFile (t * testing.T ) {
307+ func TestBundle_IsValid_MissingManifest (t * testing.T ) {
290308 t .Parallel ()
291309
292310 b := NewBundle ("v1" , nil )
293311 hash := b .computeHash ()
294312
295313 dir := t .TempDir ()
296- // dir exists but has no .version file.
314+ // dir exists but has no manifest file.
297315 assert .False (t , b .isValid (dir , hash ))
298316}
299317
318+ func TestBundle_IsValid_TamperedFileTriggersReextract (t * testing.T ) {
319+ t .Parallel ()
320+
321+ // If a cached file has been modified after extraction, isValid must
322+ // return false so Ensure re-extracts rather than spawning a tampered
323+ // binary.
324+ cacheDir := t .TempDir ()
325+ b := NewBundle ("v-tamper" , []File {
326+ {Name : "binary" , Content : []byte ("original-content" ), Mode : 0o755 },
327+ })
328+ dir , err := b .Ensure (cacheDir )
329+ require .NoError (t , err )
330+
331+ // Overwrite the cached file with different content.
332+ require .NoError (t , os .WriteFile (filepath .Join (dir , "binary" ), []byte ("tampered" ), 0o755 ))
333+
334+ assert .False (t , b .isValid (dir , b .computeHash ()),
335+ "tampered cached file must not be treated as valid" )
336+
337+ // A subsequent Ensure should re-extract the original content.
338+ dir2 , err := b .Ensure (cacheDir )
339+ require .NoError (t , err )
340+ got , err := os .ReadFile (filepath .Join (dir2 , "binary" ))
341+ require .NoError (t , err )
342+ assert .Equal (t , "original-content" , string (got ),
343+ "Ensure must re-extract the original content after tamper detection" )
344+ }
345+
300346func TestBundle_IsValid_NonexistentDir (t * testing.T ) {
301347 t .Parallel ()
302348
0 commit comments