Skip to content

Commit 36f35cb

Browse files
committed
feat(primordials): add @socketsecurity/lib/primordials export and migrate internals
Add a new public module at `./primordials` exposing safe references to built-in constructors, static methods, and prototype methods — captured at module load time so prototype-pollution attacks on the caller realm can't redirect library internals. The surface (~100 helpers) matches the Node.js-internal primordials convention: - Static methods retain their name: `ObjectKeys`, `ArrayIsArray`, `JSONParse`, `ReflectApply`. - Prototype methods are uncurried via `uncurryThis` so callers write `StringPrototypeSlice(str, 0, 3)` instead of `str.slice(0, 3)`. - Constructors get a `Ctor` suffix (`MapCtor`, `SetCtor`, …) to avoid shadowing the capital-case global. Based on the primordials file socket-packageurl-js has shipped privately; this makes the same surface available fleet-wide so other security-sensitive consumers (registry manifest readers, CLI parsers, SBOM ecosystem detectors) can stop hand-capturing and stop repeating the tsgo-destructuring workaround. Migrate six internal socket-lib files to the new module — strictly a cleanup, no behavior change, -16 net lines: - src/debug.ts, src/logger.ts, src/signal-exit.ts, src/suppress-warnings.ts: replace local `const ReflectApply = Reflect.apply` with `import { ReflectApply } from './primordials'`. - src/errors.ts: switch isErrorShim to use the uncurried `ObjectPrototypeToString(value)` directly — drops the `ReflectApply(ObjectPrototypeToString, value, [])` plumbing. - src/objects.ts: drop ~13 lines of ad-hoc Object.* captures in favour of the centralized imports; `__defineGetter__` stays local (it's deprecated-but-present, not in the primordials surface). Add test/unit/primordials.test.mts (17 tests, 100% pass) covering constructors, Array/Object/String/Reflect/RegExp/Symbol surfaces, the prototype-pollution resilience scenario (`Array.prototype.map` clobbered → captured reference still works), and the `uncurryThis` helper for callers building their own primordials. Bump to 5.25.0.
1 parent f052c38 commit 36f35cb

9 files changed

Lines changed: 632 additions & 44 deletions

File tree

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@socketsecurity/lib",
3-
"version": "5.24.0",
3+
"version": "5.25.0",
44
"packageManager": "pnpm@11.0.0-rc.5",
55
"license": "MIT",
66
"description": "Core utilities and infrastructure for Socket.dev security tools",
@@ -519,6 +519,10 @@
519519
"types": "./dist/performance.d.ts",
520520
"default": "./dist/performance.js"
521521
},
522+
"./primordials": {
523+
"types": "./dist/primordials.d.ts",
524+
"default": "./dist/primordials.js"
525+
},
522526
"./process-lock": {
523527
"types": "./dist/process-lock.d.ts",
524528
"default": "./dist/process-lock.js"

src/debug.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@ import debugJs from './external/debug'
1010

1111
import { getDefaultLogger } from './logger'
1212
import { hasOwn } from './objects'
13+
import { ReflectApply } from './primordials'
1314
import { getDefaultSpinner } from './spinner'
1415
import { applyLinePrefix } from './strings'
1516

16-
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
17-
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
18-
// `exports.SomeName = void 0;` which causes runtime errors.
19-
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
20-
const ReflectApply = Reflect.apply
21-
2217
const logger = getDefaultLogger()
2318

2419
// Type definitions

src/errors.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515

1616
import { UNKNOWN_ERROR } from './constants/core'
1717
import { messageWithCauses, stackWithCauses } from './external/pony-cause'
18+
import { ObjectPrototypeToString } from './primordials'
1819

1920
export { UNKNOWN_ERROR, messageWithCauses, stackWithCauses }
2021

21-
// Capture built-ins so a later monkey-patch can't poison our checks.
22-
const ObjectPrototypeToString = Object.prototype.toString
23-
const ReflectApply = Reflect.apply
24-
2522
/**
2623
* Spec-compliant [`Error.isError`](https://tc39.es/ecma262/#sec-error.iserror)
2724
* with a fallback shim for engines that don't ship it yet.
@@ -53,7 +50,7 @@ export function isErrorShim(value: unknown): value is Error {
5350
if (value === null || typeof value !== 'object') {
5451
return false
5552
}
56-
return ReflectApply(ObjectPrototypeToString, value, []) === '[object Error]'
53+
return ObjectPrototypeToString(value) === '[object Error]'
5754
}
5855

5956
/**

src/logger.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import process from 'node:process'
88
import isUnicodeSupported from './external/@socketregistry/is-unicode-supported'
99
import yoctocolorsCjs from './external/yoctocolors-cjs'
1010

11+
import { ReflectApply, ReflectConstruct } from './primordials'
1112
import { applyLinePrefix, isBlankString } from './strings'
1213
import { getTheme, onThemeChange } from './themes/context'
1314
import { THEMES } from './themes/themes'
@@ -89,12 +90,6 @@ interface Task {
8990
export type { LogSymbols, LoggerMethods, Task }
9091

9192
const globalConsole = console
92-
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
93-
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
94-
// `exports.SomeName = void 0;` which causes runtime errors.
95-
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
96-
const ReflectApply = Reflect.apply
97-
const ReflectConstruct = Reflect.construct
9893

9994
let _Console: typeof import('node:console').Console | undefined
10095
let _consoleSymbols: symbol[] | undefined

src/objects.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ import {
1010
} from './constants/core'
1111

1212
import { isArray } from './arrays'
13+
import {
14+
ObjectDefineProperties,
15+
ObjectDefineProperty,
16+
ObjectFreeze,
17+
ObjectFromEntries,
18+
ObjectGetOwnPropertyDescriptors,
19+
ObjectGetOwnPropertyNames,
20+
ObjectGetPrototypeOf,
21+
ObjectHasOwn,
22+
ObjectKeys,
23+
ObjectPrototype,
24+
ObjectSetPrototypeOf,
25+
ReflectOwnKeys,
26+
} from './primordials'
1327
import { localeCompare } from './sorts'
1428

1529
// Type definitions
@@ -78,32 +92,14 @@ type SortedObject<T> = {
7892

7993
export type { GetterDefObj, LazyGetterStats, ConstantsObjectOptions, Remap }
8094

81-
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
82-
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
83-
// `exports.SomeName = void 0;` which causes runtime errors.
84-
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
85-
const ObjectDefineProperties = Object.defineProperties
86-
const ObjectDefineProperty = Object.defineProperty
87-
const ObjectFreeze = Object.freeze
88-
const ObjectFromEntries = Object.fromEntries
89-
const ObjectGetOwnPropertyDescriptors = Object.getOwnPropertyDescriptors
90-
const ObjectGetOwnPropertyNames = Object.getOwnPropertyNames
91-
const ObjectGetPrototypeOf = Object.getPrototypeOf
92-
const ObjectHasOwn = Object.hasOwn
93-
const ObjectKeys = Object.keys
94-
const ObjectPrototype = Object.prototype
95-
const ObjectSetPrototypeOf = Object.setPrototypeOf
95+
// __defineGetter__ is not in the public primordials surface — it's
96+
// deprecated-but-present on Object.prototype. Capture locally.
9697
// @ts-expect-error - __defineGetter__ exists but not in type definitions.
9798
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
9899
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
99100
// `exports.SomeName = void 0;` which causes runtime errors.
100101
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
101102
const __defineGetter__ = Object.prototype.__defineGetter__
102-
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
103-
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
104-
// `exports.SomeName = void 0;` which causes runtime errors.
105-
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
106-
const ReflectOwnKeys = Reflect.ownKeys
107103

108104
/**
109105
* Create a frozen constants object with lazy getters and internal properties.

0 commit comments

Comments
 (0)