@@ -3,8 +3,7 @@ import { promises as fs } from 'node:fs';
33import path from 'node:path' ;
44import { crc32 , gzipSync } from 'node:zlib' ;
55import * as tar from 'tar-stream' ;
6- import { runCmdSync } from './exec.ts' ;
7- import { mkdtempForTest } from './tmp-dir.fixtures.ts' ;
6+ import { mkdtempForTest } from './internal/tmp-dir.fixtures.ts' ;
87
98export async function createArchiveWorkspace ( ) : Promise < {
109 archivePath : string ;
@@ -27,21 +26,30 @@ export async function createTruncatedTgz(archivePath: string): Promise<void> {
2726 await fs . writeFile ( archivePath , gzipSync ( headerOnly ) ) ;
2827}
2928
29+ /** Two stored entries whose second carries the encrypted general-purpose flag bit. */
3030export async function createZipWithEncryptedSecondEntry ( archivePath : string ) : Promise < void > {
31- const staging = path . join ( path . dirname ( archivePath ) , 'zip-input' ) ;
32- await fs . mkdir ( staging ) ;
33- await fs . writeFile ( path . join ( staging , 'first.txt' ) , 'first' ) ;
34- await fs . writeFile ( path . join ( staging , 'second.txt' ) , 'second' ) ;
35- runCmdSync ( 'zip' , [ '-q' , archivePath , 'first.txt' ] , { cwd : staging } ) ;
36- runCmdSync ( 'zip' , [ '-q' , '-P' , 'secret' , archivePath , 'second.txt' ] , { cwd : staging } ) ;
31+ await writeZipFixture ( archivePath , [
32+ { name : 'first.txt' , mode : ZIP_FILE_MODE , data : 'first' } ,
33+ { name : 'second.txt' , mode : ZIP_FILE_MODE , data : 'second' , encrypted : true } ,
34+ ] ) ;
3735}
3836
37+ const ZIP_FILE_MODE = 0o100644 ;
38+
3939/** One stored zip entry; `mode` is the full Unix `st_mode`, file-type bits included. */
40- export type ZipFixtureEntry = { name : string ; mode : number ; data ?: string } ;
40+ export type ZipFixtureEntry = {
41+ name : string ;
42+ mode : number ;
43+ data ?: string ;
44+ /** Declares the entry encrypted, which no archive reader may then extract. */
45+ encrypted ?: boolean ;
46+ } ;
4147
4248/**
4349 * Writes a stored (uncompressed) zip whose names and Unix attributes are exactly as given, which
44- * `zip` itself refuses to produce for links, special files, and escaping names.
50+ * `zip` itself refuses to produce for links, special files, and escaping names. An entry declared
51+ * `encrypted` carries the traditional encryption flag with a padded 12-byte encryption header and
52+ * a zero CRC, the shape readers must refuse before extracting anything.
4553 */
4654export async function writeZipFixture (
4755 archivePath : string ,
@@ -52,24 +60,29 @@ export async function writeZipFixture(
5260 let offset = 0 ;
5361 for ( const entry of entries ) {
5462 const name = Buffer . from ( entry . name , 'utf8' ) ;
55- const data = Buffer . from ( entry . data ?? '' , 'utf8' ) ;
56- const checksum = crc32 ( data ) ;
63+ const payload = Buffer . from ( entry . data ?? '' , 'utf8' ) ;
64+ // A traditionally encrypted stored entry carries a 12-byte encryption header before the
65+ // ciphertext, and readers validate that against the declared sizes, so the record declares
66+ // it and the data section pads it; the payload itself never decrypts to anything.
67+ const data = entry . encrypted ? Buffer . concat ( [ Buffer . alloc ( 12 ) , payload ] ) : payload ;
68+ const checksum = entry . encrypted ? 0 : crc32 ( payload ) ;
69+ const flags = 0x0800 | ( entry . encrypted ? 0x0001 : 0 ) ;
5770 const local = Buffer . alloc ( 30 ) ;
5871 local . writeUInt32LE ( 0x04034b50 , 0 ) ;
5972 local . writeUInt16LE ( 20 , 4 ) ;
60- local . writeUInt16LE ( 0x0800 , 6 ) ;
73+ local . writeUInt16LE ( flags , 6 ) ;
6174 local . writeUInt32LE ( checksum , 14 ) ;
6275 local . writeUInt32LE ( data . length , 18 ) ;
63- local . writeUInt32LE ( data . length , 22 ) ;
76+ local . writeUInt32LE ( payload . length , 22 ) ;
6477 local . writeUInt16LE ( name . length , 26 ) ;
6578 const central = Buffer . alloc ( 46 ) ;
6679 central . writeUInt32LE ( 0x02014b50 , 0 ) ;
6780 central . writeUInt16LE ( ( 3 << 8 ) | 20 , 4 ) ;
6881 central . writeUInt16LE ( 20 , 6 ) ;
69- central . writeUInt16LE ( 0x0800 , 8 ) ;
82+ central . writeUInt16LE ( flags , 8 ) ;
7083 central . writeUInt32LE ( checksum , 16 ) ;
7184 central . writeUInt32LE ( data . length , 20 ) ;
72- central . writeUInt32LE ( data . length , 24 ) ;
85+ central . writeUInt32LE ( payload . length , 24 ) ;
7386 central . writeUInt16LE ( name . length , 28 ) ;
7487 central . writeUInt32LE ( ( entry . mode << 16 ) >>> 0 , 38 ) ;
7588 central . writeUInt32LE ( offset , 42 ) ;
0 commit comments