stegdoc 3.0.2 → 5.0.0

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.
@@ -1,113 +1,118 @@
1
- const path = require('path');
2
- const chalk = require('chalk');
3
- const ora = require('ora');
4
- const { readDocxBase64 } = require('../lib/docx-handler');
5
- const { readXlsxBase64 } = require('../lib/xlsx-handler');
6
- const { validateMetadata, isMultiPart } = require('../lib/metadata');
7
- const { detectFormat, formatBytes } = require('../lib/utils');
8
- const { extractContent, findMultiPartFiles } = require('../lib/file-utils');
9
-
10
- /**
11
- * Show information about an encoded file without decoding
12
- * @param {string} inputFile - Path to input file
13
- * @param {object} options - Command options
14
- */
15
- async function infoCommand(inputFile, options) {
16
- const spinner = ora('Reading file metadata...').start();
17
-
18
- try {
19
- // Detect format from extension
20
- const format = detectFormat(inputFile);
21
- if (!format) {
22
- throw new Error('Unknown file format. Supported formats: .xlsx, .docx');
23
- }
24
-
25
- // Read file
26
- let readResult;
27
- if (format === 'xlsx') {
28
- readResult = await readXlsxBase64(inputFile);
29
- } else {
30
- readResult = await readDocxBase64(inputFile);
31
- }
32
-
33
- const { encryptionMeta, metadata } = extractContent(readResult, format);
34
-
35
- // Validate metadata
36
- validateMetadata(metadata);
37
-
38
- const isEncrypted = metadata.encrypted || (encryptionMeta && encryptionMeta.length > 0);
39
- const isCompressed = metadata.compressed || false;
40
-
41
- spinner.succeed('File metadata read successfully');
42
- console.log();
43
-
44
- // Display info
45
- console.log(chalk.bold.white('File Information:'));
46
- console.log(chalk.cyan(` Format: ${format.toUpperCase()}`));
47
- console.log(chalk.cyan(` Tool version: ${metadata.version || '1.x'}`));
48
- console.log();
49
-
50
- console.log(chalk.bold.white('Original File:'));
51
- console.log(chalk.cyan(` Filename: ${metadata.originalFilename}`));
52
- console.log(chalk.cyan(` Extension: ${metadata.originalExtension}`));
53
- console.log(chalk.cyan(` Size: ${formatBytes(metadata.originalSize)}`));
54
- console.log();
55
-
56
- console.log(chalk.bold.white('Encoding Options:'));
57
- console.log(chalk.cyan(` Encrypted: ${isEncrypted ? chalk.yellow('Yes') : 'No'}`));
58
- console.log(chalk.cyan(` Compressed: ${isCompressed ? chalk.green('Yes') : 'No'}`));
59
- console.log(chalk.cyan(` Encoded on: ${metadata.encodingDate || 'Unknown'}`));
60
-
61
- if (metadata.contentHash) {
62
- console.log(chalk.cyan(` Content hash: ${metadata.contentHash.slice(0, 16)}...`));
63
- }
64
- console.log();
65
-
66
- // Multi-part info
67
- if (isMultiPart(metadata)) {
68
- console.log(chalk.bold.white('Multi-part File:'));
69
- console.log(chalk.cyan(` This is part: ${metadata.partNumber} of ${metadata.totalParts}`));
70
- console.log(chalk.cyan(` Hash: ${metadata.hash}`));
71
-
72
- // Find other parts
73
- const inputDir = path.dirname(inputFile);
74
- const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
75
-
76
- console.log();
77
- console.log(chalk.bold.white('Parts found in directory:'));
78
-
79
- for (let i = 1; i <= metadata.totalParts; i++) {
80
- const part = allParts.find(p => p.partNumber === i);
81
- if (part) {
82
- console.log(chalk.green(` ✓ Part ${i}: ${part.filename}`));
83
- } else {
84
- console.log(chalk.red(` ✗ Part ${i}: MISSING`));
85
- }
86
- }
87
-
88
- if (allParts.length === metadata.totalParts) {
89
- console.log();
90
- console.log(chalk.green.bold('All parts found - ready to decode'));
91
- } else {
92
- console.log();
93
- console.log(chalk.yellow.bold(`Missing ${metadata.totalParts - allParts.length} part(s)`));
94
- }
95
- } else {
96
- console.log(chalk.bold.white('Single File:'));
97
- console.log(chalk.cyan(` Hash: ${metadata.hash}`));
98
- console.log();
99
- console.log(chalk.green.bold('Ready to decode'));
100
- }
101
-
102
- if (isEncrypted) {
103
- console.log(chalk.yellow('\nNote: Password required for decoding'));
104
- }
105
-
106
- } catch (error) {
107
- spinner.fail('Failed to read file info');
108
- console.error(chalk.red(`Error: ${error.message}`));
109
- process.exit(1);
110
- }
111
- }
112
-
113
- module.exports = infoCommand;
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const ora = require('ora');
4
+ const { readDocxBase64 } = require('../lib/docx-handler');
5
+ const { readXlsxBase64 } = require('../lib/xlsx-handler');
6
+ const { validateMetadata, isMultiPart, isLogEmbedFormat } = require('../lib/metadata');
7
+ const { detectFormat, formatBytes } = require('../lib/utils');
8
+ const { extractContent, findMultiPartFiles } = require('../lib/file-utils');
9
+
10
+ /**
11
+ * Show information about an encoded file without decoding
12
+ */
13
+ async function infoCommand(inputFile, options) {
14
+ const spinner = ora('Reading file metadata...').start();
15
+
16
+ try {
17
+ const format = detectFormat(inputFile);
18
+ if (!format) {
19
+ throw new Error('Unknown file format. Supported formats: .xlsx, .docx');
20
+ }
21
+
22
+ let readResult;
23
+ if (format === 'xlsx') {
24
+ readResult = await readXlsxBase64(inputFile);
25
+ } else {
26
+ readResult = await readDocxBase64(inputFile);
27
+ }
28
+
29
+ const extracted = extractContent(readResult, format);
30
+ const metadata = extracted.metadata;
31
+ const encryptionMeta = extracted.encryptionMeta;
32
+
33
+ validateMetadata(metadata);
34
+
35
+ const isEncrypted = metadata.encrypted || (encryptionMeta && encryptionMeta.length > 0);
36
+ const isCompressed = metadata.compressed || false;
37
+ const isV5 = isLogEmbedFormat(metadata);
38
+
39
+ spinner.succeed('File metadata read successfully');
40
+ console.log();
41
+
42
+ console.log(chalk.bold.white('File Information:'));
43
+ console.log(chalk.cyan(` Format: ${format.toUpperCase()}`));
44
+ console.log(chalk.cyan(` Tool version: ${metadata.version || '1.x'}`));
45
+ if (isV5) {
46
+ console.log(chalk.cyan(` Stego method: Log-embed (v5)`));
47
+ console.log(chalk.cyan(` Compression: ${metadata.compressionAlgo || 'brotli'}`));
48
+ }
49
+ console.log();
50
+
51
+ console.log(chalk.bold.white('Original File:'));
52
+ console.log(chalk.cyan(` Filename: ${metadata.originalFilename}`));
53
+ console.log(chalk.cyan(` Extension: ${metadata.originalExtension}`));
54
+ console.log(chalk.cyan(` Size: ${formatBytes(metadata.originalSize)}`));
55
+ console.log();
56
+
57
+ console.log(chalk.bold.white('Encoding Options:'));
58
+ console.log(chalk.cyan(` Encrypted: ${isEncrypted ? chalk.yellow('Yes') : 'No'}`));
59
+ console.log(chalk.cyan(` Compressed: ${isCompressed ? chalk.green('Yes') : 'No'}`));
60
+ console.log(chalk.cyan(` Encoded on: ${metadata.encodingDate || 'Unknown'}`));
61
+
62
+ if (metadata.contentHash) {
63
+ console.log(chalk.cyan(` Content hash: ${metadata.contentHash.slice(0, 16)}...`));
64
+ }
65
+
66
+ if (isV5 && metadata.dataLineCount) {
67
+ console.log(chalk.cyan(` Data lines: ${metadata.dataLineCount}`));
68
+ }
69
+ console.log();
70
+
71
+ const hasMultipleParts = isMultiPart(metadata) || metadata.partNumber !== null;
72
+ if (hasMultipleParts) {
73
+ console.log(chalk.bold.white('Multi-part File:'));
74
+ const inputDir = path.dirname(inputFile);
75
+ const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
76
+ const totalParts = metadata.totalParts || allParts.length;
77
+
78
+ console.log(chalk.cyan(` This is part: ${metadata.partNumber} of ${totalParts}`));
79
+ console.log(chalk.cyan(` Hash: ${metadata.hash}`));
80
+
81
+ console.log();
82
+ console.log(chalk.bold.white('Parts found in directory:'));
83
+
84
+ for (let i = 1; i <= totalParts; i++) {
85
+ const part = allParts.find(p => p.partNumber === i);
86
+ if (part) {
87
+ console.log(chalk.green(` ✓ Part ${i}: ${part.filename}`));
88
+ } else {
89
+ console.log(chalk.red(` ✗ Part ${i}: MISSING`));
90
+ }
91
+ }
92
+
93
+ if (allParts.length >= totalParts) {
94
+ console.log();
95
+ console.log(chalk.green.bold('All parts found - ready to decode'));
96
+ } else {
97
+ console.log();
98
+ console.log(chalk.yellow.bold(`Missing ${totalParts - allParts.length} part(s)`));
99
+ }
100
+ } else {
101
+ console.log(chalk.bold.white('Single File:'));
102
+ console.log(chalk.cyan(` Hash: ${metadata.hash}`));
103
+ console.log();
104
+ console.log(chalk.green.bold('Ready to decode'));
105
+ }
106
+
107
+ if (isEncrypted) {
108
+ console.log(chalk.yellow('\nNote: Password required for decoding'));
109
+ }
110
+
111
+ } catch (error) {
112
+ spinner.fail('Failed to read file info');
113
+ console.error(chalk.red(`Error: ${error.message}`));
114
+ process.exit(1);
115
+ }
116
+ }
117
+
118
+ module.exports = infoCommand;
@@ -1,169 +1,207 @@
1
- const path = require('path');
2
- const chalk = require('chalk');
3
- const ora = require('ora');
4
- const { readDocxBase64 } = require('../lib/docx-handler');
5
- const { readXlsxBase64 } = require('../lib/xlsx-handler');
6
- const { validateMetadata, isMultiPart } = require('../lib/metadata');
7
- const { detectFormat, formatBytes } = require('../lib/utils');
8
- const { decrypt, unpackEncryptionMeta } = require('../lib/crypto');
9
- const { extractContent, findMultiPartFiles } = require('../lib/file-utils');
10
-
11
- /**
12
- * Verify that a file can be decoded without actually writing output
13
- * @param {string} inputFile - Path to input file
14
- * @param {object} options - Command options
15
- * @param {string} options.password - Decryption password (if encrypted)
16
- */
17
- async function verifyCommand(inputFile, options) {
18
- const spinner = ora('Verifying file...').start();
19
- const issues = [];
20
-
21
- try {
22
- // Detect format from extension
23
- const format = detectFormat(inputFile);
24
- if (!format) {
25
- throw new Error('Unknown file format. Supported formats: .xlsx, .docx');
26
- }
27
-
28
- spinner.text = `Reading ${format.toUpperCase()} file...`;
29
-
30
- // Read file
31
- let readResult;
32
- if (format === 'xlsx') {
33
- readResult = await readXlsxBase64(inputFile);
34
- } else {
35
- readResult = await readDocxBase64(inputFile);
36
- }
37
-
38
- const { encryptedContent, encryptionMeta, metadata } = extractContent(readResult, format);
39
-
40
- // Validate metadata
41
- try {
42
- validateMetadata(metadata);
43
- spinner.succeed('Metadata valid');
44
- } catch (e) {
45
- issues.push(`Metadata: ${e.message}`);
46
- spinner.warn('Metadata issues found');
47
- }
48
-
49
- const isEncrypted = metadata.encrypted || (encryptionMeta && encryptionMeta.length > 0);
50
-
51
- // Check multi-part
52
- if (isMultiPart(metadata)) {
53
- spinner.text = 'Checking multi-part files...';
54
-
55
- const inputDir = path.dirname(inputFile);
56
- const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
57
-
58
- if (allParts.length !== metadata.totalParts) {
59
- const missing = [];
60
- const foundParts = new Set(allParts.map(p => p.partNumber));
61
-
62
- for (let i = 1; i <= metadata.totalParts; i++) {
63
- if (!foundParts.has(i)) {
64
- missing.push(i);
65
- }
66
- }
67
-
68
- issues.push(`Missing parts: ${missing.join(', ')}`);
69
- spinner.warn(`Found ${allParts.length}/${metadata.totalParts} parts`);
70
- } else {
71
- spinner.succeed(`All ${metadata.totalParts} parts found`);
72
- }
73
- }
74
-
75
- // Check password for encrypted files
76
- if (isEncrypted) {
77
- if (!options.password) {
78
- issues.push('File is encrypted but no password provided');
79
- spinner.warn('Encryption check skipped (no password)');
80
- } else {
81
- spinner.text = 'Verifying decryption...';
82
-
83
- try {
84
- const { iv, salt, authTag } = unpackEncryptionMeta(encryptionMeta);
85
-
86
- // For multi-part files, we need to merge all parts before decryption
87
- // AES-GCM requires the complete ciphertext to verify the auth tag
88
- let fullContent = encryptedContent;
89
-
90
- if (isMultiPart(metadata)) {
91
- const inputDir = path.dirname(inputFile);
92
- const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
93
-
94
- if (allParts.length === metadata.totalParts) {
95
- // Read and merge all parts
96
- const contentParts = [];
97
- for (const part of allParts) {
98
- let partResult;
99
- if (format === 'xlsx') {
100
- partResult = await readXlsxBase64(part.path);
101
- } else {
102
- partResult = await readDocxBase64(part.path);
103
- }
104
- const { encryptedContent: partContent } = extractContent(partResult, format);
105
- contentParts.push(partContent);
106
- }
107
- fullContent = contentParts.join('');
108
- }
109
- }
110
-
111
- // Decrypt the full content to verify password
112
- decrypt(fullContent, options.password, iv, salt, authTag);
113
- spinner.succeed('Decryption password valid');
114
- } catch (e) {
115
- issues.push('Decryption failed - wrong password or corrupted data');
116
- spinner.fail('Decryption check failed');
117
- }
118
- }
119
- }
120
-
121
- // Summary
122
- console.log();
123
-
124
- if (issues.length === 0) {
125
- console.log(chalk.green.bold(' File verification passed!'));
126
- console.log(chalk.cyan(` Original file: ${metadata.originalFilename}`));
127
- console.log(chalk.cyan(` Size: ${formatBytes(metadata.originalSize)}`));
128
- console.log(chalk.cyan(` Encrypted: ${isEncrypted ? 'Yes' : 'No'}`));
129
- console.log(chalk.cyan(` Compressed: ${metadata.compressed ? 'Yes' : 'No'}`));
130
-
131
- if (isMultiPart(metadata)) {
132
- console.log(chalk.cyan(` Parts: ${metadata.totalParts}`));
133
- }
134
-
135
- console.log();
136
- console.log(chalk.green('File is ready to decode.'));
137
- } else {
138
- console.log(chalk.yellow.bold('Verification completed with issues:'));
139
- console.log();
140
-
141
- for (const issue of issues) {
142
- console.log(chalk.yellow(` • ${issue}`));
143
- }
144
-
145
- console.log();
146
-
147
- // Determine if still decodable
148
- const hasBlockingIssue = issues.some(i =>
149
- i.includes('Missing parts') ||
150
- i.includes('Decryption failed') ||
151
- i.includes('Metadata')
152
- );
153
-
154
- if (hasBlockingIssue) {
155
- console.log(chalk.red('File cannot be decoded until issues are resolved.'));
156
- process.exit(1);
157
- } else {
158
- console.log(chalk.yellow('File may still be decodable. Run decode to attempt.'));
159
- }
160
- }
161
-
162
- } catch (error) {
163
- spinner.fail('Verification failed');
164
- console.error(chalk.red(`Error: ${error.message}`));
165
- process.exit(1);
166
- }
167
- }
168
-
169
- module.exports = verifyCommand;
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const ora = require('ora');
4
+ const { readDocxBase64 } = require('../lib/docx-handler');
5
+ const { readXlsxBase64 } = require('../lib/xlsx-handler');
6
+ const { validateMetadata, isMultiPart, isStreamingFormat, isLogEmbedFormat } = require('../lib/metadata');
7
+ const { detectFormat, formatBytes } = require('../lib/utils');
8
+ const { decrypt, unpackEncryptionMeta, createDecryptStream } = require('../lib/crypto');
9
+ const { extractContent, findMultiPartFiles } = require('../lib/file-utils');
10
+
11
+ /**
12
+ * Verify that a file can be decoded without actually writing output
13
+ */
14
+ async function verifyCommand(inputFile, options) {
15
+ const spinner = ora('Verifying file...').start();
16
+ const issues = [];
17
+
18
+ try {
19
+ const format = detectFormat(inputFile);
20
+ if (!format) {
21
+ throw new Error('Unknown file format. Supported formats: .xlsx, .docx');
22
+ }
23
+
24
+ spinner.text = `Reading ${format.toUpperCase()} file...`;
25
+
26
+ let readResult;
27
+ if (format === 'xlsx') {
28
+ readResult = await readXlsxBase64(inputFile);
29
+ } else {
30
+ readResult = await readDocxBase64(inputFile);
31
+ }
32
+
33
+ const extracted = extractContent(readResult, format);
34
+ const metadata = extracted.metadata;
35
+ const encryptionMeta = extracted.encryptionMeta;
36
+
37
+ try {
38
+ validateMetadata(metadata);
39
+ spinner.succeed('Metadata valid');
40
+ } catch (e) {
41
+ issues.push(`Metadata: ${e.message}`);
42
+ spinner.warn('Metadata issues found');
43
+ }
44
+
45
+ const isEncrypted = metadata.encrypted || (encryptionMeta && encryptionMeta.length > 0);
46
+ const isV5 = isLogEmbedFormat(metadata);
47
+ const isV4 = !isV5 && isStreamingFormat(metadata);
48
+
49
+ // Check multi-part
50
+ const hasMultipleParts = isMultiPart(metadata) || metadata.partNumber !== null;
51
+ if (hasMultipleParts) {
52
+ spinner.text = 'Checking multi-part files...';
53
+ const inputDir = path.dirname(inputFile);
54
+ const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
55
+ const totalParts = metadata.totalParts || allParts.length;
56
+
57
+ if (metadata.totalParts !== null && allParts.length !== metadata.totalParts) {
58
+ const missing = [];
59
+ const foundParts = new Set(allParts.map(p => p.partNumber));
60
+ for (let i = 1; i <= metadata.totalParts; i++) {
61
+ if (!foundParts.has(i)) missing.push(i);
62
+ }
63
+ issues.push(`Missing parts: ${missing.join(', ')}`);
64
+ spinner.warn(`Found ${allParts.length}/${metadata.totalParts} parts`);
65
+ } else {
66
+ spinner.succeed(`All ${totalParts} parts found`);
67
+ }
68
+ }
69
+
70
+ // Check password for encrypted files
71
+ if (isEncrypted) {
72
+ if (!options.password) {
73
+ issues.push('File is encrypted but no password provided');
74
+ spinner.warn('Encryption check skipped (no password)');
75
+ } else {
76
+ spinner.text = 'Verifying decryption...';
77
+ try {
78
+ if (isV5) {
79
+ await verifyV5Encryption(extracted, options.password);
80
+ } else if (isV4) {
81
+ await verifyV4Encryption(inputFile, format, encryptionMeta, options.password);
82
+ } else {
83
+ await verifyV3Encryption(inputFile, format, metadata, extracted.encryptedContent, encryptionMeta, options.password);
84
+ }
85
+ spinner.succeed('Decryption password valid');
86
+ } catch (e) {
87
+ issues.push('Decryption failed - wrong password or corrupted data');
88
+ spinner.fail('Decryption check failed');
89
+ }
90
+ }
91
+ }
92
+
93
+ // Summary
94
+ console.log();
95
+
96
+ if (issues.length === 0) {
97
+ console.log(chalk.green.bold('✓ File verification passed!'));
98
+ console.log(chalk.cyan(` Original file: ${metadata.originalFilename}`));
99
+ console.log(chalk.cyan(` Size: ${formatBytes(metadata.originalSize)}`));
100
+ console.log(chalk.cyan(` Encrypted: ${isEncrypted ? 'Yes' : 'No'}`));
101
+ console.log(chalk.cyan(` Compressed: ${metadata.compressed ? 'Yes' : 'No'}`));
102
+
103
+ let versionStr = 'v3 (legacy)';
104
+ if (isV5) versionStr = 'v5 (log-embed)';
105
+ else if (isV4) versionStr = 'v4 (streaming)';
106
+ console.log(chalk.cyan(` Format version: ${versionStr}`));
107
+
108
+ if (hasMultipleParts) {
109
+ const totalParts = metadata.totalParts || 'unknown';
110
+ console.log(chalk.cyan(` Parts: ${totalParts}`));
111
+ }
112
+
113
+ console.log();
114
+ console.log(chalk.green('File is ready to decode.'));
115
+ } else {
116
+ console.log(chalk.yellow.bold(' Verification completed with issues:'));
117
+ console.log();
118
+ for (const issue of issues) {
119
+ console.log(chalk.yellow(` • ${issue}`));
120
+ }
121
+ console.log();
122
+
123
+ const hasBlockingIssue = issues.some(i =>
124
+ i.includes('Missing parts') ||
125
+ i.includes('Decryption failed') ||
126
+ i.includes('Metadata')
127
+ );
128
+
129
+ if (hasBlockingIssue) {
130
+ console.log(chalk.red('File cannot be decoded until issues are resolved.'));
131
+ process.exit(1);
132
+ } else {
133
+ console.log(chalk.yellow('File may still be decodable. Run decode to attempt.'));
134
+ }
135
+ }
136
+
137
+ } catch (error) {
138
+ spinner.fail('Verification failed');
139
+ console.error(chalk.red(`Error: ${error.message}`));
140
+ process.exit(1);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Verify v5 log-embed encryption
146
+ */
147
+ async function verifyV5Encryption(extracted, password) {
148
+ const { payloadBuffer, encryptionMeta } = extracted;
149
+ const { iv, salt, authTag } = unpackEncryptionMeta(encryptionMeta);
150
+ const decipher = createDecryptStream(password, iv, salt, authTag);
151
+ decipher.update(payloadBuffer);
152
+ decipher.final();
153
+ }
154
+
155
+ /**
156
+ * Verify v4 per-part encryption
157
+ */
158
+ async function verifyV4Encryption(inputFile, format, encryptionMeta, password) {
159
+ const { iv, salt, authTag } = unpackEncryptionMeta(encryptionMeta);
160
+
161
+ let readResult;
162
+ if (format === 'xlsx') {
163
+ readResult = await readXlsxBase64(inputFile);
164
+ } else {
165
+ readResult = await readDocxBase64(inputFile);
166
+ }
167
+
168
+ const { encryptedContent } = extractContent(readResult, format);
169
+ const binaryData = Buffer.from(encryptedContent, 'base64');
170
+
171
+ const decipher = createDecryptStream(password, iv, salt, authTag);
172
+ decipher.update(binaryData);
173
+ decipher.final();
174
+ }
175
+
176
+ /**
177
+ * Verify v3 shared encryption
178
+ */
179
+ async function verifyV3Encryption(inputFile, format, metadata, encryptedContent, encryptionMeta, password) {
180
+ const { iv, salt, authTag } = unpackEncryptionMeta(encryptionMeta);
181
+
182
+ let fullContent = encryptedContent;
183
+
184
+ if (isMultiPart(metadata)) {
185
+ const inputDir = path.dirname(inputFile);
186
+ const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
187
+
188
+ if (allParts.length === metadata.totalParts) {
189
+ const contentParts = [];
190
+ for (const part of allParts) {
191
+ let partResult;
192
+ if (format === 'xlsx') {
193
+ partResult = await readXlsxBase64(part.path);
194
+ } else {
195
+ partResult = await readDocxBase64(part.path);
196
+ }
197
+ const { encryptedContent: partContent } = extractContent(partResult, format);
198
+ contentParts.push(partContent);
199
+ }
200
+ fullContent = contentParts.join('');
201
+ }
202
+ }
203
+
204
+ decrypt(fullContent, password, iv, salt, authTag);
205
+ }
206
+
207
+ module.exports = verifyCommand;