@socketsecurity/lib 5.11.2 → 5.11.4

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 CHANGED
@@ -5,6 +5,30 @@ 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.11.4](https://github.com/SocketDev/socket-lib/releases/tag/v5.11.4) - 2026-03-28
9
+
10
+ ### Changed
11
+
12
+ - **perf**: Lazy-load heavy external sub-bundles across 7 modules (#119)
13
+ - `sorts.ts`: Defer semver (2.5 MB via npm-pack) and fastSort until first use
14
+ - `versions.ts`: Defer semver until first use
15
+ - `archives.ts`: Defer adm-zip (102 KB) and tar-fs (105 KB) until extraction
16
+ - `globs.ts`: Defer fast-glob and picomatch (260 KB via pico-pack) until glob execution
17
+ - `fs.ts`: Defer del (260 KB via pico-pack) until safeDelete call
18
+ - `spawn.ts`: Defer @npmcli/promise-spawn (17 KB) until async spawn
19
+ - `strings.ts`: Defer get-east-asian-width (10 KB) until stringWidth call
20
+ - Importing lightweight exports (isObject, httpJson, localeCompare, readJsonSync, stripAnsi) no longer loads heavy externals at module init time
21
+
22
+ ## [5.11.3](https://github.com/SocketDev/socket-lib/releases/tag/v5.11.3) - 2026-03-26
23
+
24
+ ### Fixed
25
+
26
+ - **build**: Deduplicate shared deps across external bundles (#110)
27
+ - **quality**: Comprehensive quality scan fixes across codebase (#111)
28
+ - **releases**: Add in-memory TTL cache for GitHub API responses
29
+ - **releases**: Guard against missing assets in GitHub release response (#112)
30
+ - **process-lock**: Fix Windows path separator handling for lock directory creation (#112)
31
+
8
32
  ## [5.11.2](https://github.com/SocketDev/socket-lib/releases/tag/v5.11.2) - 2026-03-24
9
33
 
10
34
  ### Added
package/dist/abort.js CHANGED
@@ -51,9 +51,7 @@ function createTimeoutSignal(ms) {
51
51
  if (ms <= 0) {
52
52
  throw new TypeError("timeout must be a positive number");
53
53
  }
54
- const controller = new AbortController();
55
- setTimeout(() => controller.abort(), ms);
56
- return controller.signal;
54
+ return AbortSignal.timeout(Math.ceil(ms));
57
55
  }
58
56
  // Annotate the CommonJS export names for ESM import in node:
59
57
  0 && (module.exports = {
package/dist/ansi.js CHANGED
@@ -39,7 +39,7 @@ const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
39
39
  // @__NO_SIDE_EFFECTS__
40
40
  function ansiRegex(options) {
41
41
  const { onlyFirst } = options ?? {};
42
- const ST = "(?:\\u0007\\u001B\\u005C|\\u009C)";
42
+ const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
43
43
  const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
44
44
  const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
45
45
  const pattern = `${osc}|${csi}`;
@@ -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 extractStream = import_tar_fs.default.extract(normalizedOutputDir, {
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 extractStream = import_tar_fs.default.extract(normalizedOutputDir, {
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 zip = new import_adm_zip.default(archivePath);
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(
@@ -157,12 +157,7 @@ function getPositionalArgs(startIndex = 2) {
157
157
  return positionals;
158
158
  }
159
159
  function hasFlag(flag, argv = import_node_process.default.argv) {
160
- const flagVariants = [
161
- `--${flag}`,
162
- // Short flag.
163
- `-${flag.charAt(0)}`
164
- ];
165
- return flagVariants.some((variant) => argv.includes(variant));
160
+ return argv.includes(`--${flag}`);
166
161
  }
167
162
  // Annotate the CommonJS export names for ESM import in node:
168
163
  0 && (module.exports = {
@@ -90,9 +90,16 @@ function createTtlCache(options) {
90
90
  }
91
91
  const cacheEntry = await cacache.safeGet(fullKey);
92
92
  if (cacheEntry) {
93
- const entry = JSON.parse(
94
- cacheEntry.data.toString("utf8")
95
- );
93
+ let entry;
94
+ try {
95
+ entry = JSON.parse(cacheEntry.data.toString("utf8"));
96
+ } catch {
97
+ try {
98
+ await cacache.remove(fullKey);
99
+ } catch {
100
+ }
101
+ return void 0;
102
+ }
96
103
  if (!isExpired(entry)) {
97
104
  if (opts.memoize) {
98
105
  memoCache.set(fullKey, entry);
@@ -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 && actualIntegrity !== integrity) {
310
- await (0, import_fs.safeDelete)(destPath);
311
- throw new Error(
312
- `Integrity mismatch: expected ${integrity}, got ${actualIntegrity}`
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);
@@ -45,6 +45,8 @@ function getPath() {
45
45
  }
46
46
  return _path;
47
47
  }
48
+ const fs = /* @__PURE__ */ getFs();
49
+ const path = /* @__PURE__ */ getPath();
48
50
  const logger = (0, import_logger.getDefaultLogger)();
49
51
  const MANIFEST_FILE_NAME = ".dlx-manifest.json";
50
52
  function isBinaryEntry(entry) {
@@ -57,7 +59,7 @@ class DlxManifest {
57
59
  manifestPath;
58
60
  lockPath;
59
61
  constructor(options = {}) {
60
- this.manifestPath = options.manifestPath ?? (/* @__PURE__ */ getPath()).join((0, import_socket.getSocketDlxDir)(), MANIFEST_FILE_NAME);
62
+ this.manifestPath = options.manifestPath ?? path.join((0, import_socket.getSocketDlxDir)(), MANIFEST_FILE_NAME);
61
63
  this.lockPath = `${this.manifestPath}.lock`;
62
64
  }
63
65
  /**
@@ -66,7 +68,7 @@ class DlxManifest {
66
68
  */
67
69
  readManifest() {
68
70
  try {
69
- if (!(/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
71
+ if (!fs.existsSync(this.manifestPath)) {
70
72
  return /* @__PURE__ */ Object.create(null);
71
73
  }
72
74
  const rawContent = (0, import_fs.readFileUtf8Sync)(this.manifestPath);
@@ -87,7 +89,7 @@ class DlxManifest {
87
89
  * @private
88
90
  */
89
91
  async writeManifest(data) {
90
- const manifestDir = (/* @__PURE__ */ getPath()).dirname(this.manifestPath);
92
+ const manifestDir = path.dirname(this.manifestPath);
91
93
  try {
92
94
  (0, import_fs.safeMkdirSync)(manifestDir, { recursive: true });
93
95
  } catch (error) {
@@ -98,18 +100,12 @@ class DlxManifest {
98
100
  const content = JSON.stringify(data, null, 2);
99
101
  const tempPath = `${this.manifestPath}.tmp`;
100
102
  try {
101
- (/* @__PURE__ */ getFs()).writeFileSync(tempPath, content, "utf8");
102
- (/* @__PURE__ */ getFs()).writeFileSync(this.manifestPath, content, "utf8");
103
- try {
104
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
105
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
106
- }
107
- } catch {
108
- }
103
+ fs.writeFileSync(tempPath, content, "utf8");
104
+ fs.renameSync(tempPath, this.manifestPath);
109
105
  } catch (error) {
110
106
  try {
111
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
112
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
107
+ if (fs.existsSync(tempPath)) {
108
+ fs.unlinkSync(tempPath);
113
109
  }
114
110
  } catch {
115
111
  }
@@ -122,17 +118,16 @@ class DlxManifest {
122
118
  async clear(name) {
123
119
  await import_process_lock.processLock.withLock(this.lockPath, async () => {
124
120
  try {
125
- if (!(/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
121
+ if (!fs.existsSync(this.manifestPath)) {
126
122
  return;
127
123
  }
128
- const content = (/* @__PURE__ */ getFs()).readFileSync(this.manifestPath, "utf8");
124
+ const content = fs.readFileSync(this.manifestPath, "utf8");
129
125
  if (!content.trim()) {
130
126
  return;
131
127
  }
132
128
  const data = JSON.parse(content);
133
129
  delete data[name];
134
- const updatedContent = JSON.stringify(data, null, 2);
135
- (/* @__PURE__ */ getFs()).writeFileSync(this.manifestPath, updatedContent, "utf8");
130
+ await this.writeManifest(data);
136
131
  } catch (error) {
137
132
  logger.warn(
138
133
  `Failed to clear cache for ${name}: ${error instanceof Error ? error.message : String(error)}`
@@ -146,8 +141,8 @@ class DlxManifest {
146
141
  async clearAll() {
147
142
  await import_process_lock.processLock.withLock(this.lockPath, async () => {
148
143
  try {
149
- if ((/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
150
- (/* @__PURE__ */ getFs()).unlinkSync(this.manifestPath);
144
+ if (fs.existsSync(this.manifestPath)) {
145
+ fs.unlinkSync(this.manifestPath);
151
146
  }
152
147
  } catch (error) {
153
148
  logger.warn(
@@ -173,7 +168,7 @@ class DlxManifest {
173
168
  */
174
169
  getAllPackages() {
175
170
  try {
176
- if (!(/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
171
+ if (!fs.existsSync(this.manifestPath)) {
177
172
  return [];
178
173
  }
179
174
  const rawContent = (0, import_fs.readFileUtf8Sync)(this.manifestPath);
@@ -219,8 +214,8 @@ class DlxManifest {
219
214
  await import_process_lock.processLock.withLock(this.lockPath, async () => {
220
215
  let data = /* @__PURE__ */ Object.create(null);
221
216
  try {
222
- if ((/* @__PURE__ */ getFs()).existsSync(this.manifestPath)) {
223
- const content2 = (/* @__PURE__ */ getFs()).readFileSync(this.manifestPath, "utf8");
217
+ if (fs.existsSync(this.manifestPath)) {
218
+ const content2 = fs.readFileSync(this.manifestPath, "utf8");
224
219
  if (content2.trim()) {
225
220
  data = JSON.parse(content2);
226
221
  }
@@ -231,7 +226,7 @@ class DlxManifest {
231
226
  );
232
227
  }
233
228
  data[name] = record;
234
- const manifestDir = (/* @__PURE__ */ getPath()).dirname(this.manifestPath);
229
+ const manifestDir = path.dirname(this.manifestPath);
235
230
  try {
236
231
  (0, import_fs.safeMkdirSync)(manifestDir, { recursive: true });
237
232
  } catch (error) {
@@ -242,18 +237,12 @@ class DlxManifest {
242
237
  const content = JSON.stringify(data, null, 2);
243
238
  const tempPath = `${this.manifestPath}.tmp`;
244
239
  try {
245
- (/* @__PURE__ */ getFs()).writeFileSync(tempPath, content, "utf8");
246
- (/* @__PURE__ */ getFs()).writeFileSync(this.manifestPath, content, "utf8");
247
- try {
248
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
249
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
250
- }
251
- } catch {
252
- }
240
+ fs.writeFileSync(tempPath, content, "utf8");
241
+ fs.renameSync(tempPath, this.manifestPath);
253
242
  } catch (error) {
254
243
  try {
255
- if ((/* @__PURE__ */ getFs()).existsSync(tempPath)) {
256
- (/* @__PURE__ */ getFs()).unlinkSync(tempPath);
244
+ if (fs.existsSync(tempPath)) {
245
+ fs.unlinkSync(tempPath);
257
246
  }
258
247
  } catch {
259
248
  }