@socketsecurity/lib 5.20.1 → 5.21.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 +96 -95
- package/dist/archives.js +13 -0
- package/dist/cacache.js +6 -8
- package/dist/cache-with-ttl.js +1 -1
- package/dist/constants/socket.js +1 -1
- package/dist/dlx/detect.js +25 -8
- package/dist/dlx/package.js +14 -1
- package/dist/fs.js +8 -2
- package/dist/globs.js +5 -1
- package/dist/ipc.js +2 -2
- package/dist/json/parse.d.ts +47 -2
- package/dist/json/parse.js +40 -2
- package/dist/json/types.d.ts +49 -0
- package/dist/memoization.d.ts +4 -23
- package/dist/memoization.js +14 -54
- package/dist/packages/specs.js +9 -2
- package/dist/process-lock.js +1 -6
- package/dist/promise-queue.d.ts +9 -4
- package/dist/promise-queue.js +9 -7
- package/dist/promises.d.ts +41 -0
- package/dist/promises.js +19 -2
- package/dist/regexps.d.ts +4 -13
- package/dist/regexps.js +60 -3
- package/dist/schema/parse.d.ts +26 -0
- package/dist/schema/parse.js +38 -0
- package/dist/schema/types.d.ts +121 -0
- package/dist/schema/validate.d.ts +35 -0
- package/dist/{validation/validate-schema.js → schema/validate.js} +4 -14
- package/dist/suppress-warnings.js +0 -2
- package/dist/url.js +5 -1
- package/dist/versions.js +2 -2
- package/dist/words.js +4 -7
- package/package.json +14 -14
- package/dist/validation/json-parser.d.ts +0 -58
- package/dist/validation/json-parser.js +0 -63
- package/dist/validation/types.d.ts +0 -118
- package/dist/validation/validate-schema.d.ts +0 -124
- /package/dist/{validation → schema}/types.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,92 +5,100 @@ 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.
|
|
8
|
+
## [5.21.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.21.0) - 2026-04-20
|
|
9
9
|
|
|
10
|
-
###
|
|
10
|
+
### Added
|
|
11
11
|
|
|
12
|
-
- `
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- `src/tables.ts` — `displayWidth` measured columns by `.length` of the ANSI-stripped string, i.e. UTF-16 code units rather than rendered terminal cells. CJK, emoji, and combined code points misaligned tables. Routes measurement through `stringWidth` (Intl.Segmenter + East Asian Width)
|
|
17
|
-
- `src/paths/packages.ts` — `resolvePackageJsonDirname` / `resolvePackageJsonPath` gated on `filepath.endsWith('package.json')`, which misidentified any file whose name ended in that suffix (e.g. `/foo/my-package.json`) as a manifest. Now checks for the literal final segment
|
|
18
|
-
- `src/json/edit.ts` — `@example` for `getEditableJsonClass` imported from `@socketsecurity/lib/json`, which is not a package export; fixed to `@socketsecurity/lib/json/edit`
|
|
12
|
+
- `@socketsecurity/lib/schema/validate` — non-throwing Zod/TypeBox validator returning `{ ok, value } | { ok, errors }` with normalized paths
|
|
13
|
+
- `@socketsecurity/lib/schema/parse` — throwing variant for fail-fast trust boundaries
|
|
14
|
+
- `@socketsecurity/lib/schema/types` — `Schema<T>`, `ValidateResult<T>`, `ValidationIssue`, `AnySchema`, `Infer<S>`
|
|
15
|
+
- `@socketsecurity/lib/promises` `withResolvers()` — spec-compliant [`Promise.withResolvers`](https://tc39.es/ecma262/#sec-promise.withResolvers) helper with `PromiseWithResolvers<T>` type. Uses the native implementation when available
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
### Changed
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
- `@socketsecurity/lib/regexps` `escapeRegExp()` — now spec-compliant with TC39 [`RegExp.escape`](https://tc39.es/ecma262/#sec-regexp.escape); uses the native implementation when available. **Caller-visible shape change**: escaped output now uses `\xHH` for many characters that previously passed through literally (e.g. `escapeRegExp('a')` is now `'\x61'`). Functional equivalence (the compiled regex matches the original input) is preserved; only callers that string-match on escape output need updates
|
|
20
|
+
- `@socketsecurity/lib/memoization` `MemoizeOptions<Args>` — dropped the unused second type parameter. Consumers who wrote `MemoizeOptions<Args, Result>` must drop the second argument
|
|
21
|
+
- `@socketsecurity/lib/packages/specs` `getRepoUrlDetails()` — now accepts `git+https://` / `git+ssh://` GitHub URLs and rejects lookalike hosts (`githubXcom`, `fake-github.com.attacker.tld`). scp-style `git@github.com:…` URLs (no `://`) now return `{ user: '', project: '' }` — callers must normalize to https/ssh upstream
|
|
22
|
+
- `@socketsecurity/lib/url` `urlSearchParamAsBoolean()` — accepts the same truthy vocabulary as `envAsBoolean` (`1` / `true` / `yes` / `on`, case-insensitive). Empty-string input now falls through to `defaultValue` instead of returning `false`
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- `@socketsecurity/lib/validation/*` subpath retired — exports re-homed:
|
|
27
|
+
- `validateSchema` / `parseSchema` → `@socketsecurity/lib/schema/validate` / `@socketsecurity/lib/schema/parse`
|
|
28
|
+
- `safeJsonParse` → `@socketsecurity/lib/json/parse`
|
|
29
|
+
- Types → `@socketsecurity/lib/schema/types` and `@socketsecurity/lib/json/types`
|
|
30
|
+
- `memoizeDebounced` from `@socketsecurity/lib/memoization` — was misnamed and had no consumers. Use `memoize` / `memoizeAsync` with a `ttl` instead
|
|
27
31
|
|
|
28
32
|
### Fixed
|
|
29
33
|
|
|
30
|
-
- `
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
34
|
+
- `@socketsecurity/lib/versions` `maxVersion()` / `minVersion()` — return the latest/earliest prerelease for all-prerelease inputs (previously returned `undefined`)
|
|
35
|
+
- `@socketsecurity/lib/fs` `findUp()` / `findUpSync()` — traverse up to and **including** the filesystem root (previously missed matches at `/.foo`)
|
|
36
|
+
- `@socketsecurity/lib/words` `capitalize()` — safe for non-BMP characters (emoji, astral-plane scripts); previously produced broken surrogate pairs
|
|
37
|
+
- `@socketsecurity/lib/words` `determineArticle()` — case-insensitive vowel match (`Apple` → `an Apple`)
|
|
38
|
+
- `@socketsecurity/lib/archives` `extractZip()` / `extractTar()` / `extractTarGz()` — missing-archive errors now uniformly surface as `ENOENT` with `code` / `path` / message (previously `extractZip` surfaced adm-zip's generic `"Invalid filename"`)
|
|
39
|
+
- `@socketsecurity/lib/promise-queue` — bounded queue now rejects the newest submission when full, preserving in-flight work
|
|
40
|
+
- `@socketsecurity/lib/cacache` / `@socketsecurity/lib/cache-with-ttl` — wildcard key deletion anchors both ends of the pattern (`deleteAll('foo*bar')` no longer sweeps `foo123bar-extra`)
|
|
41
|
+
- `@socketsecurity/lib/process-lock` — sub-second `staleMs` values now honored at full precision; TOCTOU window on lock acquisition closed
|
|
42
|
+
- `@socketsecurity/lib/suppress-warnings` `withSuppressedWarnings()` — no longer wipes concurrent suppressions on exit
|
|
43
|
+
- Unbounded LRU caches in `@socketsecurity/lib/dlx` capped (binary path, package.json path); negative package.json lookups now expire after 10s
|
|
44
|
+
- Glob cache keys for array-valued options (e.g. `ignore`) are order-insensitive
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
### Performance
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
- `@socketsecurity/lib/memoization` — `memoize()` / `memoizeAsync()` cache-hit bookkeeping dropped from O(n) to O(1). Noticeable on caches with many entries
|
|
49
|
+
- `@socketsecurity/lib/cacache` — wildcard `clear()` no longer recompiles the match regex per streamed entry
|
|
41
50
|
|
|
42
|
-
5.
|
|
51
|
+
## [5.20.1](https://github.com/SocketDev/socket-lib/releases/tag/v5.20.1) - 2026-04-19
|
|
43
52
|
|
|
44
|
-
|
|
53
|
+
### Fixed
|
|
45
54
|
|
|
46
|
-
- `@socketsecurity/lib/
|
|
47
|
-
- `@socketsecurity/lib/
|
|
48
|
-
- `@socketsecurity/lib/
|
|
49
|
-
-
|
|
50
|
-
-
|
|
55
|
+
- `@socketsecurity/lib/ipc` — harden stub-file writes against symlink/TOCTOU attacks on shared-tmp filesystems (POSIX ownership + mode validation, `O_EXCL | O_NOFOLLOW` open)
|
|
56
|
+
- `@socketsecurity/lib/cache-with-ttl` `getOrFetch()` — close concurrent-caller race that let two cold-cache awaits both skip the inflight-dedupe check and fire the fetcher twice
|
|
57
|
+
- `@socketsecurity/lib/cache-with-ttl` — cap the in-memory memo layer with LRU eviction (`memoMaxSize`, default 1000); long-running processes no longer grow unbounded
|
|
58
|
+
- `@socketsecurity/lib/memoization` `memoizeAsync()` — refresh cache entry timestamp on resolve so slow fetches (longer than `ttl`) aren't classified as expired the moment they land
|
|
59
|
+
- `@socketsecurity/lib/tables` — `displayWidth` now measures rendered terminal cells (via `stringWidth`) instead of UTF-16 code units; CJK / emoji / combining marks align correctly
|
|
60
|
+
- `@socketsecurity/lib/paths/packages` — `resolvePackageJsonDirname` / `resolvePackageJsonPath` no longer mis-identify files like `/foo/my-package.json` as package manifests
|
|
61
|
+
- `@socketsecurity/lib/json/edit` — `@example` import path corrected
|
|
51
62
|
|
|
52
|
-
## [5.
|
|
63
|
+
## [5.20.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.20.0) - 2026-04-19
|
|
53
64
|
|
|
54
|
-
### Added
|
|
65
|
+
### Added
|
|
55
66
|
|
|
56
|
-
- `
|
|
57
|
-
- `normalizeHash()`, `computeHashes()`, `verifyHash()` — `verifyHash` uses `crypto.timingSafeEqual` for constant-time comparison
|
|
58
|
-
- `DlxHashMismatchError` — carries `expected` + `actual` for diagnostics
|
|
67
|
+
- `@socketsecurity/lib/validation/validate-schema` — universal Zod-style schema validator with `validateSchema` (tagged result) and `parseSchema` (throwing); `Infer<S>`, `ValidateResult<T>`, `ValidationIssue`, `AnySchema` types. No runtime `zod` dependency
|
|
59
68
|
|
|
60
|
-
|
|
69
|
+
> **Deprecated in 5.21.0**: moved to `@socketsecurity/lib/schema/*`.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
61
72
|
|
|
62
|
-
-
|
|
63
|
-
- `
|
|
64
|
-
-
|
|
73
|
+
- `@socketsecurity/lib/promise-queue` — synchronous throws inside a queued task now convert to proper rejections instead of escaping as uncaught exceptions
|
|
74
|
+
- `@socketsecurity/lib/stdio/progress` `formatTime()` — clamp negative milliseconds so over-ticking / clock-skewed bars don't render negative ETAs
|
|
75
|
+
- `@socketsecurity/lib/dlx/lockfile` — scratch-directory cleanup can no longer clobber the real exception from the main block
|
|
76
|
+
- `@socketsecurity/lib/dlx/package` `parsePackageSpec` — normalize a bare trailing `@` (e.g. `"pkg@"`) to `version: undefined`
|
|
77
|
+
- `@socketsecurity/lib/stdio/prompts` — tighten an internal destructure type away from `as any`
|
|
78
|
+
- `@socketsecurity/lib/http-request` — hoist checksum regex literals out of a per-line loop
|
|
65
79
|
|
|
66
|
-
|
|
80
|
+
## [5.19.1](https://github.com/SocketDev/socket-lib/releases/tag/v5.19.1) - 2026-04-19
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
- **Default `minReleaseDays: 7`** — resolution refuses to select versions published in the last week. Pass `0` to disable. `minReleaseMins` is a pnpm-style alias (mutually exclusive with `minReleaseDays`)
|
|
70
|
-
- `LockfileSpec` type — export for use as the new `lockfile` option on `downloadPackage`
|
|
82
|
+
### Fixed
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
Restore `@socketsecurity/lib/stdio/prompts`, `@socketsecurity/lib/stdio/progress`, and `@socketsecurity/lib/stdio/clear` — accidentally removed in 5.19.0 without a major-bump callout. Downstream consumers that import `stdio/prompts` directly are unbroken.
|
|
73
85
|
|
|
74
|
-
|
|
75
|
-
- `DlxBinaryOptions.hash?: HashSpec` — ergonomic alternative to the lower-level `integrity` and `sha256` fields (both still accepted)
|
|
86
|
+
## [5.19.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.19.0) - 2026-04-19
|
|
76
87
|
|
|
77
|
-
###
|
|
88
|
+
### Added
|
|
78
89
|
|
|
79
|
-
- `
|
|
90
|
+
- `@socketsecurity/lib/dlx/integrity` — hash verification utilities: `HashSpec`, `NormalizedHash`, `ComputedHashes`, `normalizeHash()`, `computeHashes()`, `verifyHash()` (constant-time via `crypto.timingSafeEqual`), `DlxHashMismatchError`
|
|
91
|
+
- `@socketsecurity/lib/dlx/arborist` — hardened `@npmcli/arborist` wrappers: `safeIdealTree()`, `safeReify()`, `writeSafeNpmrc()`. Locks down `audit`, `fund`, `ignoreScripts`, `saveBundle`, etc. Supports `before?: Date` for release-age enforcement
|
|
92
|
+
- `@socketsecurity/lib/dlx/lockfile` — `generatePackagePin()` returns `{ name, version, hash, packageJson, lockfile }` for a resolved package. Default `minReleaseDays: 7` refuses versions published in the last week (`0` to disable); `minReleaseMins` accepted as pnpm-style alias
|
|
93
|
+
- `DlxPackageOptions.hash`, `DlxPackageOptions.lockfile`, `DlxBinaryOptions.hash` — first-class integrity + lockfile options on the dlx entry points
|
|
80
94
|
|
|
81
|
-
###
|
|
95
|
+
### Fixed
|
|
82
96
|
|
|
83
|
-
- `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- `@npmcli/git`, `pacote/lib/{git,file,dir,remote}.js` — registry specs only
|
|
89
|
-
- arborist `audit-report.js`, `yarn-lock.js`, `isolated-reifier.js`, `query-selector-all.js`, `printable.js` — each gated or unused
|
|
90
|
-
- `cacache/lib/verify.js` — `cacache.verify` (npm cache verify) unused
|
|
91
|
-
- `proggy` — progress tracker, gated by `progress: false`
|
|
92
|
-
- `debug/src/browser.js` — Node-only bundle
|
|
93
|
-
- `dist/external/zod.js`: 597,238 → 291,430 bytes (−306 KB, −51.2%). Stubbed `zod/v4/{core,classic,mini}`'s eager `locales/index.cjs` barrel (40+ translation modules). Opt-in via `z.config(z.locales.xx())` is never called by us
|
|
97
|
+
- `pacote` shim — exposes `tarball`, `manifest`, `packument` alongside `extract`. Fixes a latent runtime crash in `fetchPackageManifest` / `fetchPackagePackument` callers
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
|
|
101
|
+
Reduced bundle size of `dist/external/npm-pack.js` (−771 KB, −30.5%) and `dist/external/zod.js` (−306 KB, −51.2%) by stubbing code paths our callers never reach (Sigstore attestation, arborist audit/query, zod locale translations, etc.)
|
|
94
102
|
|
|
95
103
|
## [5.18.2](https://github.com/SocketDev/socket-lib/releases/tag/v5.18.2) - 2026-04-14
|
|
96
104
|
|
|
@@ -100,70 +108,63 @@ Restored:
|
|
|
100
108
|
|
|
101
109
|
## [5.18.1](https://github.com/SocketDev/socket-lib/releases/tag/v5.18.1) - 2026-04-14
|
|
102
110
|
|
|
103
|
-
### Changed
|
|
111
|
+
### Changed
|
|
104
112
|
|
|
105
|
-
-
|
|
106
|
-
- npm-pack.js: 69,738 → 66,443 lines (2.59MB → 2.46MB), 22 duplicate packages removed
|
|
113
|
+
- Deduplicated the `dist/external/npm-pack` bundle via `pnpm overrides` (pacote 21.5.0, make-fetch-happen 15.0.5, and 7 transitive `@npmcli/*` packages) — 22 duplicate packages removed, ~130 KB smaller
|
|
107
114
|
|
|
108
115
|
## [5.18.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.18.0) - 2026-04-14
|
|
109
116
|
|
|
110
|
-
### Added
|
|
117
|
+
### Added
|
|
111
118
|
|
|
112
|
-
- Socket Firewall API check before package downloads
|
|
119
|
+
- `@socketsecurity/lib/dlx` — Socket Firewall API check before package downloads. Resolves the dependency tree and blocks on critical/high severity alerts
|
|
113
120
|
|
|
114
|
-
### Changed
|
|
121
|
+
### Changed
|
|
115
122
|
|
|
116
|
-
-
|
|
123
|
+
- `@socketsecurity/lib/http-request` — default `User-Agent` updated from `socket-registry/1.0` to `socketsecurity-lib/{version}`
|
|
117
124
|
|
|
118
125
|
## [5.17.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.17.0) - 2026-04-14
|
|
119
126
|
|
|
120
|
-
### Added
|
|
127
|
+
### Added
|
|
121
128
|
|
|
122
|
-
- `isUnixPath()` — detect MSYS/Git Bash drive
|
|
129
|
+
- `@socketsecurity/lib/paths` `isUnixPath()` — detect MSYS/Git Bash drive-letter notation (`/c/...`)
|
|
123
130
|
|
|
124
|
-
### Changed
|
|
131
|
+
### Changed
|
|
125
132
|
|
|
126
|
-
- `normalizePath()`
|
|
127
|
-
- `fromUnixPath()`
|
|
133
|
+
- `@socketsecurity/lib/paths` `normalizePath()` — converts MSYS drive letters on Windows (`/c/path` → `C:/path`)
|
|
134
|
+
- `@socketsecurity/lib/paths` `fromUnixPath()` — produces native Windows paths with backslashes (`/c/path` → `C:\path`), making it the true inverse of `toUnixPath()`
|
|
128
135
|
|
|
129
136
|
## [5.16.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.16.0) - 2026-04-14
|
|
130
137
|
|
|
131
|
-
### Added
|
|
138
|
+
### Added
|
|
132
139
|
|
|
133
|
-
- `fromUnixPath()` — convert MSYS/Git Bash Unix-style paths (`/c/path`) back to native Windows format (`C:/path`), inverse of `toUnixPath` (#168)
|
|
140
|
+
- `@socketsecurity/lib/paths` `fromUnixPath()` — convert MSYS/Git Bash Unix-style paths (`/c/path`) back to native Windows format (`C:/path`), inverse of `toUnixPath` (#168)
|
|
134
141
|
|
|
135
|
-
### Fixed
|
|
142
|
+
### Fixed
|
|
136
143
|
|
|
137
|
-
-
|
|
144
|
+
- `@socketsecurity/lib/dlx` `isInSocketDlx` — normalize the dlx directory path for Windows compatibility
|
|
138
145
|
|
|
139
146
|
## [5.15.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.15.0) - 2026-04-06
|
|
140
147
|
|
|
141
|
-
### Added
|
|
142
|
-
|
|
143
|
-
- `stream` option on `HttpRequestOptions` — resolves with `HttpResponse` immediately after headers arrive, leaving `rawResponse` unconsumed for piping to files
|
|
144
|
-
- `headers`, `ok`, `status`, `statusText` fields on `HttpDownloadResult`
|
|
145
|
-
|
|
146
|
-
### Changed — http-request
|
|
148
|
+
### Added
|
|
147
149
|
|
|
148
|
-
- `
|
|
150
|
+
- `@socketsecurity/lib/http-request` — `stream` option on `HttpRequestOptions` resolves with `HttpResponse` immediately after headers arrive, leaving `rawResponse` unconsumed for piping to files
|
|
151
|
+
- `@socketsecurity/lib/http-request` — `headers`, `ok`, `status`, `statusText` fields on `HttpDownloadResult`
|
|
149
152
|
|
|
150
153
|
## [5.14.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.14.0) - 2026-04-06
|
|
151
154
|
|
|
152
|
-
### Added
|
|
155
|
+
### Added
|
|
153
156
|
|
|
154
|
-
-
|
|
155
|
-
- `
|
|
156
|
-
- `
|
|
157
|
-
-
|
|
158
|
-
- `
|
|
159
|
-
- `
|
|
157
|
+
- `@socketsecurity/lib/http-request`:
|
|
158
|
+
- `HttpResponseError` class — thrown on non-2xx when `throwOnError` is enabled; carries the full `HttpResponse`
|
|
159
|
+
- `throwOnError` option — non-2xx responses throw instead of resolving with `ok: false`
|
|
160
|
+
- `onRetry` callback — customize retry behavior per-attempt (`false` to stop, a `number` to override delay, `undefined` for default backoff)
|
|
161
|
+
- Streaming body support — `body` accepts `Readable` streams (incl. `form-data`), auto-merges `getHeaders()` when present
|
|
162
|
+
- `parseRetryAfterHeader()` — standalone RFC 7231 §7.1.3 parser
|
|
163
|
+
- `sanitizeHeaders()` — redact sensitive headers for safe logging
|
|
160
164
|
|
|
161
|
-
### Changed
|
|
165
|
+
### Changed
|
|
162
166
|
|
|
163
|
-
- `HttpRequestOptions.body`
|
|
164
|
-
- Redirect responses now drained via `res.resume()` to free sockets
|
|
165
|
-
- `maxResponseSize` exceeded now cleans up both response and request
|
|
166
|
-
- `onResponse` hooks wrapped in try/catch — user hook errors can no longer leave promises pending
|
|
167
|
+
- `@socketsecurity/lib/http-request` — `HttpRequestOptions.body` widened to `Buffer | Readable | string`; `onResponse` hook errors no longer leave promises pending
|
|
167
168
|
|
|
168
169
|
## [5.13.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.13.0) - 2026-04-05
|
|
169
170
|
|
package/dist/archives.js
CHANGED
|
@@ -80,6 +80,16 @@ function validatePathWithinBase(targetPath, baseDir, entryName) {
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
function assertArchiveExists(archivePath) {
|
|
84
|
+
if (!(0, import_node_fs.existsSync)(archivePath)) {
|
|
85
|
+
const err = new Error(
|
|
86
|
+
`ENOENT: no such file or directory, open '${archivePath}'`
|
|
87
|
+
);
|
|
88
|
+
err.code = "ENOENT";
|
|
89
|
+
err.path = archivePath;
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
83
93
|
function detectArchiveFormat(filePath) {
|
|
84
94
|
const lower = filePath.toLowerCase();
|
|
85
95
|
if (lower.endsWith(".tar.gz")) {
|
|
@@ -116,6 +126,7 @@ async function extractArchive(archivePath, outputDir, options = {}) {
|
|
|
116
126
|
}
|
|
117
127
|
}
|
|
118
128
|
async function extractTar(archivePath, outputDir, options = {}) {
|
|
129
|
+
assertArchiveExists(archivePath);
|
|
119
130
|
const {
|
|
120
131
|
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
121
132
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
@@ -207,6 +218,7 @@ async function extractTar(archivePath, outputDir, options = {}) {
|
|
|
207
218
|
}
|
|
208
219
|
}
|
|
209
220
|
async function extractTarGz(archivePath, outputDir, options = {}) {
|
|
221
|
+
assertArchiveExists(archivePath);
|
|
210
222
|
const {
|
|
211
223
|
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
212
224
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
@@ -298,6 +310,7 @@ async function extractTarGz(archivePath, outputDir, options = {}) {
|
|
|
298
310
|
}
|
|
299
311
|
}
|
|
300
312
|
async function extractZip(archivePath, outputDir, options = {}) {
|
|
313
|
+
assertArchiveExists(archivePath);
|
|
301
314
|
const {
|
|
302
315
|
maxEntries = DEFAULT_MAX_ENTRIES,
|
|
303
316
|
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
package/dist/cacache.js
CHANGED
|
@@ -41,17 +41,14 @@ __export(cacache_exports, {
|
|
|
41
41
|
module.exports = __toCommonJS(cacache_exports);
|
|
42
42
|
var import_cacache = __toESM(require("./external/cacache"));
|
|
43
43
|
var import_socket = require("./paths/socket");
|
|
44
|
-
function
|
|
44
|
+
function createPatternMatcher(pattern) {
|
|
45
45
|
if (!pattern.includes("*")) {
|
|
46
|
-
return key.startsWith(pattern);
|
|
46
|
+
return (key) => key.startsWith(pattern);
|
|
47
47
|
}
|
|
48
|
-
const regex = patternToRegex(pattern);
|
|
49
|
-
return regex.test(key);
|
|
50
|
-
}
|
|
51
|
-
function patternToRegex(pattern) {
|
|
52
48
|
const escaped = pattern.replaceAll(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
53
49
|
const regexPattern = escaped.replaceAll("*", ".*");
|
|
54
|
-
|
|
50
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
51
|
+
return (key) => regex.test(key);
|
|
55
52
|
}
|
|
56
53
|
async function clear(options) {
|
|
57
54
|
const opts = { __proto__: null, ...options };
|
|
@@ -84,9 +81,10 @@ async function clear(options) {
|
|
|
84
81
|
return removed2;
|
|
85
82
|
}
|
|
86
83
|
let removed = 0;
|
|
84
|
+
const matches = createPatternMatcher(opts.prefix);
|
|
87
85
|
const stream = cacache2.ls.stream(cacheDir);
|
|
88
86
|
for await (const entry of stream) {
|
|
89
|
-
if (
|
|
87
|
+
if (matches(entry.key)) {
|
|
90
88
|
try {
|
|
91
89
|
await cacache2.rm.entry(cacheDir, entry.key);
|
|
92
90
|
removed++;
|
package/dist/cache-with-ttl.js
CHANGED
|
@@ -84,7 +84,7 @@ function createTtlCache(options) {
|
|
|
84
84
|
}
|
|
85
85
|
const escaped = fullPattern.replaceAll(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
86
86
|
const regexPattern = escaped.replaceAll("*", ".*");
|
|
87
|
-
const regex = new RegExp(`^${regexPattern}
|
|
87
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
88
88
|
return (key) => regex.test(key);
|
|
89
89
|
}
|
|
90
90
|
async function get(key) {
|
package/dist/constants/socket.js
CHANGED
|
@@ -77,7 +77,7 @@ const SOCKET_FIREWALL_APP_NAME = "sfw";
|
|
|
77
77
|
const SOCKET_REGISTRY_APP_NAME = "registry";
|
|
78
78
|
const SOCKET_APP_PREFIX = "_";
|
|
79
79
|
const SOCKET_LIB_NAME = "@socketsecurity/lib";
|
|
80
|
-
const SOCKET_LIB_VERSION = "5.
|
|
80
|
+
const SOCKET_LIB_VERSION = "5.21.0";
|
|
81
81
|
const SOCKET_LIB_URL = "https://github.com/SocketDev/socket-lib";
|
|
82
82
|
const SOCKET_LIB_USER_AGENT = `socketsecurity-lib/${SOCKET_LIB_VERSION} (${SOCKET_LIB_URL})`;
|
|
83
83
|
const SOCKET_IPC_HANDSHAKE = "SOCKET_IPC_HANDSHAKE";
|
package/dist/dlx/detect.js
CHANGED
|
@@ -33,7 +33,20 @@ var import_socket = require("../paths/socket");
|
|
|
33
33
|
let _fs;
|
|
34
34
|
let _path;
|
|
35
35
|
const NODE_JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs"]);
|
|
36
|
+
const PACKAGE_JSON_PATH_CACHE_MAX_SIZE = 200;
|
|
37
|
+
const PACKAGE_JSON_NEGATIVE_TTL_MS = 1e4;
|
|
36
38
|
const packageJsonPathCache = /* @__PURE__ */ new Map();
|
|
39
|
+
function packageJsonPathCacheSet(key, value) {
|
|
40
|
+
if (packageJsonPathCache.has(key)) {
|
|
41
|
+
packageJsonPathCache.delete(key);
|
|
42
|
+
} else if (packageJsonPathCache.size >= PACKAGE_JSON_PATH_CACHE_MAX_SIZE) {
|
|
43
|
+
const oldest = packageJsonPathCache.keys().next().value;
|
|
44
|
+
if (oldest !== void 0) {
|
|
45
|
+
packageJsonPathCache.delete(oldest);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
packageJsonPathCache.set(key, { path: value, at: Date.now() });
|
|
49
|
+
}
|
|
37
50
|
const packageJsonContentCache = /* @__PURE__ */ new Map();
|
|
38
51
|
function findPackageJson(filePath) {
|
|
39
52
|
const fs = /* @__PURE__ */ getFs();
|
|
@@ -41,25 +54,29 @@ function findPackageJson(filePath) {
|
|
|
41
54
|
const startDir = path.dirname(path.resolve(filePath));
|
|
42
55
|
const cached = packageJsonPathCache.get(startDir);
|
|
43
56
|
if (cached !== void 0) {
|
|
44
|
-
if (cached === null) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
if (cached.path === null) {
|
|
58
|
+
if (Date.now() - cached.at < PACKAGE_JSON_NEGATIVE_TTL_MS) {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
packageJsonPathCache.delete(startDir);
|
|
62
|
+
} else if (fs.existsSync(cached.path)) {
|
|
63
|
+
packageJsonPathCacheSet(startDir, cached.path);
|
|
64
|
+
return cached.path;
|
|
65
|
+
} else {
|
|
66
|
+
packageJsonPathCache.delete(startDir);
|
|
49
67
|
}
|
|
50
|
-
packageJsonPathCache.delete(startDir);
|
|
51
68
|
}
|
|
52
69
|
let currentDir = startDir;
|
|
53
70
|
const root = path.parse(currentDir).root;
|
|
54
71
|
while (currentDir !== root) {
|
|
55
72
|
const packageJsonPath = path.join(currentDir, "package.json");
|
|
56
73
|
if (fs.existsSync(packageJsonPath)) {
|
|
57
|
-
|
|
74
|
+
packageJsonPathCacheSet(startDir, packageJsonPath);
|
|
58
75
|
return packageJsonPath;
|
|
59
76
|
}
|
|
60
77
|
currentDir = path.dirname(currentDir);
|
|
61
78
|
}
|
|
62
|
-
|
|
79
|
+
packageJsonPathCacheSet(startDir, null);
|
|
63
80
|
return void 0;
|
|
64
81
|
}
|
|
65
82
|
// @__NO_SIDE_EFFECTS__
|
package/dist/dlx/package.js
CHANGED
|
@@ -62,7 +62,19 @@ const FIREWALL_BLOCK_SEVERITIES = /* @__PURE__ */ new Set([
|
|
|
62
62
|
"critical",
|
|
63
63
|
"high"
|
|
64
64
|
]);
|
|
65
|
+
const BINARY_PATH_CACHE_MAX_SIZE = 200;
|
|
65
66
|
const binaryPathCache = /* @__PURE__ */ new Map();
|
|
67
|
+
function binaryPathCacheSet(key, value) {
|
|
68
|
+
if (binaryPathCache.has(key)) {
|
|
69
|
+
binaryPathCache.delete(key);
|
|
70
|
+
} else if (binaryPathCache.size >= BINARY_PATH_CACHE_MAX_SIZE) {
|
|
71
|
+
const oldest = binaryPathCache.keys().next().value;
|
|
72
|
+
if (oldest !== void 0) {
|
|
73
|
+
binaryPathCache.delete(oldest);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
binaryPathCache.set(key, value);
|
|
77
|
+
}
|
|
66
78
|
async function checkFirewallPurls(arb, requestedPackage) {
|
|
67
79
|
const idealTree = arb.idealTree;
|
|
68
80
|
if (!idealTree) {
|
|
@@ -431,6 +443,7 @@ function resolveBinaryPath(basePath) {
|
|
|
431
443
|
const cached = binaryPathCache.get(basePath);
|
|
432
444
|
if (cached) {
|
|
433
445
|
if (fs.existsSync(cached)) {
|
|
446
|
+
binaryPathCacheSet(basePath, cached);
|
|
434
447
|
return cached;
|
|
435
448
|
}
|
|
436
449
|
binaryPathCache.delete(basePath);
|
|
@@ -439,7 +452,7 @@ function resolveBinaryPath(basePath) {
|
|
|
439
452
|
for (const ext of extensions) {
|
|
440
453
|
const testPath = basePath + ext;
|
|
441
454
|
if (fs.existsSync(testPath)) {
|
|
442
|
-
|
|
455
|
+
binaryPathCacheSet(basePath, testPath);
|
|
443
456
|
return testPath;
|
|
444
457
|
}
|
|
445
458
|
}
|
package/dist/fs.js
CHANGED
|
@@ -167,7 +167,7 @@ async function findUp(name, options) {
|
|
|
167
167
|
let dir = path.resolve(cwd);
|
|
168
168
|
const { root } = path.parse(dir);
|
|
169
169
|
const names = (0, import_arrays.isArray)(name) ? name : [name];
|
|
170
|
-
while (dir
|
|
170
|
+
while (dir) {
|
|
171
171
|
for (const n of names) {
|
|
172
172
|
if (signal?.aborted) {
|
|
173
173
|
return void 0;
|
|
@@ -184,6 +184,9 @@ async function findUp(name, options) {
|
|
|
184
184
|
} catch {
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
+
if (dir === root) {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
187
190
|
dir = path.dirname(dir);
|
|
188
191
|
}
|
|
189
192
|
return void 0;
|
|
@@ -210,7 +213,7 @@ function findUpSync(name, options) {
|
|
|
210
213
|
const { root } = path.parse(dir);
|
|
211
214
|
const stopDir = stopAt ? path.resolve(stopAt) : void 0;
|
|
212
215
|
const names = (0, import_arrays.isArray)(name) ? name : [name];
|
|
213
|
-
while (dir
|
|
216
|
+
while (dir) {
|
|
214
217
|
if (stopDir && dir === stopDir) {
|
|
215
218
|
for (const n of names) {
|
|
216
219
|
const thePath = path.join(dir, n);
|
|
@@ -240,6 +243,9 @@ function findUpSync(name, options) {
|
|
|
240
243
|
} catch {
|
|
241
244
|
}
|
|
242
245
|
}
|
|
246
|
+
if (dir === root) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
243
249
|
dir = path.dirname(dir);
|
|
244
250
|
}
|
|
245
251
|
return void 0;
|
package/dist/globs.js
CHANGED
|
@@ -91,7 +91,11 @@ function getPicomatch() {
|
|
|
91
91
|
function getGlobMatcher(glob2, options) {
|
|
92
92
|
const patterns = Array.isArray(glob2) ? glob2 : [glob2];
|
|
93
93
|
const sortedPatterns = [...patterns].sort();
|
|
94
|
-
const sortedOptions = options ? Object.keys(options).sort().map((k) =>
|
|
94
|
+
const sortedOptions = options ? Object.keys(options).sort().map((k) => {
|
|
95
|
+
const value = options[k];
|
|
96
|
+
const normalized = Array.isArray(value) ? [...value].sort() : value;
|
|
97
|
+
return `${k}:${JSON.stringify(normalized)}`;
|
|
98
|
+
}).join(",") : "";
|
|
95
99
|
const key = `${sortedPatterns.join("|")}:${sortedOptions}`;
|
|
96
100
|
const existing = matcherCache.get(key);
|
|
97
101
|
if (existing) {
|
package/dist/ipc.js
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(ipc_exports);
|
|
|
37
37
|
var import_node_process = __toESM(require("node:process"));
|
|
38
38
|
var import_typebox = require("./external/@sinclair/typebox");
|
|
39
39
|
var import_socket = require("./paths/socket");
|
|
40
|
-
var
|
|
40
|
+
var import_parse = require("./schema/parse");
|
|
41
41
|
const IpcStubSchema = import_typebox.Type.Object({
|
|
42
42
|
/** Process ID that created the stub. */
|
|
43
43
|
pid: import_typebox.Type.Integer({ minimum: 1 }),
|
|
@@ -100,7 +100,7 @@ async function writeIpcStub(appName, data) {
|
|
|
100
100
|
pid: import_node_process.default.pid,
|
|
101
101
|
timestamp: Date.now()
|
|
102
102
|
};
|
|
103
|
-
const validated = (0,
|
|
103
|
+
const validated = (0, import_parse.parseSchema)(IpcStubSchema, ipcData);
|
|
104
104
|
const fs = /* @__PURE__ */ getFs();
|
|
105
105
|
const flags = fs.constants.O_CREAT | fs.constants.O_WRONLY | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW;
|
|
106
106
|
let handle;
|
package/dist/json/parse.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview JSON parsing utilities with Buffer detection and BOM stripping.
|
|
3
|
-
* Provides safe JSON parsing with automatic encoding handling
|
|
3
|
+
* Provides safe JSON parsing with automatic encoding handling, plus
|
|
4
|
+
* `safeJsonParse` for untrusted input (prototype-pollution protection +
|
|
5
|
+
* size limits + optional schema validation).
|
|
4
6
|
*/
|
|
5
|
-
import type {
|
|
7
|
+
import type { Schema } from '../schema/types';
|
|
8
|
+
import type { JsonParseOptions, JsonPrimitive, JsonValue, SafeJsonParseOptions } from './types';
|
|
6
9
|
/**
|
|
7
10
|
* Check if a value is a JSON primitive type.
|
|
8
11
|
* JSON primitives are: `null`, `boolean`, `number`, or `string`.
|
|
@@ -76,3 +79,45 @@ export declare function isJsonPrimitive(value: unknown): value is JsonPrimitive;
|
|
|
76
79
|
* ```
|
|
77
80
|
*/
|
|
78
81
|
export declare function jsonParse(content: string | Buffer, options?: JsonParseOptions | undefined): JsonValue | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Safely parse JSON with optional schema validation and security controls.
|
|
84
|
+
* Throws on parse failure, validation failure, or security violation.
|
|
85
|
+
*
|
|
86
|
+
* Recommended for parsing untrusted JSON (user input, network payloads,
|
|
87
|
+
* anything beyond a trust boundary). Layers:
|
|
88
|
+
* 1. Size cap (default 10 MB) prevents memory exhaustion.
|
|
89
|
+
* 2. Prototype-pollution reviver rejects `__proto__` / `constructor` /
|
|
90
|
+
* `prototype` keys at any depth (unless `allowPrototype: true`).
|
|
91
|
+
* 3. Optional Zod-shaped schema validation via
|
|
92
|
+
* `@socketsecurity/lib/schema/validate`.
|
|
93
|
+
*
|
|
94
|
+
* For trusted-source reads (package.json, local config files), prefer
|
|
95
|
+
* `jsonParse()` — it offers Buffer/BOM handling and filepath-aware error
|
|
96
|
+
* messages, without the untrusted-input overhead.
|
|
97
|
+
*
|
|
98
|
+
* @throws {Error} When `jsonString` exceeds `maxSize`.
|
|
99
|
+
* @throws {Error} When JSON parsing fails.
|
|
100
|
+
* @throws {Error} When prototype-pollution keys are detected (and
|
|
101
|
+
* `allowPrototype` is not `true`).
|
|
102
|
+
* @throws {Error} When schema validation fails.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* // Basic parsing with type inference.
|
|
107
|
+
* const data = safeJsonParse<User>('{"name":"Alice","age":30}')
|
|
108
|
+
*
|
|
109
|
+
* // With schema validation.
|
|
110
|
+
* import { z } from 'zod'
|
|
111
|
+
* const userSchema = z.object({ name: z.string(), age: z.number() })
|
|
112
|
+
* const user = safeJsonParse('{"name":"Alice","age":30}', userSchema)
|
|
113
|
+
*
|
|
114
|
+
* // With size limit.
|
|
115
|
+
* const data = safeJsonParse(jsonString, undefined, { maxSize: 1024 })
|
|
116
|
+
*
|
|
117
|
+
* // Allow prototype keys (DANGEROUS — only for trusted sources).
|
|
118
|
+
* const data = safeJsonParse('{"__proto__":{}}', undefined, {
|
|
119
|
+
* allowPrototype: true,
|
|
120
|
+
* })
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export declare function safeJsonParse<T = unknown>(jsonString: string, schema?: Schema<T> | undefined, options?: SafeJsonParseOptions): T;
|