
Research
wget to Wipeout: Malicious Go Modules Fetch Destructive Payload
Socket's research uncovers three dangerous Go modules that contain obfuscated disk-wiping malware, threatening complete data loss.
bare-module-resolve
Advanced tools
Low-level module resolution algorithm for Bare. The algorithm is implemented as a generator function that yields either package manifests to be read or resolution candidates to be tested by the caller. As a convenience, the main export is a synchronous and asynchronous iterable that relies on package manifests being read by a callback. For asynchronous iteration, the callback may return promises which will be awaited before being passed to the generator.
npm i bare-module-resolve
For synchronous resolution:
const resolve = require('bare-module-resolve')
function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for (const resolution of resolve(
'./file.js',
new URL('file:///directory/'),
readPackage
)) {
console.log(resolution)
}
For asynchronous resolution:
const resolve = require('bare-module-resolve')
async function readPackage(url) {
// Read and parse `url` if it exists, otherwise `null`
}
for await (const resolution of resolve(
'./file.js',
new URL('file:///directory/'),
readPackage
)) {
console.log(resolution)
}
const resolver = resolve(specifier, parentURL[, options][, readPackage])
Resolve specifier
relative to parentURL
, which must be a WHATWG URL
instance. readPackage
is called with a URL
instance for every package manifest to be read and must either return the parsed JSON package manifest, if it exists, or null
. If readPackage
returns a promise, synchronous iteration is not supported.
Options include:
options = {
// A default "imports" map to apply to all specifiers. Follows the same
// syntax and rules as the "imports" property defined in `package.json`.
imports,
// A list of builtin module specifiers. If matched, the protocol of the
// resolved URL will be `builtinProtocol`.
builtins: [],
// The protocol to use for resolved builtin module specifiers.
builtinProtocol: 'builtin:',
// The supported import conditions. "default" is always recognized.
conditions: [],
// An array reference which will contain the matched conditions when yielding
// resolutions.
matchedConditions: [],
// The supported engine versions.
engines: {},
// The file extensions to look for. Must be provided to support extensionless
// specifier resolution and directory support, such as resolving './foo' to
// './foo.js' or './foo/index.js'.
extensions: [],
// A map of preresolved imports with keys being serialized parent URLs and
// values being "imports" maps.
resolutions
}
for (const resolution of resolver)
Synchronously iterate the module resolution candidates. The resolved module is the first candidate that exists, either as a file on a file system, a resource at a URL, or something else entirely.
for await (const resolution of resolver)
Asynchronously iterate the module resolution candidates. If readPackage
returns promises, these will be awaited. The same comments as for (const resolution of resolver)
apply.
The following generator functions implement the resolution algorithm, which has been adapted from the Node.js resolution algorithms for CommonJS and ES modules. Unlike Node.js, Bare uses the same resolution algorithm for both module formats. The yielded values have the following shape:
Package manifest
next.value = {
package: URL
}
If the package manifest identified by next.value.package
exists, generator.next()
must be passed the parsed JSON value of the manifest. If it does not exist, pass null
instead.
Resolution candidate
next.value = {
resolution: URL
}
If the module identified by next.value.resolution
exists, generator.next()
may be passed true
to signal that the resolution for the current set of conditions has been identified. If it does not exist, pass false
instead.
To drive the generator functions, a loop like the following can be used:
const generator = resolve.module(specifier, parentURL)
let next = generator.next()
while (next.done !== true) {
const value = next.value
if (value.package) {
// Read and parse `value.package` if it exists, otherwise `null`
let info
next = generator.next(info)
} else {
const resolution = value.resolution
// `true` if `resolution` was the correct candidate, otherwise `false`
let resolved
next = generator.next(resolved)
}
}
Options are the same as resolve()
for all functions.
[!WARNING] These functions are currently subject to change between minor releases. If using them directly, make sure to specify a tilde range (
~1.2.3
) when declaring the module dependency.
const generator = resolve.module(specifier, parentURL[, options])
specifier
starts with a Windows drive letter:
/
to specifier
.options.resolutions
is set:
preresolved(specifier, options.resolutions, parentURL, options)
yields, return.url(specifier, parentURL, options)
yields, return.packageImports(specifier, parentURL, options)
yields, return.specifier
equals .
or ..
, or if specifier
starts with /
, \
, ./
, .\
, ../
, or ..\
:
options.imports
is set:
packageImportsExports(specifier, options.imports, parentURL, true, options)
yields, return.file(specifier, parentURL, false, options)
resolves, return.directory(specifier, parentURL, options)
.package(specifier, parentURL, options)
.const generator = resolve.url(url, parentURL[, options])
url
is not a valid URL, return.options.imports
is set:
packageImportsExports(url.href, options.imports, parentURL, true, options)
yields, return.url.protocol
equals node:
:
specifier
be url.pathname
.specifier
equals .
or ..
, or if specifier
starts with /
, \
, ./
, .\
, ../
, or ..\
, throw.package(specifier, parentURL, options)
.url
.const generator = resolve.preresolved(specifier, resolutions, parentURL[, options])
imports
be resolutions[parentURL]
.imports
is a non-null
object:
packageImportsExports(specifier, imports, parentURL, true, options)
.const generator = resolve.package(packageSpecifier, parentURL[, options])
packageSpecifier
is the empty string, throw.packageSpecifier
does not start with @
:
packageName
to the substring of packageSpecifier
until the first /
or the end of the string.packageName
be undefined
.packageSpecifier
does not include /
, throw.packageName
to the substring of packageSpecifier
until the second /
or the end of the string.packageName
starts with .
or includes \
or %
, throw.builtinTarget(packageSpecifier, null, options.builtins, options)
yields, return.packageSubpath
be .
concatenated with the substring of packageSpecifier
from the position at the length of packageName
.packageSelf(packageName, packageSubpath, parentURL, options)
yields, return.packageURL
be the resolution of node_modules/
concatenated with packageName
and /
relative to parentURL
.parentURL
to the substring of parentURL
until the last /
.info
be the result of yielding the resolution of package.json
relative to packageURL
.info
is not null
:
info.engines
is set:
validateEngines(packageURL, info.engines, options)
.info.exports
is set:
packageExports(packageURL, packageSubpath, info.exports, options)
.packageSubpath
is .
:
info.main
is a non-empty string:
packageSubpath
to info.main
.file('index', packageURL, true, options)
.file(packageSubpath, packageURL, false, options)
resolves, return.directory(packageSubpath, packageURL, options)
.parentURL
is the file system root, return.const generator = resolve.packageSelf(packageName, packageSubpath, parentURL[, options])
packageURL
of lookupPackageScope(parentURL, options)
:
info
be the result of yielding packageURL
.info
is not null
:
info.name
does not equal packageName
, return.info.exports
is set:
packageExports(packageURL, packageSubpath, info.exports, options)
.packageSubpath
is .
:
info.main
is a non-empty string:
packageSubpath
to info.main
.file('index', packageURL, true, options)
.file(packageSubpath, packageURL, false, options)
resolves, return.directory(packageSubpath, packageURL, options)
.const generator = resolve.packageExports(packageURL, subpath, exports[, options])
subpath
is .
:
mainExport
be undefined
.exports
is a string or an array:
mainExport
to exports
.exports
is a non-null
object:
exports
start with .
:
.
is a key of exports
:
mainExport
to exports['.']
.mainExport
to exports
.mainExport
is not undefined
:
packageTarget(packageURL, mainExport, null, false, options)
yields, return.exports
is a non-null
object:
exports
starts with .
:
packageImportsExports(subpath, exports, packageURL, false, options)
yields, return.const generator = resolve.packageImports(specifier, parentURL[, options])
specifier
is #
or starts with #/
, throw.packageURL
of lookupPackageScope(parentURL, opions)
:
info
be the result of yielding packageURL
.info
is not null
:
info.imports
is set:
packageImportsExports(specifier, info.imports, packageURL, true, options)
yields, return.#
, throw.options.imports
is set:
packageImportsExports(url.href, options.imports, parentURL, true, options)
yields, return.const generator = resolve.packageImportsExports(matchKey, matchObject, packageURL, isImports[, options])
matchKey
is a key of matchObject
and matchKey
does not include *
:
target
be matchObject[matchKey]
.packageTarget(packageURL, target, null, isImports, options)
.expansionKeys
be the keys of matchObject
that include *
sorted by patternKeyCompare
.expansionKey
of expansionKeys
:
patternBase
be the substring of expansionKey
until the first *
.matchKey
starts with but isn't equal to patternBase
:
patternTrailer
be the substring of expansionKey
from the position at the index after the first *
.patternTrailer
is the empty string, or if matchKey
ends with patternTrailer
and the length of matchKey
is greater than or equal to the length of expansionKey
:
target
be matchObject[expansionKey]
.patternMatch
be the substring of matchKey
from the position at the length of patternBase
until the length of matchKey
minus the length of patternTrailer
.packageTarget(packageURL, target, patternMatch, isImports, options)
.const generator = resolve.packageTarget(packageURL, target, patternMatch, isImports[, options])
target
is a string:
target
does not start with ./
and isImports
is false
, throw.patternMatch
is not null
:
*
in target
with patternMatch
.url(target, packageURL, options)
yields, return.target
equals .
or ..
, or if target
starts with /
, ./
, or ../
:
target
relative to packageURL
and return.package(target, packageURL, options)
.target
is an array:
targetValue
of target
:
packageTarget(packageURL, targetValue, patternMatch, isImports, options)
yields, return.target
is a non-null
object:
condition
of target
:
condition
equals default
or if options.conditions
includes condition
:
targetValue
be target[condition]
.packageTarget(packageURL, targetValue, patternMatch, isImports, options)
.const generator = resolve.builtinTarget(packageSpecifier, packageVersion, target[, options])
target
is a string:
target
does not start with @
:
targetName
be the substring of target
until the first @
or the end of the string.targetVersion
be the substring of target
from the character following the first @
and to the end of string, or null
if no such substring exists.targetName
be the substring of target
until the second @
or the end of the string.targetVersion
be the substring of target
from the character following the second @
and to the end of string, or null
if no such substring exists.packageSpecifier
equals targetName
:
packageVersion
is null
and targetVersion
is null
:
options.builtinProtocol
concatenated with packageSpecifier
and return.version
be null
.packageVersion
is null
, let version
be targetVersion
.targetVersion
is either null
or equals packageVersion
, let version
be packageVersion
version
is not null
:
options.builtinProtocol
concatenated with packageSpecifier
, @
, and version
and return.target
is an array:
targetValue
of target
:
builtinTarget(packageSpecifier, packageVersion, targetValue, options)
yields, return.target
is a non-null
object:
condition
of target
:
condition
equals default
or if options.conditions
includes condition
:
targetValue
be target[condition]
.builtinTarget(packageSpecifier, packageVersion, targetValue, options)
.const generator = resolve.file(filename, parentURL, isIndex[, options])
filename
equals .
or ..
, or if filename
ends with /
or \
, return.parentURL
is a file:
URL and filename
includes encoded /
or \
, throw.isIndex
is false
:
filename
relative to parentURL
.ext
of options.extensions
:
filename
concatenated with ext
relative to parentURL
.const generator = resolve.directory(dirname, parentURL[, options])
directoryURL
be undefined
.dirname
ends with /
or \
:
directoryURL
to the resolution of dirname
relative to parentURL
.directoryURL
to the resolution of dirname
concatenated with /
relative to parentURL
.info
be the result of yielding the resolution of package.json
relative to directoryURL
.info
is not null
:
info.exports
is set:
packageExports(directoryURL, '.', info.exports, options)
.info.main
is a non-empty string:
file(info.main, directoryURL, false, options)
resolves, return.directory(info.main, directoryURL, options)
.file('index', directoryURL, true, options)
.Apache-2.0
FAQs
Low-level module resolution algorithm for Bare
The npm package bare-module-resolve receives a total of 82,828 weekly downloads. As such, bare-module-resolve popularity was classified as popular.
We found that bare-module-resolve demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket's research uncovers three dangerous Go modules that contain obfuscated disk-wiping malware, threatening complete data loss.
Research
Socket uncovers malicious packages on PyPI using Gmail's SMTP protocol for command and control (C2) to exfiltrate data and execute commands.
Product
We redesigned Socket's first logged-in page to display rich and insightful visualizations about your repositories protected against supply chain threats.