@futpib/parser 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/apk.d.ts +29 -3
- package/build/apkParser.d.ts +15 -2
- package/build/apkParser.js +70 -41
- package/build/apkParser.test.js +2 -2
- package/build/apkUnparser.d.ts +4 -0
- package/build/apkUnparser.js +90 -0
- package/build/apkUnparser.test.d.ts +1 -0
- package/build/apkUnparser.test.js +26 -0
- package/build/arbitraryFileSystemEntry.js +1 -1
- package/build/arbitraryZip.d.ts +1 -1
- package/build/arbitraryZip.js +13 -19
- package/build/arbitraryZipPermissions.d.ts +1 -8
- package/build/arbitraryZipPermissions.js +1 -16
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arbitraryZipStream.js +3 -3
- package/build/arrayParser.d.ts +1 -1
- package/build/arrayParser.js +2 -2
- package/build/arrayParser.test.js +2 -2
- package/build/arrayUnparser.d.ts +2 -0
- package/build/arrayUnparser.js +8 -0
- package/build/bsonParser.test.js +2 -2
- package/build/customInvariant.d.ts +4 -0
- package/build/customInvariant.js +11 -0
- package/build/debugLogParser.d.ts +1 -1
- package/build/debugLogParser.js +1 -1
- package/build/elementParser.d.ts +2 -2
- package/build/elementParser.js +1 -1
- package/build/endOfInputParser.d.ts +2 -2
- package/build/exactElementParser.d.ts +1 -1
- package/build/exactSequenceParser.js +1 -1
- package/build/index.d.ts +5 -2
- package/build/index.js +3 -0
- package/build/inputReader.d.ts +3 -3
- package/build/inputReader.js +6 -6
- package/build/inputReader.test.js +7 -7
- package/build/javaKeyStore.d.ts +1 -0
- package/build/javaKeyStore.js +1 -0
- package/build/javaKeyStoreParser.d.ts +2 -0
- package/build/javaKeyStoreParser.js +67 -0
- package/build/javaKeyStoreParser.test.d.ts +1 -0
- package/build/javaKeyStoreParser.test.js +16 -0
- package/build/javaKeystoreParser.d.ts +2 -0
- package/build/javaKeystoreParser.js +7 -0
- package/build/jsonParser.js +3 -2
- package/build/jsonParser.test.js +2 -2
- package/build/listParser.d.ts +1 -1
- package/build/listParser.js +5 -5
- package/build/negativeLookahead.d.ts +1 -1
- package/build/negativeLookahead.js +16 -18
- package/build/negativeLookaheadParser.d.ts +2 -0
- package/build/negativeLookaheadParser.js +18 -0
- package/build/optionalParser.d.ts +1 -1
- package/build/optionalParser.js +2 -2
- package/build/parser.d.ts +3 -3
- package/build/parser.js +3 -3
- package/build/parser.test.js +16 -16
- package/build/parserAccessorParser.d.ts +1 -1
- package/build/parserConsumedSequenceParser.d.ts +2 -0
- package/build/parserConsumedSequenceParser.js +17 -0
- package/build/parserContext.d.ts +6 -6
- package/build/parserContext.js +15 -14
- package/build/parserContext.test.js +7 -7
- package/build/parserCreatorCompose.d.ts +3 -3
- package/build/parserCreatorCompose.js +2 -2
- package/build/parserImplementationInvariant.d.ts +1 -1
- package/build/parserImplementationInvariant.js +2 -2
- package/build/parserInputCompanion.d.ts +20 -0
- package/build/parserInputCompanion.js +30 -0
- package/build/parserInvariant.d.ts +1 -1
- package/build/parserInvariant.js +1 -1
- package/build/quantifierParser.d.ts +2 -0
- package/build/quantifierParser.js +17 -0
- package/build/sequenceBuffer.d.ts +3 -3
- package/build/sequenceBuffer.js +6 -6
- package/build/sequenceBuffer.test.js +2 -2
- package/build/sequenceUnparser.d.ts +2 -0
- package/build/sequenceUnparser.js +6 -0
- package/build/sliceBoundedParser.d.ts +1 -1
- package/build/sliceBoundedParser.js +1 -1
- package/build/sliceBoundedParser.test.js +2 -2
- package/build/terminatedArrayParser.d.ts +1 -1
- package/build/terminatedArrayParser.js +3 -3
- package/build/uint8Array.d.ts +1 -0
- package/build/uint8Array.js +7 -0
- package/build/unparser.d.ts +8 -0
- package/build/unparser.js +104 -0
- package/build/unparser.test.d.ts +1 -0
- package/build/unparser.test.js +150 -0
- package/build/unparserContext.d.ts +31 -0
- package/build/unparserContext.js +74 -0
- package/build/unparserError.d.ts +9 -0
- package/build/unparserError.js +9 -0
- package/build/unparserImplementationInvariant.d.ts +2 -0
- package/build/unparserImplementationInvariant.js +5 -0
- package/build/unparserInputCompanion.d.ts +15 -0
- package/build/unparserInputCompanion.js +13 -0
- package/build/unparserOutputCompanion.d.ts +15 -0
- package/build/unparserOutputCompanion.js +13 -0
- package/build/zip.d.ts +9 -17
- package/build/zipParser.d.ts +13 -10
- package/build/zipParser.js +48 -60
- package/build/zipParser.test.js +2 -7
- package/build/zipUnparser.d.ts +5 -0
- package/build/zipUnparser.js +171 -0
- package/build/zipUnparser.test.d.ts +1 -0
- package/build/zipUnparser.test.js +80 -0
- package/package.json +4 -2
- package/src/apk.ts +35 -3
- package/src/apkParser.test.ts +2 -2
- package/src/apkParser.test.ts.md +114 -111
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +150 -85
- package/src/apkUnparser.test.ts +37 -0
- package/src/apkUnparser.ts +120 -0
- package/src/arbitraryFileSystemEntry.ts +2 -4
- package/src/arbitraryZip.ts +20 -27
- package/src/arbitraryZipPermissions.ts +0 -25
- package/src/arbitraryZipStream.ts +4 -4
- package/src/arrayParser.test.ts +3 -3
- package/src/arrayParser.ts +3 -2
- package/src/arrayUnparser.ts +13 -0
- package/src/bsonParser.test.ts +2 -2
- package/src/bsonParser.ts +3 -3
- package/src/{parserInvariant.ts → customInvariant.ts} +1 -1
- package/src/debugLogParser.ts +1 -1
- package/src/elementParser.ts +3 -3
- package/src/endOfInputParser.ts +4 -4
- package/src/exactElementParser.ts +1 -1
- package/src/exactSequenceParser.ts +2 -2
- package/src/index.ts +15 -2
- package/src/inputReader.test.ts +7 -7
- package/src/inputReader.ts +5 -5
- package/src/javaKeyStore.ts +0 -0
- package/src/javaKeyStoreParser.test.ts +23 -0
- package/src/javaKeyStoreParser.test.ts.md +103 -0
- package/src/javaKeyStoreParser.test.ts.snap +0 -0
- package/src/javaKeyStoreParser.ts +136 -0
- package/src/jsonParser.test.ts +2 -2
- package/src/jsonParser.ts +13 -12
- package/src/listParser.ts +6 -6
- package/src/negativeLookaheadParser.ts +24 -0
- package/src/optionalParser.ts +3 -3
- package/src/parser.test.ts +19 -17
- package/src/parser.ts +7 -7
- package/src/parserAccessorParser.ts +1 -1
- package/src/parserConsumedSequenceParser.ts +20 -0
- package/src/parserContext.test.ts +7 -7
- package/src/parserContext.ts +18 -14
- package/src/parserCreatorCompose.ts +6 -6
- package/src/parserImplementationInvariant.ts +2 -2
- package/src/{inputCompanion.ts → parserInputCompanion.ts} +10 -6
- package/src/quantifierParser.ts +25 -0
- package/src/sequenceBuffer.test.ts +2 -2
- package/src/sequenceBuffer.ts +5 -5
- package/src/sequenceUnparser.ts +9 -0
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +2 -2
- package/src/terminatedArrayParser.ts +3 -3
- package/src/uint8Array.ts +10 -0
- package/src/unparser.test.ts +221 -0
- package/src/unparser.ts +209 -0
- package/src/unparserContext.ts +127 -0
- package/src/unparserError.ts +12 -0
- package/src/unparserImplementationInvariant.ts +6 -0
- package/src/unparserOutputCompanion.ts +24 -0
- package/src/zip.ts +10 -22
- package/src/zipParser.test.ts +2 -8
- package/src/zipParser.ts +147 -129
- package/src/zipUnparser.test.ts +119 -0
- package/src/zipUnparser.ts +239 -0
- package/src/negativeLookahead.ts +0 -26
package/src/apkParser.ts
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
import { createArrayParser } from './arrayParser.js';
|
|
3
|
+
import { createOptionalParser } from './optionalParser.js';
|
|
4
|
+
import { type Parser, setParserName } from './parser.js';
|
|
5
|
+
import { promiseCompose } from './promiseCompose.js';
|
|
6
|
+
import { createTupleParser } from './tupleParser.js';
|
|
7
|
+
import {
|
|
8
|
+
zip64EndOfCentralDirectoryLocatorParser,
|
|
9
|
+
zip64EndOfCentralDirectoryRecordParser,
|
|
10
|
+
zipArchiveDecryptionHeaderParser,
|
|
11
|
+
zipArchiveExtraDataRecordParser,
|
|
12
|
+
ZipCentralDirectoryHeader,
|
|
13
|
+
zipCentralDirectoryHeaderParser,
|
|
14
|
+
ZipEndOfCentralDirectoryRecord,
|
|
15
|
+
zipEndOfCentralDirectoryRecordParser,
|
|
16
|
+
zipFromZipSegments,
|
|
17
|
+
ZipLocalFile,
|
|
18
|
+
zipLocalFileParser,
|
|
19
|
+
} from './zipParser.js';
|
|
20
|
+
import { ApkSignatureV2AdditionalAttribute, ApkSignatureV2Digest, ApkSignatureV2Signature, ApkSignatureV2SignedData, ApkSignatureV2Signer, type Apk, type ApkSigningBlock, type ApkSigningBlockPair } from './apk.js';
|
|
21
|
+
import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js';
|
|
22
|
+
import { parserCreatorCompose } from './parserCreatorCompose.js';
|
|
23
|
+
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
24
|
+
import { createSliceBoundedParser } from './sliceBoundedParser.js';
|
|
25
|
+
import { createDisjunctionParser } from './disjunctionParser.js';
|
|
26
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
27
|
+
import { createParserConsumedSequenceParser } from './parserConsumedSequenceParser.js';
|
|
15
28
|
|
|
16
29
|
// https://source.android.com/docs/security/features/apksigning/v2#apk-signing-block
|
|
17
30
|
|
|
@@ -46,8 +59,8 @@ const createUint32LengthPrefixedSliceBoundedParser = <T>(innerParser: Parser<T,
|
|
|
46
59
|
const createUint32LengthPrefixedSliceBoundedArrayParser = <T>(innerParser: Parser<T, Uint8Array>): Parser<T[], Uint8Array> => createUint32LengthPrefixedSliceBoundedParser(createArrayParser(innerParser));
|
|
47
60
|
|
|
48
61
|
type ApkSigningBlockZeroPaddingPair = {
|
|
49
|
-
type: 'zeroPadding'
|
|
50
|
-
length: number
|
|
62
|
+
type: 'zeroPadding';
|
|
63
|
+
length: number;
|
|
51
64
|
};
|
|
52
65
|
|
|
53
66
|
const createApkSigningBlockZeroPaddingPairInnerParser = (length: number): Parser<ApkSigningBlockZeroPaddingPair, Uint8Array> => promiseCompose(
|
|
@@ -59,15 +72,15 @@ const createApkSigningBlockZeroPaddingPairInnerParser = (length: number): Parser
|
|
|
59
72
|
);
|
|
60
73
|
|
|
61
74
|
type ApkSigningBlockSignatureV2Pair = {
|
|
62
|
-
type: 'signatureV2'
|
|
63
|
-
signers:
|
|
75
|
+
type: 'signatureV2';
|
|
76
|
+
signers: ApkSignatureV2Signer[];
|
|
64
77
|
};
|
|
65
78
|
|
|
66
79
|
const createApkSigningBlockSignatureV2PairInnerParser = (length: number): Parser<ApkSigningBlockSignatureV2Pair, Uint8Array> => {
|
|
67
80
|
const apkSigningBlockSignatureV2PairInnerParser = promiseCompose(
|
|
68
81
|
createTupleParser([
|
|
69
82
|
createExactSequenceParser<Uint8Array>(Buffer.from('1a870971', 'hex')),
|
|
70
|
-
|
|
83
|
+
apkSignatureV2SignersParser,
|
|
71
84
|
]),
|
|
72
85
|
([ _magic, signers = [] ]) => ({ type: 'signatureV2' as const, signers }),
|
|
73
86
|
);
|
|
@@ -76,8 +89,8 @@ const createApkSigningBlockSignatureV2PairInnerParser = (length: number): Parser
|
|
|
76
89
|
};
|
|
77
90
|
|
|
78
91
|
type ApkSigningBlockGenericPair = {
|
|
79
|
-
type: 'generic'
|
|
80
|
-
pair: ApkSigningBlockPair
|
|
92
|
+
type: 'generic';
|
|
93
|
+
pair: ApkSigningBlockPair;
|
|
81
94
|
};
|
|
82
95
|
|
|
83
96
|
const createApkSigningBlockGenericPairInnerParser = (length: number): Parser<ApkSigningBlockGenericPair, Uint8Array> => promiseCompose(
|
|
@@ -108,14 +121,14 @@ setParserName(apkSigningBlockPairParser, 'apkSigningBlockPairParser');
|
|
|
108
121
|
|
|
109
122
|
const apkSigningBlockPairsParser: Parser<ApkSigningBlockPairType[], Uint8Array> = createArrayParser(apkSigningBlockPairParser);
|
|
110
123
|
|
|
111
|
-
const apkSigningBlockParser: Parser<ApkSigningBlock, Uint8Array> = createUint64LengthPrefixedParser(
|
|
124
|
+
export const apkSigningBlockParser: Parser<ApkSigningBlock, Uint8Array> = createUint64LengthPrefixedParser(
|
|
112
125
|
sizeOfBlock => promiseCompose(
|
|
113
126
|
createTupleParser([
|
|
114
127
|
apkSigningBlockPairsParser,
|
|
115
128
|
uint64LEParser,
|
|
116
129
|
createExactSequenceParser<Uint8Array>(Buffer.from('APK Sig Block 42', 'utf8')),
|
|
117
130
|
]),
|
|
118
|
-
async ([ pairs, sizeOfBlockRepeated, _magic ]) => {
|
|
131
|
+
async ([ pairs, sizeOfBlockRepeated, _magic ]): Promise<ApkSigningBlock> => {
|
|
119
132
|
invariant(sizeOfBlock === sizeOfBlockRepeated, 'Size of block mismatch: %s !== %s.', sizeOfBlock, sizeOfBlockRepeated);
|
|
120
133
|
|
|
121
134
|
const zeroPaddingPair = pairs.find(pair => pair.type === 'zeroPadding');
|
|
@@ -135,19 +148,16 @@ const apkSigningBlockParser: Parser<ApkSigningBlock, Uint8Array> = createUint64L
|
|
|
135
148
|
|
|
136
149
|
return {
|
|
137
150
|
zeroPaddingLength: zeroPaddingPair?.length,
|
|
138
|
-
signatureV2: signatureV2Pair
|
|
151
|
+
signatureV2: signatureV2Pair ? {
|
|
152
|
+
signers: signatureV2Pair?.signers,
|
|
153
|
+
} : undefined,
|
|
139
154
|
pairs: genericPairs,
|
|
140
155
|
};
|
|
141
156
|
},
|
|
142
157
|
),
|
|
143
158
|
);
|
|
144
159
|
|
|
145
|
-
|
|
146
|
-
signatureAlgorithmId: number,
|
|
147
|
-
digest: Uint8Array,
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const apkSignatureSchemeV2DigestParser = createUint32LengthPrefixedParser<ApkSignatureSchemeV2Digest>(
|
|
160
|
+
const apkSignatureV2DigestParser = createUint32LengthPrefixedParser<ApkSignatureV2Digest>(
|
|
151
161
|
pairLength => promiseCompose(
|
|
152
162
|
createTupleParser([
|
|
153
163
|
uint32LEParser,
|
|
@@ -159,24 +169,19 @@ const apkSignatureSchemeV2DigestParser = createUint32LengthPrefixedParser<ApkSig
|
|
|
159
169
|
),
|
|
160
170
|
);
|
|
161
171
|
|
|
162
|
-
const
|
|
163
|
-
|
|
172
|
+
const apkSignatureV2DigestsParser = createUint32LengthPrefixedSliceBoundedArrayParser(
|
|
173
|
+
apkSignatureV2DigestParser,
|
|
164
174
|
);
|
|
165
175
|
|
|
166
|
-
const
|
|
176
|
+
const apkSignatureV2CertificateParser = createUint32LengthPrefixedParser(
|
|
167
177
|
certificateLength => createFixedLengthSequenceParser(certificateLength),
|
|
168
178
|
);
|
|
169
179
|
|
|
170
|
-
const
|
|
171
|
-
|
|
180
|
+
const apkSignatureV2CertificatesParser = createUint32LengthPrefixedSliceBoundedArrayParser(
|
|
181
|
+
apkSignatureV2CertificateParser,
|
|
172
182
|
);
|
|
173
183
|
|
|
174
|
-
|
|
175
|
-
id: number,
|
|
176
|
-
value: Uint8Array,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const apkSignatureSchemeV2AdditionalAttributeParser = createUint32LengthPrefixedParser<ApkSignatureSchemeV2AdditionalAttribute>(
|
|
184
|
+
const apkSignatureV2AdditionalAttributeParser = createUint32LengthPrefixedParser<ApkSignatureV2AdditionalAttribute>(
|
|
180
185
|
pairLength => promiseCompose(
|
|
181
186
|
createTupleParser([
|
|
182
187
|
uint32LEParser,
|
|
@@ -186,44 +191,39 @@ const apkSignatureSchemeV2AdditionalAttributeParser = createUint32LengthPrefixed
|
|
|
186
191
|
),
|
|
187
192
|
);
|
|
188
193
|
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
setParserName(apkSignatureV2AdditionalAttributeParser, 'apkSignatureV2AdditionalAttributeParser');
|
|
195
|
+
|
|
196
|
+
const apkSignatureV2AdditionalAttributesParser = createUint32LengthPrefixedSliceBoundedArrayParser(
|
|
197
|
+
apkSignatureV2AdditionalAttributeParser,
|
|
191
198
|
);
|
|
192
199
|
|
|
193
|
-
|
|
194
|
-
digests: ApkSignatureSchemeV2Digest[],
|
|
195
|
-
certificates: Uint8Array[],
|
|
196
|
-
additionalAttributes: ApkSignatureSchemeV2AdditionalAttribute[],
|
|
197
|
-
};
|
|
200
|
+
setParserName(apkSignatureV2AdditionalAttributesParser, 'apkSignatureV2AdditionalAttributesParser');
|
|
198
201
|
|
|
199
|
-
const
|
|
202
|
+
const apkSignatureV2SignedDataParser = createUint32LengthPrefixedSliceBoundedParser(
|
|
200
203
|
promiseCompose(
|
|
201
204
|
createTupleParser([
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
apkSignatureV2DigestsParser,
|
|
206
|
+
apkSignatureV2CertificatesParser,
|
|
207
|
+
apkSignatureV2AdditionalAttributesParser,
|
|
205
208
|
createArrayParser(createExactElementParser(0)),
|
|
206
209
|
]),
|
|
207
210
|
([
|
|
208
|
-
digests = [],
|
|
209
|
-
certificates = [],
|
|
210
|
-
additionalAttributes = [],
|
|
211
|
-
]): ApkSignatureSchemeV2SignedData => ({
|
|
212
211
|
digests,
|
|
213
212
|
certificates,
|
|
214
213
|
additionalAttributes,
|
|
214
|
+
zeroPadding,
|
|
215
|
+
]): ApkSignatureV2SignedData => ({
|
|
216
|
+
digests,
|
|
217
|
+
certificates,
|
|
218
|
+
additionalAttributes,
|
|
219
|
+
zeroPaddingLength: zeroPadding.length,
|
|
215
220
|
}),
|
|
216
221
|
),
|
|
217
222
|
);
|
|
218
223
|
|
|
219
|
-
setParserName(
|
|
224
|
+
setParserName(apkSignatureV2SignedDataParser, 'apkSignatureV2SignedDataParser');
|
|
220
225
|
|
|
221
|
-
|
|
222
|
-
signatureAlgorithmId: number,
|
|
223
|
-
signature: Uint8Array,
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const apkSignatureSchemeV2SignatureParser = createUint32LengthPrefixedParser(
|
|
226
|
+
const apkSignatureV2SignatureParser = createUint32LengthPrefixedParser(
|
|
227
227
|
signatureLength => promiseCompose(
|
|
228
228
|
createTupleParser([
|
|
229
229
|
uint32LEParser,
|
|
@@ -234,43 +234,37 @@ const apkSignatureSchemeV2SignatureParser = createUint32LengthPrefixedParser(
|
|
|
234
234
|
([
|
|
235
235
|
signatureAlgorithmId,
|
|
236
236
|
signature,
|
|
237
|
-
]):
|
|
237
|
+
]): ApkSignatureV2Signature => ({
|
|
238
238
|
signatureAlgorithmId,
|
|
239
239
|
signature,
|
|
240
240
|
}),
|
|
241
241
|
),
|
|
242
242
|
);
|
|
243
243
|
|
|
244
|
-
const
|
|
245
|
-
|
|
244
|
+
const apkSignatureV2SignaturesParser = createUint32LengthPrefixedSliceBoundedArrayParser(
|
|
245
|
+
apkSignatureV2SignatureParser,
|
|
246
246
|
);
|
|
247
247
|
|
|
248
|
-
setParserName(
|
|
248
|
+
setParserName(apkSignatureV2SignaturesParser, 'apkSignatureV2SignaturesParser');
|
|
249
249
|
|
|
250
|
-
const
|
|
250
|
+
const apkSignatureV2PublicKeyParser = createUint32LengthPrefixedParser(
|
|
251
251
|
publicKeyLength => createFixedLengthSequenceParser(publicKeyLength),
|
|
252
252
|
);
|
|
253
253
|
|
|
254
|
-
setParserName(
|
|
254
|
+
setParserName(apkSignatureV2PublicKeyParser, 'apkSignatureV2PublicKeyParser');
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
signedData: ApkSignatureSchemeV2SignedData,
|
|
258
|
-
signatures: ApkSignatureSchemeV2Signature[],
|
|
259
|
-
publicKey?: Uint8Array,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const apkSignatureSchemeV2SignerParser = createUint32LengthPrefixedSliceBoundedParser(
|
|
256
|
+
const apkSignatureV2SignerParser = createUint32LengthPrefixedSliceBoundedParser(
|
|
263
257
|
promiseCompose(
|
|
264
258
|
createTupleParser([
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
259
|
+
apkSignatureV2SignedDataParser,
|
|
260
|
+
apkSignatureV2SignaturesParser,
|
|
261
|
+
apkSignatureV2PublicKeyParser,
|
|
268
262
|
]),
|
|
269
263
|
([
|
|
270
264
|
signedData,
|
|
271
265
|
signatures = [],
|
|
272
|
-
publicKey
|
|
273
|
-
]):
|
|
266
|
+
publicKey,
|
|
267
|
+
]): ApkSignatureV2Signer => ({
|
|
274
268
|
signedData,
|
|
275
269
|
signatures,
|
|
276
270
|
publicKey,
|
|
@@ -278,13 +272,13 @@ const apkSignatureSchemeV2SignerParser = createUint32LengthPrefixedSliceBoundedP
|
|
|
278
272
|
),
|
|
279
273
|
);
|
|
280
274
|
|
|
281
|
-
setParserName(
|
|
275
|
+
setParserName(apkSignatureV2SignerParser, 'apkSignatureV2SignerParser');
|
|
282
276
|
|
|
283
|
-
const
|
|
284
|
-
|
|
277
|
+
const apkSignatureV2SignersParser = createUint32LengthPrefixedSliceBoundedArrayParser(
|
|
278
|
+
apkSignatureV2SignerParser,
|
|
285
279
|
);
|
|
286
280
|
|
|
287
|
-
setParserName(
|
|
281
|
+
setParserName(apkSignatureV2SignersParser, 'apkSignatureV2SignersParser');
|
|
288
282
|
|
|
289
283
|
const apkParser_ = createTupleParser([
|
|
290
284
|
createArrayParser(zipLocalFileParser),
|
|
@@ -325,3 +319,74 @@ export const apkParser: Parser<Apk, Uint8Array> = promiseCompose(
|
|
|
325
319
|
);
|
|
326
320
|
|
|
327
321
|
setParserName(apkParser, 'apkParser');
|
|
322
|
+
|
|
323
|
+
export type ApkSignableSections = {
|
|
324
|
+
zipLocalFiles: ZipLocalFile[];
|
|
325
|
+
apkSigningBlock?: ApkSigningBlock;
|
|
326
|
+
zipCentralDirectory: ZipCentralDirectoryHeader[];
|
|
327
|
+
zipEndOfCentralDirectory: ZipEndOfCentralDirectoryRecord;
|
|
328
|
+
|
|
329
|
+
zipLocalFilesUint8Array: Uint8Array;
|
|
330
|
+
apkSigningBlockUint8Array?: Uint8Array;
|
|
331
|
+
zipCentralDirectoryUint8Array: Uint8Array;
|
|
332
|
+
zipEndOfCentralDirectoryUint8Array: Uint8Array;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export const apkSignableSectionsParser: Parser<ApkSignableSections, Uint8Array> = promiseCompose(
|
|
336
|
+
createTupleParser([
|
|
337
|
+
createParserConsumedSequenceParser(
|
|
338
|
+
createTupleParser([
|
|
339
|
+
createArrayParser(zipLocalFileParser),
|
|
340
|
+
createOptionalParser(zipArchiveDecryptionHeaderParser),
|
|
341
|
+
createOptionalParser(zipArchiveExtraDataRecordParser),
|
|
342
|
+
]),
|
|
343
|
+
),
|
|
344
|
+
createOptionalParser(
|
|
345
|
+
createParserConsumedSequenceParser(
|
|
346
|
+
apkSigningBlockParser
|
|
347
|
+
),
|
|
348
|
+
),
|
|
349
|
+
createParserConsumedSequenceParser(
|
|
350
|
+
createTupleParser([
|
|
351
|
+
createArrayParser(zipCentralDirectoryHeaderParser),
|
|
352
|
+
createOptionalParser(zip64EndOfCentralDirectoryRecordParser),
|
|
353
|
+
createOptionalParser(zip64EndOfCentralDirectoryLocatorParser),
|
|
354
|
+
]),
|
|
355
|
+
),
|
|
356
|
+
createParserConsumedSequenceParser(
|
|
357
|
+
zipEndOfCentralDirectoryRecordParser,
|
|
358
|
+
),
|
|
359
|
+
]),
|
|
360
|
+
async ([
|
|
361
|
+
[
|
|
362
|
+
[
|
|
363
|
+
zipLocalFiles,
|
|
364
|
+
],
|
|
365
|
+
zipLocalFilesUint8Array,
|
|
366
|
+
],
|
|
367
|
+
[
|
|
368
|
+
apkSigningBlock = undefined,
|
|
369
|
+
apkSigningBlockUint8Array = undefined,
|
|
370
|
+
] = [],
|
|
371
|
+
[
|
|
372
|
+
[
|
|
373
|
+
zipCentralDirectory,
|
|
374
|
+
],
|
|
375
|
+
zipCentralDirectoryUint8Array,
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
zipEndOfCentralDirectory,
|
|
379
|
+
zipEndOfCentralDirectoryUint8Array,
|
|
380
|
+
],
|
|
381
|
+
]) => ({
|
|
382
|
+
zipLocalFiles,
|
|
383
|
+
apkSigningBlock,
|
|
384
|
+
zipCentralDirectory,
|
|
385
|
+
zipEndOfCentralDirectory,
|
|
386
|
+
|
|
387
|
+
zipLocalFilesUint8Array,
|
|
388
|
+
apkSigningBlockUint8Array,
|
|
389
|
+
zipCentralDirectoryUint8Array,
|
|
390
|
+
zipEndOfCentralDirectoryUint8Array,
|
|
391
|
+
}),
|
|
392
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
3
|
+
import { runParser } from './parser.js';
|
|
4
|
+
import { apkParser, apkSigningBlockParser } from './apkParser.js';
|
|
5
|
+
import { runUnparser } from './unparser.js';
|
|
6
|
+
import { apkSigningBlockUnparser } from './apkUnparser.js';
|
|
7
|
+
import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
|
|
8
|
+
import invariant from 'invariant';
|
|
9
|
+
|
|
10
|
+
for (const apkCid of [
|
|
11
|
+
'bafkreicckcmzrdxwoc3w2in3tivpyxrdtcfpct4zwauq3igew3nkpvfapu',
|
|
12
|
+
]) {
|
|
13
|
+
test(
|
|
14
|
+
'apk ' + apkCid,
|
|
15
|
+
async t => {
|
|
16
|
+
const apkResponse = await fetch('https://ipfs.io/ipfs/' + apkCid);
|
|
17
|
+
|
|
18
|
+
const apkStream = apkResponse.body!;
|
|
19
|
+
|
|
20
|
+
const apk = await runParser(apkParser, apkStream, uint8ArrayParserInputCompanion, {
|
|
21
|
+
errorJoinMode: 'all',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const apkSigningBlock = apk.signingBlock;
|
|
25
|
+
|
|
26
|
+
invariant(apkSigningBlock, 'APK has no signing block');
|
|
27
|
+
|
|
28
|
+
const apkSigningBlockStream = runUnparser(apkSigningBlockUnparser, apkSigningBlock, uint8ArrayUnparserOutputCompanion);
|
|
29
|
+
|
|
30
|
+
const actual = await runParser(apkSigningBlockParser, apkSigningBlockStream, uint8ArrayParserInputCompanion, {
|
|
31
|
+
errorJoinMode: 'all',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
t.deepEqual(actual, apkSigningBlock);
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ApkSignatureV2AdditionalAttribute, ApkSignatureV2Digest, ApkSignatureV2Signature, ApkSignatureV2SignedData, ApkSignatureV2Signer, ApkSigningBlock } from "./apk.js";
|
|
2
|
+
import { createArrayUnparser } from "./arrayUnparser.js";
|
|
3
|
+
import { createSequenceUnparser } from "./sequenceUnparser.js";
|
|
4
|
+
import { Unparser } from "./unparser.js";
|
|
5
|
+
|
|
6
|
+
const uint8ArrayUnparser = createSequenceUnparser<Uint8Array>();
|
|
7
|
+
|
|
8
|
+
const uint32LEUnparser: Unparser<number, Uint8Array> = async function * (input) {
|
|
9
|
+
const buffer = Buffer.alloc(4);
|
|
10
|
+
buffer.writeUInt32LE(input);
|
|
11
|
+
yield buffer;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const uint64LEUnparser: Unparser<number | bigint, Uint8Array> = async function * (input) {
|
|
15
|
+
const buffer = Buffer.alloc(8);
|
|
16
|
+
buffer.writeBigUInt64LE(BigInt(input));
|
|
17
|
+
yield buffer;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const createUint32LengthPrefixedUnparser = <T>(innerUnparser: Unparser<T, Uint8Array>): Unparser<T, Uint8Array> => async function * (input, unparserContext) {
|
|
21
|
+
const length = yield * unparserContext.writeLater(4);
|
|
22
|
+
yield * innerUnparser(input, unparserContext);
|
|
23
|
+
yield * unparserContext.writeEarlier(length, uint32LEUnparser, unparserContext.position - length.positionEnd);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const createUint64LengthPrefixedUnparser = <T>(innerUnparser: Unparser<T, Uint8Array>): Unparser<T, Uint8Array> => async function * (input, unparserContext) {
|
|
27
|
+
const length = yield * unparserContext.writeLater(8);
|
|
28
|
+
yield * innerUnparser(input, unparserContext);
|
|
29
|
+
yield * unparserContext.writeEarlier(length, uint64LEUnparser, unparserContext.position - length.positionEnd);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const apkSignatureV2DigestUnparser: Unparser<ApkSignatureV2Digest, Uint8Array> = createUint32LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
33
|
+
yield * uint32LEUnparser(input.signatureAlgorithmId, unparserContext);
|
|
34
|
+
yield * uint32LEUnparser(input.digest.length, unparserContext);
|
|
35
|
+
yield input.digest;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const apkSignatureV2DigestsUnparser = createUint32LengthPrefixedUnparser(createArrayUnparser(apkSignatureV2DigestUnparser));
|
|
39
|
+
|
|
40
|
+
const apkSignatureV2CertificateUnparser = createUint32LengthPrefixedUnparser(uint8ArrayUnparser);
|
|
41
|
+
|
|
42
|
+
const apkSignatureV2CertificatesUnparser = createUint32LengthPrefixedUnparser(createArrayUnparser(apkSignatureV2CertificateUnparser));
|
|
43
|
+
|
|
44
|
+
const apkSignatureV2AdditionalAttributeUnparser: Unparser<ApkSignatureV2AdditionalAttribute, Uint8Array> = createUint32LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
45
|
+
yield * uint32LEUnparser(input.id, unparserContext);
|
|
46
|
+
yield input.value;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const apkSignatureV2AdditionalAttributesUnparser = createUint32LengthPrefixedUnparser(createArrayUnparser(apkSignatureV2AdditionalAttributeUnparser));
|
|
50
|
+
|
|
51
|
+
export const apkSignatureV2SignedDataUnparser: Unparser<ApkSignatureV2SignedData, Uint8Array> = createUint32LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
52
|
+
yield * apkSignatureV2DigestsUnparser(input.digests, unparserContext);
|
|
53
|
+
yield * apkSignatureV2CertificatesUnparser(input.certificates, unparserContext);
|
|
54
|
+
yield * apkSignatureV2AdditionalAttributesUnparser(input.additionalAttributes, unparserContext);
|
|
55
|
+
if (input.zeroPaddingLength) {
|
|
56
|
+
yield Buffer.alloc(input.zeroPaddingLength);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const apkSignatureV2SignatureUnparser: Unparser<ApkSignatureV2Signature, Uint8Array> = createUint32LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
61
|
+
yield * uint32LEUnparser(input.signatureAlgorithmId, unparserContext);
|
|
62
|
+
yield * uint32LEUnparser(input.signature.length, unparserContext);
|
|
63
|
+
yield input.signature;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const apkSignatureV2SignaturesUnparser = createUint32LengthPrefixedUnparser(createArrayUnparser(apkSignatureV2SignatureUnparser));
|
|
67
|
+
|
|
68
|
+
const apkSignatureV2PublicKeyUnparser = createUint32LengthPrefixedUnparser(uint8ArrayUnparser);
|
|
69
|
+
|
|
70
|
+
const apkSignatureV2SignerUnparser: Unparser<ApkSignatureV2Signer, Uint8Array> = createUint32LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
71
|
+
yield * apkSignatureV2SignedDataUnparser(input.signedData, unparserContext);
|
|
72
|
+
yield * apkSignatureV2SignaturesUnparser(input.signatures, unparserContext);
|
|
73
|
+
yield * apkSignatureV2PublicKeyUnparser(input.publicKey, unparserContext);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const apkSignatureV2SignersUnparser = createUint32LengthPrefixedUnparser(createArrayUnparser(apkSignatureV2SignerUnparser));
|
|
77
|
+
|
|
78
|
+
type ApkSigningBlockPair = {
|
|
79
|
+
id: number;
|
|
80
|
+
value: Uint8Array | ApkSignatureV2Signer[];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const apkSigningBlockPairUnparser: Unparser<ApkSigningBlockPair, Uint8Array> = createUint64LengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
84
|
+
yield * uint32LEUnparser(input.id, unparserContext);
|
|
85
|
+
if (input.value instanceof Uint8Array) {
|
|
86
|
+
yield input.value;
|
|
87
|
+
} else {
|
|
88
|
+
yield * apkSignatureV2SignersUnparser(input.value, unparserContext);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const apkSigningBlockPairsUnparser = createArrayUnparser(apkSigningBlockPairUnparser);
|
|
93
|
+
|
|
94
|
+
export const apkSigningBlockUnparser: Unparser<ApkSigningBlock, Uint8Array> = async function * (
|
|
95
|
+
input,
|
|
96
|
+
unparserContext,
|
|
97
|
+
) {
|
|
98
|
+
const pairs = [
|
|
99
|
+
...input.pairs,
|
|
100
|
+
...(input.signatureV2 ? [
|
|
101
|
+
{
|
|
102
|
+
id: 0x7109871a,
|
|
103
|
+
value: input.signatureV2!.signers,
|
|
104
|
+
},
|
|
105
|
+
] : []),
|
|
106
|
+
...(input.zeroPaddingLength ? [
|
|
107
|
+
{
|
|
108
|
+
id: 0x42726577,
|
|
109
|
+
value: Buffer.alloc(input.zeroPaddingLength),
|
|
110
|
+
},
|
|
111
|
+
] : []),
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const sizeOfBlockWriteLater = yield * unparserContext.writeLater(8);
|
|
115
|
+
yield * apkSigningBlockPairsUnparser(pairs, unparserContext);
|
|
116
|
+
const sizeOfBlock = unparserContext.position - sizeOfBlockWriteLater.position + 16;
|
|
117
|
+
yield * uint64LEUnparser(sizeOfBlock, unparserContext);
|
|
118
|
+
yield * unparserContext.writeEarlier(sizeOfBlockWriteLater, uint64LEUnparser, sizeOfBlock);
|
|
119
|
+
yield Buffer.from('APK Sig Block 42', 'utf8');
|
|
120
|
+
};
|
|
@@ -14,10 +14,8 @@ type FileSystemFile = {
|
|
|
14
14
|
|
|
15
15
|
export type FileSystemEntry =
|
|
16
16
|
| FileSystemFile
|
|
17
|
-
| FileSystemDirectory
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export const arbitraryFileSystemEntry = fc.letrec((tie) => ({
|
|
17
|
+
| FileSystemDirectory;
|
|
18
|
+
export const arbitraryFileSystemEntry = fc.letrec(tie => ({
|
|
21
19
|
entry: fc.oneof<[
|
|
22
20
|
fc.Arbitrary<FileSystemFile>,
|
|
23
21
|
fc.Arbitrary<FileSystemDirectory>,
|
package/src/arbitraryZip.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as fc from 'fast-check';
|
|
2
|
-
import { Zip, ZipDirectoryEntry, ZipEntry, ZipFileEntry } from './zip.js';
|
|
3
|
-
import { arbitraryDosDateTime } from './arbitraryDosDateTime.js';
|
|
4
|
-
import { createArbitraryZipPermissions } from './arbitraryZipPermissions.js';
|
|
5
2
|
import invariant from 'invariant';
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import {
|
|
4
|
+
type Zip,
|
|
5
|
+
type ZipDirectoryEntry,
|
|
6
|
+
type ZipFileEntry,
|
|
7
|
+
type ZipEntry,
|
|
8
|
+
type ZipEntryAttributes,
|
|
9
|
+
} from './zip.js';
|
|
10
|
+
import { arbitraryDosDateTime } from './arbitraryDosDateTime.js';
|
|
8
11
|
|
|
9
12
|
const arbitraryPath = fc.string({ minLength: 1 }).filter(path => {
|
|
10
13
|
const pathSegments = path.split('/');
|
|
@@ -26,7 +29,10 @@ const createArbitraryZipEntry = (platform: 'unix' | 'dos') => fc.oneof<[
|
|
|
26
29
|
path: arbitraryPath,
|
|
27
30
|
date: arbitraryDosDateTime,
|
|
28
31
|
comment: fc.string(),
|
|
29
|
-
|
|
32
|
+
hostSystem: fc.constant(platform),
|
|
33
|
+
attributes: fc.record<ZipEntryAttributes>({
|
|
34
|
+
directory: fc.constant(false),
|
|
35
|
+
}),
|
|
30
36
|
compression: fc.oneof(
|
|
31
37
|
fc.constant('store' as const),
|
|
32
38
|
fc.constant('deflate' as const),
|
|
@@ -40,13 +46,6 @@ const createArbitraryZipEntry = (platform: 'unix' | 'dos') => fc.oneof<[
|
|
|
40
46
|
return false;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
if (
|
|
44
|
-
zipFileEntry.permissions.type === 'dos'
|
|
45
|
-
&& zipFileEntry.permissions.dosPermissions & DOS_DIRECTORY_FLAG
|
|
46
|
-
) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
49
|
return true;
|
|
51
50
|
}),
|
|
52
51
|
fc.record<ZipDirectoryEntry>({
|
|
@@ -54,16 +53,10 @@ const createArbitraryZipEntry = (platform: 'unix' | 'dos') => fc.oneof<[
|
|
|
54
53
|
path: arbitraryPath,
|
|
55
54
|
date: arbitraryDosDateTime,
|
|
56
55
|
comment: fc.string(),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
&& !(zipDirectoryEntry.permissions.dosPermissions & DOS_DIRECTORY_FLAG)
|
|
62
|
-
) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return true;
|
|
56
|
+
hostSystem: fc.constant(platform),
|
|
57
|
+
attributes: fc.record<ZipEntryAttributes>({
|
|
58
|
+
directory: fc.constant(false),
|
|
59
|
+
}),
|
|
67
60
|
}),
|
|
68
61
|
);
|
|
69
62
|
|
|
@@ -106,10 +99,10 @@ const createArbitraryZipEntries = (platform: 'unix' | 'dos') => (
|
|
|
106
99
|
path: directoryPath,
|
|
107
100
|
date: entry.date,
|
|
108
101
|
comment: '',
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
102
|
+
hostSystem: platform,
|
|
103
|
+
attributes: {
|
|
104
|
+
directory: true,
|
|
105
|
+
},
|
|
113
106
|
});
|
|
114
107
|
|
|
115
108
|
seenDirectoryPaths.add(directoryPath);
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as fc from 'fast-check';
|
|
2
|
-
import { ZipDosPermissions, ZipUnixPermissions } from './zip.js';
|
|
3
|
-
|
|
4
|
-
const arbitraryZipUnixPermissions = fc.record<ZipUnixPermissions>({
|
|
5
|
-
type: fc.constant('unix'),
|
|
6
|
-
unixPermissions: (
|
|
7
|
-
fc.nat({ max: 0b0000000111111111 })
|
|
8
|
-
.filter((unixPermissions) => unixPermissions > 0)
|
|
9
|
-
),
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const arbitraryZipDosPermissions = fc.record<ZipDosPermissions>({
|
|
13
|
-
type: fc.constant('dos'),
|
|
14
|
-
dosPermissions: (
|
|
15
|
-
fc.nat({ max: 0b00111111 })
|
|
16
|
-
),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
export const createArbitraryZipPermissions = (type: 'unix' | 'dos') => {
|
|
20
|
-
if (type === 'unix') {
|
|
21
|
-
return arbitraryZipUnixPermissions;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return arbitraryZipDosPermissions;
|
|
25
|
-
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import JSZip from 'jszip';
|
|
2
|
-
import { ZipEntry } from './zip.js';
|
|
2
|
+
import { type ZipEntry } from './zip.js';
|
|
3
3
|
import { arbitraryZip } from './arbitraryZip.js';
|
|
4
4
|
|
|
5
5
|
function addZipEntryToZip(zip: JSZip, zipEntry: ZipEntry) {
|
|
6
6
|
const options = {
|
|
7
7
|
comment: zipEntry.comment,
|
|
8
8
|
date: zipEntry.date,
|
|
9
|
-
unixPermissions: zipEntry.permissions.type === 'unix' ? zipEntry.permissions.unixPermissions : undefined,
|
|
10
|
-
dosPermissions: zipEntry.permissions.type === 'dos' ? zipEntry.permissions.dosPermissions : undefined,
|
|
9
|
+
// unixPermissions: zipEntry.permissions.type === 'unix' ? zipEntry.permissions.unixPermissions : undefined,
|
|
10
|
+
// dosPermissions: zipEntry.permissions.type === 'dos' ? zipEntry.permissions.dosPermissions : undefined,
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
if (zipEntry.type === 'file') {
|
|
@@ -35,7 +35,7 @@ export const arbitraryZipStream = arbitraryZip.map(zip => {
|
|
|
35
35
|
const zipInternalStream = jsZip.generateInternalStream({
|
|
36
36
|
type: 'uint8array',
|
|
37
37
|
comment: zip.comment,
|
|
38
|
-
platform: zip.entries.at(0)?.
|
|
38
|
+
platform: zip.entries.at(0)?.hostSystem.toUpperCase() as any,
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
const zipStream = new ReadableStream<Uint8Array>({
|