@socketsecurity/lib 5.11.3 → 5.12.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.
- package/CHANGELOG.md +31 -0
- package/dist/archives.d.ts +2 -0
- package/dist/archives.js +84 -5
- package/dist/dlx/binary.js +10 -4
- package/dist/external/@npmcli/package-json.js +152 -130
- package/dist/external/@socketregistry/packageurl-js.js +3191 -2336
- package/dist/external/npm-pack.js +196 -174
- package/dist/fs.js +12 -3
- package/dist/globs.js +21 -13
- package/dist/http-request.d.ts +55 -1
- package/dist/http-request.js +113 -24
- package/dist/ipc.js +6 -6
- package/dist/sorts.js +16 -12
- package/dist/spawn.js +10 -2
- package/dist/strings.js +11 -3
- package/dist/versions.js +27 -11
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.12.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.12.0) - 2026-04-04
|
|
9
|
+
|
|
10
|
+
### Added — http-request
|
|
11
|
+
|
|
12
|
+
- Lifecycle hooks (`onRequest`/`onResponse`) on `HttpRequestOptions` (#133)
|
|
13
|
+
- Fire per-attempt — retries and redirects each trigger separate hook calls
|
|
14
|
+
- `HttpHooks`, `HttpHookRequestInfo`, `HttpHookResponseInfo` types exported
|
|
15
|
+
- `maxResponseSize` option to reject responses exceeding a byte limit
|
|
16
|
+
- Works through redirects, `httpJson`, and `httpText`
|
|
17
|
+
- `rawResponse` property on `HttpResponse` exposing the underlying `IncomingMessage`
|
|
18
|
+
- `enrichErrorMessage()` exported for reusable error enrichment
|
|
19
|
+
|
|
20
|
+
### Changed — http-request
|
|
21
|
+
|
|
22
|
+
- Error messages now include HTTP method and URL for easier debugging
|
|
23
|
+
- `HttpResponse.headers` type changed from `Record<string, string | string[] | undefined>` to `IncomingHttpHeaders`
|
|
24
|
+
|
|
25
|
+
## [5.11.4](https://github.com/SocketDev/socket-lib/releases/tag/v5.11.4) - 2026-03-28
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **perf**: Lazy-load heavy external sub-bundles across 7 modules (#119)
|
|
30
|
+
- `sorts.ts`: Defer semver (2.5 MB via npm-pack) and fastSort until first use
|
|
31
|
+
- `versions.ts`: Defer semver until first use
|
|
32
|
+
- `archives.ts`: Defer adm-zip (102 KB) and tar-fs (105 KB) until extraction
|
|
33
|
+
- `globs.ts`: Defer fast-glob and picomatch (260 KB via pico-pack) until glob execution
|
|
34
|
+
- `fs.ts`: Defer del (260 KB via pico-pack) until safeDelete call
|
|
35
|
+
- `spawn.ts`: Defer @npmcli/promise-spawn (17 KB) until async spawn
|
|
36
|
+
- `strings.ts`: Defer get-east-asian-width (10 KB) until stringWidth call
|
|
37
|
+
- Importing lightweight exports (isObject, httpJson, localeCompare, readJsonSync, stripAnsi) no longer loads heavy externals at module init time
|
|
38
|
+
|
|
8
39
|
## [5.11.3](https://github.com/SocketDev/socket-lib/releases/tag/v5.11.3) - 2026-03-26
|
|
9
40
|
|
|
10
41
|
### Fixed
|
package/dist/archives.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface ExtractOptions {
|
|
|
10
10
|
quiet?: boolean;
|
|
11
11
|
/** Strip leading path components (like tar --strip-components) */
|
|
12
12
|
strip?: number;
|
|
13
|
+
/** Maximum number of entries to extract (default: 100,000) */
|
|
14
|
+
maxEntries?: number;
|
|
13
15
|
/** Maximum size of a single extracted file in bytes (default: 100MB) */
|
|
14
16
|
maxFileSize?: number;
|
|
15
17
|
/** Maximum total extracted size in bytes (default: 1GB) */
|
package/dist/archives.js
CHANGED
|
@@ -40,10 +40,24 @@ var import_node_fs = require("node:fs");
|
|
|
40
40
|
var import_promises = require("node:stream/promises");
|
|
41
41
|
var import_node_zlib = require("node:zlib");
|
|
42
42
|
var import_node_process = __toESM(require("node:process"));
|
|
43
|
-
var import_adm_zip = __toESM(require("./external/adm-zip.js"));
|
|
44
|
-
var import_tar_fs = __toESM(require("./external/tar-fs.js"));
|
|
45
43
|
var import_fs = require("./fs.js");
|
|
46
44
|
var import_normalize = require("./paths/normalize.js");
|
|
45
|
+
let _AdmZip;
|
|
46
|
+
// @__NO_SIDE_EFFECTS__
|
|
47
|
+
function getAdmZip() {
|
|
48
|
+
if (_AdmZip === void 0) {
|
|
49
|
+
_AdmZip = require("./external/adm-zip.js");
|
|
50
|
+
}
|
|
51
|
+
return _AdmZip;
|
|
52
|
+
}
|
|
53
|
+
let _tarFs;
|
|
54
|
+
// @__NO_SIDE_EFFECTS__
|
|
55
|
+
function getTarFs() {
|
|
56
|
+
if (_tarFs === void 0) {
|
|
57
|
+
_tarFs = require("./external/tar-fs.js");
|
|
58
|
+
}
|
|
59
|
+
return _tarFs;
|
|
60
|
+
}
|
|
47
61
|
let _path;
|
|
48
62
|
// @__NO_SIDE_EFFECTS__
|
|
49
63
|
function getPath() {
|
|
@@ -54,6 +68,7 @@ function getPath() {
|
|
|
54
68
|
}
|
|
55
69
|
const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024;
|
|
56
70
|
const DEFAULT_MAX_TOTAL_SIZE = 1024 * 1024 * 1024;
|
|
71
|
+
const DEFAULT_MAX_ENTRIES = 1e5;
|
|
57
72
|
function validatePathWithinBase(targetPath, baseDir, entryName) {
|
|
58
73
|
const path = /* @__PURE__ */ getPath();
|
|
59
74
|
const resolvedTarget = path.resolve(targetPath);
|
|
@@ -82,6 +97,7 @@ function detectArchiveFormat(filePath) {
|
|
|
82
97
|
}
|
|
83
98
|
async function extractTar(archivePath, outputDir, options = {}) {
|
|
84
99
|
const {
|
|
100
|
+
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
85
101
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
86
102
|
maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,
|
|
87
103
|
strip = 0
|
|
@@ -89,12 +105,37 @@ async function extractTar(archivePath, outputDir, options = {}) {
|
|
|
89
105
|
const normalizedOutputDir = (0, import_normalize.normalizePath)(outputDir);
|
|
90
106
|
await (0, import_fs.safeMkdir)(normalizedOutputDir);
|
|
91
107
|
let totalExtractedSize = 0;
|
|
108
|
+
let entryCount = 0;
|
|
92
109
|
let destroyScheduled = false;
|
|
93
|
-
const
|
|
110
|
+
const tarFs = /* @__PURE__ */ getTarFs();
|
|
111
|
+
const extractStream = tarFs.extract(normalizedOutputDir, {
|
|
94
112
|
map: (header) => {
|
|
95
113
|
if (destroyScheduled) {
|
|
96
114
|
return header;
|
|
97
115
|
}
|
|
116
|
+
entryCount += 1;
|
|
117
|
+
if (entryCount > maxEntries) {
|
|
118
|
+
destroyScheduled = true;
|
|
119
|
+
import_node_process.default.nextTick(() => {
|
|
120
|
+
extractStream.destroy(
|
|
121
|
+
new Error(
|
|
122
|
+
`Archive has too many entries: exceeded limit of ${maxEntries}`
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
return header;
|
|
127
|
+
}
|
|
128
|
+
if (header.name.includes("\0")) {
|
|
129
|
+
destroyScheduled = true;
|
|
130
|
+
import_node_process.default.nextTick(() => {
|
|
131
|
+
extractStream.destroy(
|
|
132
|
+
new Error(
|
|
133
|
+
`Invalid null byte in archive entry name: ${header.name}`
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
return header;
|
|
138
|
+
}
|
|
98
139
|
if (header.type === "symlink" || header.type === "link") {
|
|
99
140
|
destroyScheduled = true;
|
|
100
141
|
import_node_process.default.nextTick(() => {
|
|
@@ -147,6 +188,7 @@ async function extractTar(archivePath, outputDir, options = {}) {
|
|
|
147
188
|
}
|
|
148
189
|
async function extractTarGz(archivePath, outputDir, options = {}) {
|
|
149
190
|
const {
|
|
191
|
+
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
150
192
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
151
193
|
maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,
|
|
152
194
|
strip = 0
|
|
@@ -154,12 +196,37 @@ async function extractTarGz(archivePath, outputDir, options = {}) {
|
|
|
154
196
|
const normalizedOutputDir = (0, import_normalize.normalizePath)(outputDir);
|
|
155
197
|
await (0, import_fs.safeMkdir)(normalizedOutputDir);
|
|
156
198
|
let totalExtractedSize = 0;
|
|
199
|
+
let entryCount = 0;
|
|
157
200
|
let destroyScheduled = false;
|
|
158
|
-
const
|
|
201
|
+
const tarFs = /* @__PURE__ */ getTarFs();
|
|
202
|
+
const extractStream = tarFs.extract(normalizedOutputDir, {
|
|
159
203
|
map: (header) => {
|
|
160
204
|
if (destroyScheduled) {
|
|
161
205
|
return header;
|
|
162
206
|
}
|
|
207
|
+
entryCount += 1;
|
|
208
|
+
if (entryCount > maxEntries) {
|
|
209
|
+
destroyScheduled = true;
|
|
210
|
+
import_node_process.default.nextTick(() => {
|
|
211
|
+
extractStream.destroy(
|
|
212
|
+
new Error(
|
|
213
|
+
`Archive has too many entries: exceeded limit of ${maxEntries}`
|
|
214
|
+
)
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
return header;
|
|
218
|
+
}
|
|
219
|
+
if (header.name.includes("\0")) {
|
|
220
|
+
destroyScheduled = true;
|
|
221
|
+
import_node_process.default.nextTick(() => {
|
|
222
|
+
extractStream.destroy(
|
|
223
|
+
new Error(
|
|
224
|
+
`Invalid null byte in archive entry name: ${header.name}`
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
return header;
|
|
229
|
+
}
|
|
163
230
|
if (header.type === "symlink" || header.type === "link") {
|
|
164
231
|
destroyScheduled = true;
|
|
165
232
|
import_node_process.default.nextTick(() => {
|
|
@@ -212,20 +279,32 @@ async function extractTarGz(archivePath, outputDir, options = {}) {
|
|
|
212
279
|
}
|
|
213
280
|
async function extractZip(archivePath, outputDir, options = {}) {
|
|
214
281
|
const {
|
|
282
|
+
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
215
283
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
216
284
|
maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,
|
|
217
285
|
strip = 0
|
|
218
286
|
} = options;
|
|
219
287
|
const normalizedOutputDir = (0, import_normalize.normalizePath)(outputDir);
|
|
220
288
|
await (0, import_fs.safeMkdir)(normalizedOutputDir);
|
|
221
|
-
const
|
|
289
|
+
const AdmZip = /* @__PURE__ */ getAdmZip();
|
|
290
|
+
const zip = new AdmZip(archivePath);
|
|
222
291
|
const path = /* @__PURE__ */ getPath();
|
|
223
292
|
const entries = zip.getEntries();
|
|
293
|
+
if (entries.length > maxEntries) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`Archive has too many entries: ${entries.length} (limit: ${maxEntries})`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
224
298
|
let totalExtractedSize = 0;
|
|
225
299
|
for (const entry of entries) {
|
|
226
300
|
if (entry.isDirectory) {
|
|
227
301
|
continue;
|
|
228
302
|
}
|
|
303
|
+
if (entry.entryName.includes("\0")) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
`Invalid null byte in archive entry name: ${entry.entryName}`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
229
308
|
const uncompressedSize = entry.header.size;
|
|
230
309
|
if (uncompressedSize > maxFileSize) {
|
|
231
310
|
throw new Error(
|
package/dist/dlx/binary.js
CHANGED
|
@@ -306,11 +306,17 @@ Check your internet connection or verify the URL is accessible.`,
|
|
|
306
306
|
const fileBuffer = await fs.promises.readFile(destPath);
|
|
307
307
|
const hash = crypto.createHash("sha512").update(fileBuffer).digest("base64");
|
|
308
308
|
const actualIntegrity = `sha512-${hash}`;
|
|
309
|
-
if (integrity
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
if (integrity) {
|
|
310
|
+
const integrityMatch = actualIntegrity.length === integrity.length && crypto.timingSafeEqual(
|
|
311
|
+
Buffer.from(actualIntegrity),
|
|
312
|
+
Buffer.from(integrity)
|
|
313
313
|
);
|
|
314
|
+
if (!integrityMatch) {
|
|
315
|
+
await (0, import_fs.safeDelete)(destPath);
|
|
316
|
+
throw new Error(
|
|
317
|
+
`Integrity mismatch: expected ${integrity}, got ${actualIntegrity}`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
314
320
|
}
|
|
315
321
|
if (!import_platform.WIN32) {
|
|
316
322
|
await fs.promises.chmod(destPath, 493);
|