7zip-native 0.0.10 → 0.0.12
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/README.md +2 -2
- package/dist/index.cjs +23 -11
- package/dist/index.mjs +23 -11
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/7zip_native.node +0 -0
- package/prebuilds/darwin-x64/7zip_native.node +0 -0
- package/prebuilds/win32-arm64/7z.dll +0 -0
- package/prebuilds/win32-arm64/7zip_native.node +0 -0
- package/prebuilds/win32-x64/7z.dll +0 -0
- package/prebuilds/win32-x64/7zip_native.node +0 -0
package/README.md
CHANGED
|
@@ -192,7 +192,7 @@ It also accepts these options:
|
|
|
192
192
|
- `compressionLevel`: Integer from 0 to 9; defaults to 5 and applies only to ZIP and 7z. TAR is uncompressed; WIM uses the upstream handler's default settings.
|
|
193
193
|
- `overwrite`: Defaults to `false`. When `true`, an existing output file is replaced after the new archive has been written.
|
|
194
194
|
|
|
195
|
-
Duplicate,
|
|
195
|
+
Duplicate, absolute, or traversal paths are rejected, but platform-supported filename characters such as trailing spaces are preserved in the archive-internal path. The output is first written to a temporary file beside the destination, then installed atomically, so an existing file is preserved if archive creation fails. Archive creation runs in the bounded native worker pool and does not launch a 7-Zip executable.
|
|
196
196
|
|
|
197
197
|
### Extracting to a folder
|
|
198
198
|
|
|
@@ -221,7 +221,7 @@ await archive.extractToFolder('./unpacked', {
|
|
|
221
221
|
|
|
222
222
|
Each native extraction call receives a batch of entry IDs and output paths. The addon queues disk writes asynchronously while 7-Zip continues extracting later entries; `onExtract` runs after that file's queued writes have completed and the file is closed. File contents are not streamed through Node.js or Electron. The `extractToFolder()` promise resolves after all `onExtract` callbacks have run.
|
|
223
223
|
|
|
224
|
-
On Windows only,
|
|
224
|
+
On Windows only, unsupported filename characters (including control characters), trailing dots/spaces in each destination component, and reserved device names are normalized for the output path; selectors and entry metadata remain unchanged. On Linux and macOS, platform-supported filename characters are preserved. Unsafe archive paths, path collisions, symlinked destination components, and symlink or non-file targets are rejected. Archive links are written as regular file data; the API does not create symbolic or hard links. If extraction fails partway through, files already written (or partially written) may remain in the destination.
|
|
225
225
|
|
|
226
226
|
### Concurrency
|
|
227
227
|
|
package/dist/index.cjs
CHANGED
|
@@ -66,32 +66,44 @@ function safeEntrySegments(entryPath) {
|
|
|
66
66
|
throw new ArchiveError('ERR_ARCHIVE_PATH', 'Archive entry path contains a null byte');
|
|
67
67
|
}
|
|
68
68
|
const normalized = entryPath.replace(/\\/g, '/');
|
|
69
|
-
if (normalized.startsWith('/') || /^[a-zA-Z]
|
|
69
|
+
if (normalized.startsWith('/') || /^[a-zA-Z]:\//.test(normalized)) {
|
|
70
70
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Archive entry path is absolute: ${entryPath}`);
|
|
71
71
|
}
|
|
72
72
|
const segments = normalized.split('/').filter(segment => segment !== '' && segment !== '.');
|
|
73
|
-
if (segments.length === 0 || segments.some(segment => segment === '..'
|
|
74
|
-
|| segment.includes(':')
|
|
75
|
-
|| /[. ]$/.test(segment)
|
|
76
|
-
|| /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(segment))) {
|
|
73
|
+
if (segments.length === 0 || segments.some(segment => segment === '..')) {
|
|
77
74
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Unsafe archive entry path: ${entryPath}`);
|
|
78
75
|
}
|
|
79
76
|
return segments;
|
|
80
77
|
}
|
|
78
|
+
function sanitizeWindowsSegment(segment) {
|
|
79
|
+
if (segment === '.' || segment === '..') {
|
|
80
|
+
return segment;
|
|
81
|
+
}
|
|
82
|
+
const sanitized = [...segment].map(character => character.charCodeAt(0) < 0x20 || '<>:|?*"'.includes(character) ? '_' : character).join('')
|
|
83
|
+
.replace(/[. ]+$/g, trailing => '_'.repeat(trailing.length));
|
|
84
|
+
const reservedName = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])/i.exec(sanitized);
|
|
85
|
+
if (reservedName) {
|
|
86
|
+
let index = reservedName[0].length;
|
|
87
|
+
while (sanitized[index] === ' ') {
|
|
88
|
+
index++;
|
|
89
|
+
}
|
|
90
|
+
if (index === sanitized.length || sanitized[index] === '.') {
|
|
91
|
+
return `_${sanitized}`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return sanitized;
|
|
95
|
+
}
|
|
81
96
|
function sanitizeOutputPath(entryPath) {
|
|
82
97
|
if (process.platform !== 'win32') {
|
|
83
98
|
return entryPath;
|
|
84
99
|
}
|
|
85
|
-
return entryPath
|
|
86
|
-
.replace(/([. ]+)([\\/]|$)/g, (_match, unsupported, separator) => '_'.repeat(unsupported.length) + separator)
|
|
87
|
-
.replace(/[<>:|?*"]/g, '_');
|
|
100
|
+
return entryPath.replace(/\\/g, '/').split('/').map(sanitizeWindowsSegment).join('/');
|
|
88
101
|
}
|
|
89
102
|
function assertSafeRelativeArchivePath(entryPath) {
|
|
90
103
|
const normalized = entryPath.replace(/\\/g, '/');
|
|
91
104
|
const segments = normalized.split('/').filter(segment => segment !== '' && segment !== '.');
|
|
92
|
-
if (
|
|
93
|
-
|| normalized
|
|
94
|
-
|| /^[a-zA-Z]:/.test(normalized)
|
|
105
|
+
if (normalized.startsWith('/')
|
|
106
|
+
|| /^[a-zA-Z]:\//.test(normalized)
|
|
95
107
|
|| segments.length === 0
|
|
96
108
|
|| segments.some(segment => segment === '..')) {
|
|
97
109
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Unsafe archive entry path: ${entryPath}`);
|
package/dist/index.mjs
CHANGED
|
@@ -63,32 +63,44 @@ function safeEntrySegments(entryPath) {
|
|
|
63
63
|
throw new ArchiveError('ERR_ARCHIVE_PATH', 'Archive entry path contains a null byte');
|
|
64
64
|
}
|
|
65
65
|
const normalized = entryPath.replace(/\\/g, '/');
|
|
66
|
-
if (normalized.startsWith('/') || /^[a-zA-Z]
|
|
66
|
+
if (normalized.startsWith('/') || /^[a-zA-Z]:\//.test(normalized)) {
|
|
67
67
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Archive entry path is absolute: ${entryPath}`);
|
|
68
68
|
}
|
|
69
69
|
const segments = normalized.split('/').filter(segment => segment !== '' && segment !== '.');
|
|
70
|
-
if (segments.length === 0 || segments.some(segment => segment === '..'
|
|
71
|
-
|| segment.includes(':')
|
|
72
|
-
|| /[. ]$/.test(segment)
|
|
73
|
-
|| /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(segment))) {
|
|
70
|
+
if (segments.length === 0 || segments.some(segment => segment === '..')) {
|
|
74
71
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Unsafe archive entry path: ${entryPath}`);
|
|
75
72
|
}
|
|
76
73
|
return segments;
|
|
77
74
|
}
|
|
75
|
+
function sanitizeWindowsSegment(segment) {
|
|
76
|
+
if (segment === '.' || segment === '..') {
|
|
77
|
+
return segment;
|
|
78
|
+
}
|
|
79
|
+
const sanitized = [...segment].map(character => character.charCodeAt(0) < 0x20 || '<>:|?*"'.includes(character) ? '_' : character).join('')
|
|
80
|
+
.replace(/[. ]+$/g, trailing => '_'.repeat(trailing.length));
|
|
81
|
+
const reservedName = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])/i.exec(sanitized);
|
|
82
|
+
if (reservedName) {
|
|
83
|
+
let index = reservedName[0].length;
|
|
84
|
+
while (sanitized[index] === ' ') {
|
|
85
|
+
index++;
|
|
86
|
+
}
|
|
87
|
+
if (index === sanitized.length || sanitized[index] === '.') {
|
|
88
|
+
return `_${sanitized}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return sanitized;
|
|
92
|
+
}
|
|
78
93
|
function sanitizeOutputPath(entryPath) {
|
|
79
94
|
if (process.platform !== 'win32') {
|
|
80
95
|
return entryPath;
|
|
81
96
|
}
|
|
82
|
-
return entryPath
|
|
83
|
-
.replace(/([. ]+)([\\/]|$)/g, (_match, unsupported, separator) => '_'.repeat(unsupported.length) + separator)
|
|
84
|
-
.replace(/[<>:|?*"]/g, '_');
|
|
97
|
+
return entryPath.replace(/\\/g, '/').split('/').map(sanitizeWindowsSegment).join('/');
|
|
85
98
|
}
|
|
86
99
|
function assertSafeRelativeArchivePath(entryPath) {
|
|
87
100
|
const normalized = entryPath.replace(/\\/g, '/');
|
|
88
101
|
const segments = normalized.split('/').filter(segment => segment !== '' && segment !== '.');
|
|
89
|
-
if (
|
|
90
|
-
|| normalized
|
|
91
|
-
|| /^[a-zA-Z]:/.test(normalized)
|
|
102
|
+
if (normalized.startsWith('/')
|
|
103
|
+
|| /^[a-zA-Z]:\//.test(normalized)
|
|
92
104
|
|| segments.length === 0
|
|
93
105
|
|| segments.some(segment => segment === '..')) {
|
|
94
106
|
throw new ArchiveError('ERR_ARCHIVE_PATH', `Unsafe archive entry path: ${entryPath}`);
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|