stegdoc 4.0.0 → 5.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.
@@ -1,114 +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 — check both totalParts and partNumber (v4 may have null totalParts)
67
- const hasMultipleParts = isMultiPart(metadata) || metadata.partNumber !== null;
68
- if (hasMultipleParts) {
69
- console.log(chalk.bold.white('Multi-part File:'));
70
- const inputDir = path.dirname(inputFile);
71
- const allParts = findMultiPartFiles(inputDir, metadata.hash, format);
72
- const totalParts = metadata.totalParts || allParts.length;
73
-
74
- console.log(chalk.cyan(` This is part: ${metadata.partNumber} of ${totalParts}`));
75
- console.log(chalk.cyan(` Hash: ${metadata.hash}`));
76
-
77
- console.log();
78
- console.log(chalk.bold.white('Parts found in directory:'));
79
-
80
- for (let i = 1; i <= totalParts; i++) {
81
- const part = allParts.find(p => p.partNumber === i);
82
- if (part) {
83
- console.log(chalk.green(` ✓ Part ${i}: ${part.filename}`));
84
- } else {
85
- console.log(chalk.red(` ✗ Part ${i}: MISSING`));
86
- }
87
- }
88
-
89
- if (allParts.length >= totalParts) {
90
- console.log();
91
- console.log(chalk.green.bold('All parts found - ready to decode'));
92
- } else {
93
- console.log();
94
- console.log(chalk.yellow.bold(`Missing ${totalParts - allParts.length} part(s)`));
95
- }
96
- } else {
97
- console.log(chalk.bold.white('Single File:'));
98
- console.log(chalk.cyan(` Hash: ${metadata.hash}`));
99
- console.log();
100
- console.log(chalk.green.bold('Ready to decode'));
101
- }
102
-
103
- if (isEncrypted) {
104
- console.log(chalk.yellow('\nNote: Password required for decoding'));
105
- }
106
-
107
- } catch (error) {
108
- spinner.fail('Failed to read file info');
109
- console.error(chalk.red(`Error: ${error.message}`));
110
- process.exit(1);
111
- }
112
- }
113
-
114
- 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;