Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

lavamoat-core

Package Overview
Dependencies
Maintainers
6
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lavamoat-core - npm Package Compare versions

Comparing version 14.4.1 to 15.0.0

.depcheckrc.yml

24

CHANGELOG.md
# Changelog
## [15.0.0](https://github.com/LavaMoat/LavaMoat/compare/lavamoat-core-v14.4.1...lavamoat-core-v15.0.0) (2023-10-18)
### ⚠ BREAKING CHANGES
* The minimum supported Node.js version is now v16.20.0.
### Features
* **core:** add overrideTaming: 'severe' for improved developer experience under lavamoat ([#730](https://github.com/LavaMoat/LavaMoat/issues/730)) ([20e4f76](https://github.com/LavaMoat/LavaMoat/commit/20e4f764dfdabcf21c7e72ad45fcfeaf45fd2b6c))
* node20 ([ef3a0da](https://github.com/LavaMoat/LavaMoat/commit/ef3a0da9960d7f5734e3d4180ebafdae2432a260))
### Bug Fixes
* drop Node.js v14 ([#729](https://github.com/LavaMoat/LavaMoat/issues/729)) ([10c667b](https://github.com/LavaMoat/LavaMoat/commit/10c667bd88eaabf60a8fd8e4493cc7676848b201))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* lavamoat-tofu bumped from ^6.2.1 to ^7.0.0
## [14.4.1](https://github.com/LavaMoat/LavaMoat/compare/lavamoat-core-v14.4.0...lavamoat-core-v14.4.1) (2023-09-14)

@@ -4,0 +28,0 @@

12

lib/strict-scope-terminator.js

@@ -38,6 +38,6 @@ // import {

assert.fail(
d`Please report unexpected scope handler trap: ${q(String(prop))}`,
d`Please report unexpected scope handler trap: ${q(String(prop))}`
)
},
}),
})
)

@@ -81,3 +81,3 @@

`getOwnPropertyDescriptor trap on scopeTerminatorHandler for ${quotedProp}`,
new TypeError().stack,
new TypeError().stack
)

@@ -94,4 +94,4 @@ return undefined

alwaysThrowHandler,
getOwnPropertyDescriptors(scopeProxyHandlerProperties),
),
getOwnPropertyDescriptors(scopeProxyHandlerProperties)
)
)

@@ -101,3 +101,3 @@

immutableObject,
strictScopeTerminatorHandler,
strictScopeTerminatorHandler
)

@@ -104,0 +104,0 @@

{
"name": "lavamoat-core",
"version": "14.4.1",
"version": "15.0.0",
"description": "LavaMoat kernel and utils",

@@ -10,7 +10,7 @@ "main": "src/index.js",

"engines": {
"node": ">=14.0.0 <19.0.0"
"node": "^16.20.0 || ^18.0.0 || ^20.0.0"
},
"dependencies": {
"json-stable-stringify": "^1.0.2",
"lavamoat-tofu": "^6.2.1",
"lavamoat-tofu": "^7.0.0",
"merge-deep": "^3.0.3"

@@ -39,4 +39,3 @@ },

"timeout": "30s"
},
"gitHead": "5fb4a8824b4100150f891fabf17448a77de48498"
}
}
# lavamoat-core
LavaMoat kernel and utils.
LavaMoat kernel and utils.

@@ -10,3 +10,5 @@ // the contents of this file will be copied into the prelude template

function endowmentsToolkit({ createFunctionWrapper = defaultCreateFunctionWrapper } = {}) {
function endowmentsToolkit({
createFunctionWrapper = defaultCreateFunctionWrapper,
} = {}) {
return {

@@ -31,3 +33,8 @@ getEndowmentsForConfig,

*/
function getEndowmentsForConfig(sourceRef, packagePolicy, unwrapTo, unwrapFrom) {
function getEndowmentsForConfig(
sourceRef,
packagePolicy,
unwrapTo,
unwrapFrom
) {
if (!packagePolicy.globals) {

@@ -39,30 +46,58 @@ return {}

const explicitlyBanned = []
Object.entries(packagePolicy.globals).forEach(([path, packagePolicyValue]) => {
const pathParts = path.split('.')
// disallow dunder proto in path
const pathContainsDunderProto = pathParts.some(pathPart => pathPart === '__proto__')
if (pathContainsDunderProto) {
throw new Error(`Lavamoat - "__proto__" disallowed when creating minimal view. saw "${path}"`)
Object.entries(packagePolicy.globals).forEach(
([path, packagePolicyValue]) => {
const pathParts = path.split('.')
// disallow dunder proto in path
const pathContainsDunderProto = pathParts.some(
(pathPart) => pathPart === '__proto__'
)
if (pathContainsDunderProto) {
throw new Error(
`Lavamoat - "__proto__" disallowed when creating minimal view. saw "${path}"`
)
}
// false means no access. It's necessary so that overrides can also be used to tighten the policy
if (packagePolicyValue === false) {
explicitlyBanned.push(path)
return
}
// write access handled elsewhere
if (packagePolicyValue === 'write') {
return
}
if (packagePolicyValue !== true) {
throw new Error(
`LavaMoat - unrecognizable policy value (${typeof packagePolicyValue}) for path "${path}"`
)
}
whitelistedReads.push(path)
}
// false means no access. It's necessary so that overrides can also be used to tighten the policy
if (packagePolicyValue === false) {
explicitlyBanned.push(path)
return
}
// write access handled elsewhere
if (packagePolicyValue === 'write') {
return
}
if (packagePolicyValue !== true) {
throw new Error(`LavaMoat - unrecognizable policy value (${typeof packagePolicyValue}) for path "${path}"`)
}
whitelistedReads.push(path)
})
return makeMinimalViewOfRef(sourceRef, whitelistedReads, unwrapTo, unwrapFrom, explicitlyBanned)
)
return makeMinimalViewOfRef(
sourceRef,
whitelistedReads,
unwrapTo,
unwrapFrom,
explicitlyBanned
)
}
function makeMinimalViewOfRef(sourceRef, paths, unwrapTo, unwrapFrom, explicitlyBanned = []) {
function makeMinimalViewOfRef(
sourceRef,
paths,
unwrapTo,
unwrapFrom,
explicitlyBanned = []
) {
const targetRef = {}
paths.forEach(path => {
copyValueAtPath('', path.split('.'), explicitlyBanned, sourceRef, targetRef, unwrapTo, unwrapFrom)
paths.forEach((path) => {
copyValueAtPath(
'',
path.split('.'),
explicitlyBanned,
sourceRef,
targetRef,
unwrapTo,
unwrapFrom
)
})

@@ -79,3 +114,11 @@ return targetRef

function copyValueAtPath(visitedPath, pathParts, explicitlyBanned, sourceRef, targetRef, unwrapTo = sourceRef, unwrapFrom = targetRef) {
function copyValueAtPath(
visitedPath,
pathParts,
explicitlyBanned,
sourceRef,
targetRef,
unwrapTo = sourceRef,
unwrapFrom = targetRef
) {
if (pathParts.length === 0) {

@@ -87,3 +130,6 @@ throw new Error('unable to copy, must have pathParts, was empty')

// get the property from any depth in the property chain
const { prop: sourcePropDesc } = getPropertyDescriptorDeep(sourceRef, nextPart)
const { prop: sourcePropDesc } = getPropertyDescriptorDeep(
sourceRef,
nextPart
)

@@ -100,3 +146,5 @@ // if source missing the value to copy, just skip it

if (!('value' in targetPropDesc)) {
throw new Error(`unable to copy on to targetRef, targetRef has a getter at "${nextPart}"`)
throw new Error(
`unable to copy on to targetRef, targetRef has a getter at "${nextPart}"`
)
}

@@ -107,3 +155,5 @@ // value must be extensible (cant write properties onto it)

if (valueType !== 'object' && valueType !== 'function') {
throw new Error(`unable to copy on to targetRef, targetRef value is not an obj or func "${nextPart}"`)
throw new Error(
`unable to copy on to targetRef, targetRef value is not an obj or func "${nextPart}"`
)
}

@@ -135,3 +185,9 @@ }

}
copyValueAtPath(currentPath, remainingParts, explicitlyBanned, nextSourceRef, nextTargetRef)
copyValueAtPath(
currentPath,
remainingParts,
explicitlyBanned,
nextSourceRef,
nextTargetRef
)
return

@@ -150,3 +206,7 @@ }

// wrapper setter/getter with correct receiver
const wrapperPropDesc = applyGetSetPropDescTransforms(sourcePropDesc, unwrapFrom, unwrapTo)
const wrapperPropDesc = applyGetSetPropDescTransforms(
sourcePropDesc,
unwrapFrom,
unwrapTo
)
Reflect.defineProperty(targetRef, nextPart, wrapperPropDesc)

@@ -166,3 +226,3 @@ return

// otherwise add workaround for functions to swap back to the sourceal "this" reference
const unwrapTest = thisValue => thisValue === unwrapFrom
const unwrapTest = (thisValue) => thisValue === unwrapFrom
const newValue = createFunctionWrapper(sourceValue, unwrapTest, unwrapTo)

@@ -189,3 +249,5 @@ const newPropDesc = {

} else {
throw new Error('getEndowmentsForConfig - property descriptor missing a getter')
throw new Error(
'getEndowmentsForConfig - property descriptor missing a getter'
)
}

@@ -196,10 +258,26 @@ return { sourceValue, sourceWritable }

function applyEndowmentPropDescTransforms(propDesc, unwrapFromCompartmentGlobalThis, unwrapToGlobalThis) {
function applyEndowmentPropDescTransforms(
propDesc,
unwrapFromCompartmentGlobalThis,
unwrapToGlobalThis
) {
let newPropDesc = propDesc
newPropDesc = applyFunctionPropDescTransform(newPropDesc, unwrapFromCompartmentGlobalThis, unwrapToGlobalThis)
newPropDesc = applyGetSetPropDescTransforms(newPropDesc, unwrapFromCompartmentGlobalThis, unwrapToGlobalThis)
newPropDesc = applyFunctionPropDescTransform(
newPropDesc,
unwrapFromCompartmentGlobalThis,
unwrapToGlobalThis
)
newPropDesc = applyGetSetPropDescTransforms(
newPropDesc,
unwrapFromCompartmentGlobalThis,
unwrapToGlobalThis
)
return newPropDesc
}
function applyGetSetPropDescTransforms(sourcePropDesc, unwrapFromGlobalThis, unwrapToGlobalThis) {
function applyGetSetPropDescTransforms(
sourcePropDesc,
unwrapFromGlobalThis,
unwrapToGlobalThis
) {
const wrappedPropDesc = { ...sourcePropDesc }

@@ -210,3 +288,4 @@ if (sourcePropDesc.get) {

// replace the "receiver" value if it points to fake parent
const receiverRef = receiver === unwrapFromGlobalThis ? unwrapToGlobalThis : receiver
const receiverRef =
receiver === unwrapFromGlobalThis ? unwrapToGlobalThis : receiver
// sometimes getters replace themselves with static properties, as seen wih the FireFox runtime

@@ -221,3 +300,7 @@ const result = Reflect.apply(sourcePropDesc.get, receiverRef, [])

// "getter.originalValue" property being available
return createFunctionWrapper(result, (thisValue) => thisValue === unwrapFromGlobalThis, unwrapToGlobalThis)
return createFunctionWrapper(
result,
(thisValue) => thisValue === unwrapFromGlobalThis,
unwrapToGlobalThis
)
} else {

@@ -232,3 +315,4 @@ return result

const receiver = this
const receiverRef = receiver === unwrapFromGlobalThis ? unwrapToGlobalThis : receiver
const receiverRef =
receiver === unwrapFromGlobalThis ? unwrapToGlobalThis : receiver
return Reflect.apply(sourcePropDesc.set, receiverRef, [value])

@@ -240,3 +324,7 @@ }

function applyFunctionPropDescTransform(propDesc, unwrapFromCompartmentGlobalThis, unwrapToGlobalThis) {
function applyFunctionPropDescTransform(
propDesc,
unwrapFromCompartmentGlobalThis,
unwrapToGlobalThis
) {
if (!('value' in propDesc && typeof propDesc.value === 'function')) {

@@ -251,7 +339,10 @@ return propDesc

}
const newFn = createFunctionWrapper(propDesc.value, unwrapTest, unwrapToGlobalThis)
const newFn = createFunctionWrapper(
propDesc.value,
unwrapTest,
unwrapToGlobalThis
)
return { ...propDesc, value: newFn }
}
function getPropertyDescriptorDeep(target, key) {

@@ -282,3 +373,7 @@ let receiver = target

function copyWrappedGlobals(globalRef, target, globalThisRefs = ['globalThis']) {
function copyWrappedGlobals(
globalRef,
target,
globalThisRefs = ['globalThis']
) {
// find the relevant endowment sources

@@ -288,6 +383,10 @@ const globalProtoChain = getPrototypeChain(globalRef)

// this should always be the last index, but we check just in case
const commonPrototypeIndex = globalProtoChain.findIndex(globalProtoChainEntry => globalProtoChainEntry === Object.prototype)
const commonPrototypeIndex = globalProtoChain.findIndex(
(globalProtoChainEntry) => globalProtoChainEntry === Object.prototype
)
if (commonPrototypeIndex === -1) {
// TODO: fix this error message
throw new Error('Lavamoat - unable to find common prototype between Compartment and globalRef')
throw new Error(
'Lavamoat - unable to find common prototype between Compartment and globalRef'
)
}

@@ -299,5 +398,5 @@ // we will copy endowments from all entries in the prototype chain, excluding Object.prototype

// call on contents of endowmentsSources directly instead of in new array instances. If there is a lazy getter it only changes the original prop desc.
endowmentSources.forEach(source => {
endowmentSources.forEach((source) => {
const descriptors = Object.getOwnPropertyDescriptors(source)
Object.values(descriptors).forEach(desc => {
Object.values(descriptors).forEach((desc) => {
if ('get' in desc) {

@@ -307,3 +406,3 @@ try {

Reflect.apply(desc.get, globalRef, [])
} catch { }
} catch {}
}

@@ -313,5 +412,11 @@ })

const endowmentSourceDescriptors = endowmentSources.map(globalProtoChainEntry => Object.getOwnPropertyDescriptors(globalProtoChainEntry))
const endowmentSourceDescriptors = endowmentSources.map(
(globalProtoChainEntry) =>
Object.getOwnPropertyDescriptors(globalProtoChainEntry)
)
// flatten propDesc collections with precedence for globalThis-end of the prototype chain
const endowmentDescriptorsFlat = Object.assign(Object.create(null), ...endowmentSourceDescriptors.reverse())
const endowmentDescriptorsFlat = Object.assign(
Object.create(null),
...endowmentSourceDescriptors.reverse()
)
// expose all own properties of globalRef, including non-enumerable

@@ -322,7 +427,11 @@ Object.entries(endowmentDescriptorsFlat)

// ignore circular globalThis refs
.filter(([key]) => !(globalThisRefs.includes(key)))
.filter(([key]) => !globalThisRefs.includes(key))
// define property on compartment global
.forEach(([key, desc]) => {
// unwrap functions, setters/getters & apply scope proxy workaround
const wrappedPropDesc = applyEndowmentPropDescTransforms(desc, target, globalRef)
const wrappedPropDesc = applyEndowmentPropDescTransforms(
desc,
target,
globalRef
)
Reflect.defineProperty(target, key, wrappedPropDesc)

@@ -346,3 +455,6 @@ })

let current = value
while (current && (typeof current === 'object' || typeof current === 'function')) {
while (
current &&
(typeof current === 'object' || typeof current === 'function')
) {
protoChain.push(current)

@@ -367,4 +479,7 @@ current = Reflect.getPrototypeOf(current)

}
Object.defineProperties(newValue, Object.getOwnPropertyDescriptors(sourceValue))
Object.defineProperties(
newValue,
Object.getOwnPropertyDescriptors(sourceValue)
)
return newValue
}

@@ -9,8 +9,26 @@ // The "prelude" is the kernel of a browserify bundle. It initializes the modules and

const path = require('path')
const kernelTemplate = fs.readFileSync(path.join(__dirname, '/kernelTemplate.js'), 'utf-8')
const kernelCoreTemplate = fs.readFileSync(path.join(__dirname, '/kernelCoreTemplate.js'), 'utf-8')
const sesSrc = fs.readFileSync(path.join(__dirname, '/../lib/lockdown.umd.js'), 'utf-8')
const endowmentsToolkitSrc = fs.readFileSync(path.join(__dirname, '/endowmentsToolkit.js'), 'utf-8')
const makePrepareRealmGlobalFromConfigSrc = fs.readFileSync(path.join(__dirname, '/makePrepareRealmGlobalFromConfig.js'), 'utf-8')
const strictScopeTerminatorSrc = fs.readFileSync(path.join(__dirname, '/../lib/strict-scope-terminator.js'), 'utf-8')
const kernelTemplate = fs.readFileSync(
path.join(__dirname, '/kernelTemplate.js'),
'utf-8'
)
const kernelCoreTemplate = fs.readFileSync(
path.join(__dirname, '/kernelCoreTemplate.js'),
'utf-8'
)
const sesSrc = fs.readFileSync(
path.join(__dirname, '/../lib/lockdown.umd.js'),
'utf-8'
)
const endowmentsToolkitSrc = fs.readFileSync(
path.join(__dirname, '/endowmentsToolkit.js'),
'utf-8'
)
const makePrepareRealmGlobalFromConfigSrc = fs.readFileSync(
path.join(__dirname, '/makePrepareRealmGlobalFromConfig.js'),
'utf-8'
)
const strictScopeTerminatorSrc = fs.readFileSync(
path.join(__dirname, '/../lib/strict-scope-terminator.js'),
'utf-8'
)

@@ -25,7 +43,7 @@ module.exports = {

function getSesShimSrc () {
function getSesShimSrc() {
return sesSrc
}
function getStrictScopeTerminatorShimSrc () {
function getStrictScopeTerminatorShimSrc() {
return strictScopeTerminatorSrc

@@ -35,3 +53,3 @@ }

// takes the kernelTemplate and populates it with the libraries
function generateKernel (_opts = {}) {
function generateKernel(_opts = {}) {
const opts = Object.assign({}, _opts)

@@ -43,3 +61,7 @@ const kernelCode = generateKernelCore()

output = stringReplace(output, '__createKernelCore__', kernelCode)
output = stringReplace(output, '__lavamoatDebugOptions__', JSON.stringify({debugMode: !!opts.debugMode}))
output = stringReplace(
output,
'__lavamoatDebugOptions__',
JSON.stringify({ debugMode: !!opts.debugMode })
)
// eslint-disable-next-line no-prototype-builtins

@@ -51,8 +73,11 @@ if (opts?.hasOwnProperty('scuttleGlobalThis')) {

if (opts.scuttleGlobalThisExceptions) {
console.warn('Lavamoat - "scuttleGlobalThisExceptions" is deprecated. Use "scuttleGlobalThis.exceptions" instead.')
console.warn(
'Lavamoat - "scuttleGlobalThisExceptions" is deprecated. Use "scuttleGlobalThis.exceptions" instead.'
)
if (scuttleGlobalThis === true) {
scuttleGlobalThis = {enabled: true}
scuttleGlobalThis = { enabled: true }
}
}
const exceptions = scuttleGlobalThis?.exceptions || opts.scuttleGlobalThisExceptions
const exceptions =
scuttleGlobalThis?.exceptions || opts.scuttleGlobalThisExceptions
scuttleGlobalThis.exceptions = exceptions

@@ -65,5 +90,9 @@ if (exceptions) {

}
output = stringReplace(output, '__lavamoatSecurityOptions__', JSON.stringify({
scuttleGlobalThis,
}))
output = stringReplace(
output,
'__lavamoatSecurityOptions__',
JSON.stringify({
scuttleGlobalThis,
})
)
}

@@ -75,11 +104,23 @@

// takes the kernelCoreTemplate and populates it with the libraries
function generateKernelCore () {
function generateKernelCore() {
let output = kernelCoreTemplate
output = replaceTemplateRequire(output, 'endowmentsToolkit', endowmentsToolkitSrc)
output = replaceTemplateRequire(output, 'makePrepareRealmGlobalFromConfig', makePrepareRealmGlobalFromConfigSrc)
output = replaceTemplateRequire(output, 'strict-scope-terminator', strictScopeTerminatorSrc)
output = replaceTemplateRequire(
output,
'endowmentsToolkit',
endowmentsToolkitSrc
)
output = replaceTemplateRequire(
output,
'makePrepareRealmGlobalFromConfig',
makePrepareRealmGlobalFromConfigSrc
)
output = replaceTemplateRequire(
output,
'strict-scope-terminator',
strictScopeTerminatorSrc
)
return output
}
function replaceTemplateRequire (code, moduleName, src) {
function replaceTemplateRequire(code, moduleName, src) {
const wrappedSrc = wrapWithReturnCjsExports(moduleName, src)

@@ -91,8 +132,9 @@ code = stringReplace(code, `templateRequire('${moduleName}')`, wrappedSrc)

// this wraps the content of a commonjs module with an IIFE that returns the module.exports
function wrapWithReturnCjsExports (label, src) {
function wrapWithReturnCjsExports(label, src) {
if (String(label).includes('\n')) {
throw new Error('Lavamoat - "wrapWithReturnCjsExports" does not allow labels with newlines')
throw new Error(
'Lavamoat - "wrapWithReturnCjsExports" does not allow labels with newlines'
)
}
return (
`// define ${label}
return `// define ${label}
(function(){

@@ -109,3 +151,2 @@ const global = globalRef

})()`
)
}

@@ -115,4 +156,4 @@

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter
function stringReplace (src, target, replacement) {
function stringReplace(src, target, replacement) {
return src.split(target).join(replacement)
}

@@ -10,3 +10,7 @@ const EventEmitter = require('events')

codeSampleFromAstNode,
utils: { mergePolicy: mergeGlobalsPolicy, mapToObj, reduceToTopmostApiCallsFromStrings },
utils: {
mergePolicy: mergeGlobalsPolicy,
mapToObj,
reduceToTopmostApiCallsFromStrings,
},
} = require('lavamoat-tofu')

@@ -19,3 +23,3 @@ const { mergePolicy } = require('./mergePolicy')

function createModuleInspector (opts = {}) {
function createModuleInspector(opts = {}) {
const moduleIdToModuleRecord = new Map()

@@ -39,3 +43,6 @@ // "packageToModules" does not include builtin modules

function inspectModule (moduleRecord, { isBuiltin, includeDebugInfo = false } = {}) {
function inspectModule(
moduleRecord,
{ isBuiltin, includeDebugInfo = false } = {}
) {
const { packageName, specifier, type } = moduleRecord

@@ -66,3 +73,3 @@ // record the module

// eslint-disable-next-line no-unused-vars
function inspectBuiltinModule (moduleRecord) {
function inspectBuiltinModule(moduleRecord) {
// builtins themselves do not require any configuration

@@ -72,3 +79,3 @@ // packages that import builtins need to add that to their configuration

function inspectNativeModule (moduleRecord) {
function inspectNativeModule(moduleRecord) {
// LavaMoat does attempt to sandbox native modules

@@ -84,3 +91,6 @@ // packages with native modules need to specify that in the policy file

function inspectJsModule (moduleRecord, { isBuiltin, includeDebugInfo = false }) {
function inspectJsModule(
moduleRecord,
{ isBuiltin, includeDebugInfo = false }
) {
const { packageName, specifier } = moduleRecord

@@ -116,9 +126,11 @@ let moduleDebug

// get ast (parse or use cached)
const ast = moduleRecord.ast || parse(moduleRecord.content, {
// esm support
sourceType: 'module',
// someone must have been doing this
allowReturnOutsideFunction: true,
errorRecovery: true,
})
const ast =
moduleRecord.ast ||
parse(moduleRecord.content, {
// esm support
sourceType: 'module',
// someone must have been doing this
allowReturnOutsideFunction: true,
errorRecovery: true,
})
if (includeDebugInfo && ast.errors && ast.errors.length) {

@@ -132,3 +144,9 @@ moduleDebug.parseErrors = ast.errors

// get builtin package usage
inspectForImports(ast, moduleRecord, packageName, isBuiltin, includeDebugInfo)
inspectForImports(
ast,
moduleRecord,
packageName,
isBuiltin,
includeDebugInfo
)
// ensure module ast is cleaned up

@@ -138,7 +156,11 @@ delete moduleRecord.ast

function inspectForEnvironment (ast, moduleRecord, includeDebugInfo) {
function inspectForEnvironment(ast, moduleRecord, includeDebugInfo) {
const { packageName } = moduleRecord
const compatWarnings = inspectSesCompat(ast, packageName)
const { primordialMutations, strictModeViolations, dynamicRequires } = compatWarnings
const hasResults = primordialMutations.length > 0 || strictModeViolations.length > 0 || dynamicRequires.length > 0
const { primordialMutations, strictModeViolations, dynamicRequires } =
compatWarnings
const hasResults =
primordialMutations.length > 0 ||
strictModeViolations.length > 0 ||
dynamicRequires.length > 0
if (!hasResults) {

@@ -152,5 +174,11 @@ return

// fix serialization
primordialMutations: primordialMutations.map(({ node: { loc } }) => ({ node: { loc } })),
strictModeViolations: strictModeViolations.map(({ node: { loc } }) => ({ node: { loc } })),
dynamicRequires: dynamicRequires.map(({ node: { loc } }) => ({ node: { loc } })),
primordialMutations: primordialMutations.map(({ node: { loc } }) => ({
node: { loc },
})),
strictModeViolations: strictModeViolations.map(({ node: { loc } }) => ({
node: { loc },
})),
dynamicRequires: dynamicRequires.map(({ node: { loc } }) => ({
node: { loc },
})),
}

@@ -163,5 +191,11 @@ } else {

const samples = jsonStringify({
primordialMutations: primordialMutations.map(({ node }) => codeSampleFromAstNode(node, moduleRecord)),
strictModeViolations: strictModeViolations.map(({ node }) => codeSampleFromAstNode(node, moduleRecord)),
dynamicRequires: dynamicRequires.map(({ node }) => codeSampleFromAstNode(node, moduleRecord)),
primordialMutations: primordialMutations.map(({ node }) =>
codeSampleFromAstNode(node, moduleRecord)
),
strictModeViolations: strictModeViolations.map(({ node }) =>
codeSampleFromAstNode(node, moduleRecord)
),
dynamicRequires: dynamicRequires.map(({ node }) =>
codeSampleFromAstNode(node, moduleRecord)
),
})

@@ -174,3 +208,3 @@ const errMsg = `Incompatible code detected in package "${packageName}" file "${moduleRecord.file}". Violations:\n${samples}`

function inspectForGlobals (ast, moduleRecord, packageName, includeDebugInfo) {
function inspectForGlobals(ast, moduleRecord, packageName, includeDebugInfo) {
const commonJsRefs = ['require', 'module', 'exports', 'arguments']

@@ -202,3 +236,9 @@ const globalObjPrototypeRefs = Object.getOwnPropertyNames(Object.prototype)

function inspectForImports (ast, moduleRecord, packageName, isBuiltin, includeDebugInfo) {
function inspectForImports(
ast,
moduleRecord,
packageName,
isBuiltin,
includeDebugInfo
) {
// get all requested names that resolve to isBuiltin

@@ -226,3 +266,3 @@ const namesForBuiltins = Object.entries(moduleRecord.importMap)

function generatePolicy ({ policyOverride, includeDebugInfo } = {}) {
function generatePolicy({ policyOverride, includeDebugInfo } = {}) {
const resources = {}

@@ -239,5 +279,10 @@ const policy = { resources }

// get dependencies, ignoring builtins
const packageDeps = aggregateDeps({ packageModules, moduleIdToModuleRecord })
const packageDeps = aggregateDeps({
packageModules,
moduleIdToModuleRecord,
})
if (packageDeps.length) {
packages = Object.fromEntries(packageDeps.map(depPackageName => [depPackageName, true]))
packages = Object.fromEntries(
packageDeps.map((depPackageName) => [depPackageName, true])
)
}

@@ -249,3 +294,3 @@ // get globals

// read/write syntax highlighting
Object.keys(globals).forEach(key => {
Object.keys(globals).forEach((key) => {
if (globals[key] === 'read') {

@@ -260,3 +305,3 @@ globals[key] = true

builtin = {}
reduceToTopmostApiCallsFromStrings(builtinImports).forEach(path => {
reduceToTopmostApiCallsFromStrings(builtinImports).forEach((path) => {
builtin[path] = true

@@ -300,25 +345,27 @@ })

function aggregateDeps ({ packageModules, moduleIdToModuleRecord }) {
function aggregateDeps({ packageModules, moduleIdToModuleRecord }) {
const deps = new Set()
// get all dep package from the "packageModules" collection of modules
Object.values(packageModules).forEach((moduleRecord) => {
Object.entries(moduleRecord.importMap).forEach(([requestedName, specifier]) => {
// skip entries where resolution was skipped
if (!specifier) {
return
}
// get packageName from module record, or guess
const moduleRecord = moduleIdToModuleRecord.get(specifier)
if (moduleRecord) {
// builtin modules are ignored here, handled elsewhere
if (moduleRecord.type === 'builtin') {
Object.entries(moduleRecord.importMap).forEach(
([requestedName, specifier]) => {
// skip entries where resolution was skipped
if (!specifier) {
return
}
deps.add(moduleRecord.packageName)
return
// get packageName from module record, or guess
const moduleRecord = moduleIdToModuleRecord.get(specifier)
if (moduleRecord) {
// builtin modules are ignored here, handled elsewhere
if (moduleRecord.type === 'builtin') {
return
}
deps.add(moduleRecord.packageName)
return
}
// moduleRecord missing, guess package name
const packageName = guessPackageName(requestedName)
deps.add(packageName)
}
// moduleRecord missing, guess package name
const packageName = guessPackageName(requestedName)
deps.add(packageName)
})
)
// ensure the package is not listed as its own dependency

@@ -333,4 +380,5 @@ deps.delete(moduleRecord.packageName)

// for when you encounter a requestedName that was not inspected, likely because resolution was skipped for that module
function guessPackageName (requestedName) {
const isNotPackageName = requestedName.startsWith('/') || requestedName.startsWith('.')
function guessPackageName(requestedName) {
const isNotPackageName =
requestedName.startsWith('/') || requestedName.startsWith('.')
if (isNotPackageName) {

@@ -347,3 +395,3 @@ return `<unknown:${requestedName}>`

function getDefaultPaths (policyName) {
function getDefaultPaths(policyName) {
const policiesDir = 'lavamoat'

@@ -350,0 +398,0 @@ const policyDir = path.join(policiesDir, policyName)

@@ -1,4 +0,2 @@

const {
generateKernel,
} = require('./generateKernel')
const { generateKernel } = require('./generateKernel')
const { createModuleInspector, getDefaultPaths } = require('./generatePolicy')

@@ -5,0 +3,0 @@ const { parseForPolicy } = require('./parseForPolicy')

@@ -63,2 +63,4 @@ // LavaMoat Prelude

stackFiltering: 'verbose',
// prevents most common override mistake cases from tripping up users
overrideTaming: 'severe',
}

@@ -65,0 +67,0 @@

@@ -7,3 +7,3 @@ const fs = require('fs')

async function readPolicyFile ({ debugMode, policyPath }) {
async function readPolicyFile({ debugMode, policyPath }) {
if (debugMode) {

@@ -16,6 +16,6 @@ console.warn(`Lavamoat looking for policy at'${policyPath}'`)

async function loadPolicy ({ debugMode, policyPath }) {
async function loadPolicy({ debugMode, policyPath }) {
let policy = { resources: {} }
if (fs.existsSync(policyPath)) {
policy = readPolicyFile ({ debugMode, policyPath })
policy = readPolicyFile({ debugMode, policyPath })
} else {

@@ -29,3 +29,7 @@ if (debugMode) {

async function loadPolicyAndApplyOverrides({ debugMode, policyPath, policyOverridePath }) {
async function loadPolicyAndApplyOverrides({
debugMode,
policyPath,
policyOverridePath,
}) {
const policy = await loadPolicy({ debugMode, policyPath })

@@ -37,3 +41,6 @@ let lavamoatPolicy = policy

}
const policyOverride = await readPolicyFile({ debugMode, policyPath: policyOverridePath })
const policyOverride = await readPolicyFile({
debugMode,
policyPath: policyOverridePath,
})
lavamoatPolicy = mergePolicy(policy, policyOverride)

@@ -40,0 +47,0 @@ // TODO: Only write if merge results in changes.

@@ -6,3 +6,3 @@ module.exports = makeGeneralUtils

*/
function makeGeneralUtils () {
function makeGeneralUtils() {
return {

@@ -12,3 +12,3 @@ createFunctionWrapper,

function createFunctionWrapper (sourceValue, unwrapTest, unwrapTo) {
function createFunctionWrapper(sourceValue, unwrapTest, unwrapTo) {
const newValue = function (...args) {

@@ -25,5 +25,8 @@ if (new.target) {

}
Object.defineProperties(newValue, Object.getOwnPropertyDescriptors(sourceValue))
Object.defineProperties(
newValue,
Object.getOwnPropertyDescriptors(sourceValue)
)
return newValue
}
}
module.exports = { makeInitStatsHook }
function makeInitStatsHook ({ onStatsReady }) {
function makeInitStatsHook({ onStatsReady }) {
let statModuleStack = []
return reportStatsHook
function reportStatsHook (event, moduleId) {
function reportStatsHook(event, moduleId) {
if (event === 'start') {

@@ -13,7 +13,7 @@ // record start

const statRecord = {
'name': moduleId,
'value': null,
'children': [],
'startTime': startTime,
'endTime': null,
name: moduleId,
value: null,
children: [],
startTime: startTime,
endTime: null,
}

@@ -32,3 +32,7 @@ // add as child to current

if (currentStat.name !== moduleId) {
console.error(`stats hook misaligned "${currentStat.name}", "${moduleId}" ${statModuleStack.map(e => e.name).join()}`)
console.error(
`stats hook misaligned "${
currentStat.name
}", "${moduleId}" ${statModuleStack.map((e) => e.name).join()}`
)
}

@@ -48,3 +52,2 @@ currentStat.endTime = endTime

}
}

@@ -10,3 +10,3 @@ // the contents of this file will be copied into the prelude template

function makePrepareRealmGlobalFromConfig ({ createFunctionWrapper }) {
function makePrepareRealmGlobalFromConfig({ createFunctionWrapper }) {
return {

@@ -18,5 +18,10 @@ prepareCompartmentGlobalFromConfig,

function getTopLevelReadAccessFromPackageConfig (globalsConfig) {
function getTopLevelReadAccessFromPackageConfig(globalsConfig) {
const result = Object.entries(globalsConfig)
.filter(([key, value]) => value === 'read' || value === true || (value === 'write' && key.split('.').length > 1))
.filter(
([key, value]) =>
value === 'read' ||
value === true ||
(value === 'write' && key.split('.').length > 1)
)
.map(([key]) => key.split('.')[0])

@@ -27,5 +32,7 @@ // return unique array

function getTopLevelWriteAccessFromPackageConfig (globalsConfig) {
function getTopLevelWriteAccessFromPackageConfig(globalsConfig) {
const result = Object.entries(globalsConfig)
.filter(([key, value]) => value === 'write' && key.split('.').length === 1)
.filter(
([key, value]) => value === 'write' && key.split('.').length === 1
)
.map(([key]) => key)

@@ -35,7 +42,15 @@ return result

function prepareCompartmentGlobalFromConfig (packageCompartment, globalsConfig, endowments, globalStore, globalThisRefs) {
function prepareCompartmentGlobalFromConfig(
packageCompartment,
globalsConfig,
endowments,
globalStore,
globalThisRefs
) {
const packageCompartmentGlobal = packageCompartment.globalThis
// lookup top level read + write access keys
const topLevelWriteAccessKeys = getTopLevelWriteAccessFromPackageConfig(globalsConfig)
const topLevelReadAccessKeys = getTopLevelReadAccessFromPackageConfig(globalsConfig)
const topLevelWriteAccessKeys =
getTopLevelWriteAccessFromPackageConfig(globalsConfig)
const topLevelReadAccessKeys =
getTopLevelReadAccessFromPackageConfig(globalsConfig)

@@ -50,5 +65,5 @@ // NOTE: getters for read should only ever be needed on props marked for 'write' (unless we want to allow sloppy behavior from the root compartment modifying everything...)

// allow read access via globalStore or packageCompartmentGlobal
topLevelReadAccessKeys.forEach(key => {
topLevelReadAccessKeys.forEach((key) => {
Object.defineProperty(packageCompartmentGlobal, key, {
get () {
get() {
if (globalStore.has(key)) {

@@ -60,5 +75,7 @@ return globalStore.get(key)

},
set () {
set() {
// TODO: there should be a config to throw vs silently ignore
console.warn(`LavaMoat: ignoring write attempt to read-access global "${key}"`)
console.warn(
`LavaMoat: ignoring write attempt to read-access global "${key}"`
)
},

@@ -70,5 +87,5 @@ })

// read access via globalStore or packageCompartmentGlobal
topLevelWriteAccessKeys.forEach(key => {
topLevelWriteAccessKeys.forEach((key) => {
Object.defineProperty(packageCompartmentGlobal, key, {
get () {
get() {
if (globalStore.has(key)) {

@@ -80,3 +97,3 @@ return globalStore.get(key)

},
set (value) {
set(value) {
globalStore.set(key, value)

@@ -90,3 +107,3 @@ },

// set circular globalRefs
globalThisRefs.forEach(key => {
globalThisRefs.forEach((key) => {
// if globalRef is actually an endowment, ignore

@@ -108,8 +125,11 @@ if (topLevelReadAccessKeys.includes(key)) {

const fn = origFunction(...args)
const unwrapTest = thisValue => thisValue === undefined
const unwrapTest = (thisValue) => thisValue === undefined
return createFunctionWrapper(fn, unwrapTest, packageCompartmentGlobal)
}
Object.defineProperties(newFunction, Object.getOwnPropertyDescriptors(origFunction))
Object.defineProperties(
newFunction,
Object.getOwnPropertyDescriptors(origFunction)
)
packageCompartmentGlobal.Function = newFunction
}
}

@@ -1,2 +0,6 @@

const { reduceToTopmostApiCalls, objToMap, mapToObj } = require('lavamoat-tofu/src/util')
const {
reduceToTopmostApiCalls,
objToMap,
mapToObj,
} = require('lavamoat-tofu/src/util')
const mergeDeep = require('merge-deep')

@@ -6,3 +10,3 @@

function mergePolicy (policyA, policyB) {
function mergePolicy(policyA, policyB) {
const mergedPolicy = mergeDeep(policyA, policyB)

@@ -20,3 +24,3 @@ Object.values(mergedPolicy.resources).forEach((packagePolicy) => {

function dedupePolicyPaths (packagePolicy) {
function dedupePolicyPaths(packagePolicy) {
const itemMap = objToMap(packagePolicy)

@@ -23,0 +27,0 @@ reduceToTopmostApiCalls(itemMap)

@@ -1,4 +0,3 @@

class LavamoatModuleRecord {
constructor ({
constructor({
specifier,

@@ -5,0 +4,0 @@ file,

@@ -17,3 +17,3 @@ const { createModuleInspector } = require('./generatePolicy')

*/
async function parseForPolicy ({
async function parseForPolicy({
moduleSpecifier,

@@ -27,3 +27,7 @@ importHook,

}) {
for await (const moduleRecord of eachNodeInTree({ moduleSpecifier, importHook, shouldImport })) {
for await (const moduleRecord of eachNodeInTree({
moduleSpecifier,
importHook,
shouldImport,
})) {
// inspect each module

@@ -30,0 +34,0 @@ inspector.inspectModule(moduleRecord)

@@ -1,4 +0,8 @@

const { applyTransforms, evadeHtmlCommentTest, evadeImportExpressionTest } = require('../lib/transforms.umd.js')
const {
applyTransforms,
evadeHtmlCommentTest,
evadeImportExpressionTest,
} = require('../lib/transforms.umd.js')
function applySourceTransforms (source) {
function applySourceTransforms(source) {
return applyTransforms(source, [

@@ -11,3 +15,3 @@ evadeHtmlCommentTest,

function evadeDirectEvalExpressions (source) {
function evadeDirectEvalExpressions(source) {
/* eslint-disable-next-line prefer-regex-literals */

@@ -14,0 +18,0 @@ const someDirectEvalPattern = new RegExp('\\beval(\\s*\\()', 'g')

@@ -15,3 +15,3 @@ // specifier = exact unique module name

*/
async function walk ({
async function walk({
moduleSpecifier,

@@ -44,3 +44,3 @@ importHook,

// NOTE: i think this is depth first in a way that doesnt take advantage of concurrency
async function * eachNodeInTree ({
async function* eachNodeInTree({
moduleSpecifier,

@@ -47,0 +47,0 @@ importHook,

@@ -12,3 +12,4 @@ /* eslint-disable n/prefer-global/buffer */

defineOne: () => {
let abc = null; let xyz = null
let abc = null
let xyz = null
try {

@@ -85,3 +86,4 @@ abc = require('abc')

// this test ensures "Buffer.prototype.slice" is copied in a way that allows "this" to be overridden
module.exports.overrideCheck = (buf) => Buffer.prototype.slice.call(buf, 1, 2)[0] === buf[1]
module.exports.overrideCheck = (buf) =>
Buffer.prototype.slice.call(buf, 1, 2)[0] === buf[1]
// this test ensures "Buffer.prototype.slice" is copied in a way that allows "this" to be overridden

@@ -125,5 +127,7 @@ module.exports.thisCheck = () => thisChecker.check()

thisChecker: (() => {
const parent = {}; parent.check = function () {
const parent = {}
parent.check = function () {
return this === parent
}; return parent
}
return parent
})(),

@@ -130,0 +134,0 @@ someClass: { SomeClass: class SomeClass {} },

const test = require('ava')
const {
createScenarioFromScaffold,
runScenario,
} = require('./util')
const { createScenarioFromScaffold, runScenario } = require('./util')

@@ -7,0 +4,0 @@ test('circularDeps - multi-module circular deps dont inf loop', async (t) => {

@@ -37,18 +37,38 @@ const test = require('ava')

{
const sourceProp = Object.getOwnPropertyDescriptor(sourceGlobal.Buffer, 'from')
const resultProp = Object.getOwnPropertyDescriptor(resultGlobal.Buffer, 'from')
const sourceProp = Object.getOwnPropertyDescriptor(
sourceGlobal.Buffer,
'from'
)
const resultProp = Object.getOwnPropertyDescriptor(
resultGlobal.Buffer,
'from'
)
t.is(typeof resultProp.value, 'function')
t.deepEqual(resultProp, {
...sourceProp,
value: resultProp.value,
}, 'prop descriptor matches (except value)')
t.deepEqual(
resultProp,
{
...sourceProp,
value: resultProp.value,
},
'prop descriptor matches (except value)'
)
}
{
const sourceProp = Object.getOwnPropertyDescriptor(sourceGlobal.Buffer, 'isBuffer')
const resultProp = Object.getOwnPropertyDescriptor(resultGlobal.Buffer, 'isBuffer')
const sourceProp = Object.getOwnPropertyDescriptor(
sourceGlobal.Buffer,
'isBuffer'
)
const resultProp = Object.getOwnPropertyDescriptor(
resultGlobal.Buffer,
'isBuffer'
)
t.is(typeof resultProp.value, 'function')
t.deepEqual(resultProp, {
...sourceProp,
value: resultProp.value,
}, 'prop descriptor matches (except value)')
t.deepEqual(
resultProp,
{
...sourceProp,
value: resultProp.value,
},
'prop descriptor matches (except value)'
)
}

@@ -59,5 +79,7 @@ })

const getEndowmentsForConfig = prepareTest()
const sourceGlobal = { get abc () {
return { xyz: 42 }
} }
const sourceGlobal = {
get abc() {
return { xyz: 42 }
},
}
const config = {

@@ -74,8 +96,12 @@ globals: {

const { enumerable, configurable } = sourceProp
t.deepEqual(resultProp, {
enumerable,
configurable,
value: resultProp.value,
writable: true,
}, 'prop descriptor matches (except value)')
t.deepEqual(
resultProp,
{
enumerable,
configurable,
value: resultProp.value,
writable: true,
},
'prop descriptor matches (except value)'
)
}

@@ -151,3 +177,8 @@ })

}
const resultGlobal = getEndowmentsForConfig(sourceGlobal, config, unwrapTo, unwrapFrom)
const resultGlobal = getEndowmentsForConfig(
sourceGlobal,
config,
unwrapTo,
unwrapFrom
)
const getter = Reflect.getOwnPropertyDescriptor(resultGlobal, 'xyz').get

@@ -170,3 +201,3 @@

abc: function () {
return this
return this
},

@@ -196,4 +227,4 @@ }

const sourceGlobal = {
setTimeout () {
return this
setTimeout() {
return this
},

@@ -200,0 +231,0 @@ }

@@ -11,17 +11,24 @@ /* eslint-disable no-undef, no-unused-vars, no-unused-expressions, no-extend-native */

t.deepEqual(config, {
resources: {
test: {
globals: {
'location.href': true,
t.deepEqual(
config,
{
resources: {
test: {
globals: {
'location.href': true,
},
},
},
},
}, 'config matched expected')
'config matched expected'
)
})
test('generatePolicy - config with debugInfo', async (t) => {
const config = await createConfigForTest(function () {
location.href
}, { includeDebugInfo: true })
const config = await createConfigForTest(
function () {
location.href
},
{ includeDebugInfo: true }
)

@@ -31,16 +38,21 @@ const testModuleFile = './node_modules/test/index.js'

t.deepEqual(testPackageConfigDebugInfo, {
moduleRecord: {
specifier: testModuleFile,
file: testModuleFile,
type: 'js',
content: '(function () {\n location.href\n })()',
importMap: {},
packageName: 'test',
moduleInitializer: undefined,
t.deepEqual(
testPackageConfigDebugInfo,
{
moduleRecord: {
specifier: testModuleFile,
file: testModuleFile,
type: 'js',
// this is brittle
content: '(function () {\n location.href\n })()',
importMap: {},
packageName: 'test',
moduleInitializer: undefined,
},
globals: {
'location.href': 'read',
},
},
globals: {
'location.href': 'read',
},
}, 'config matched expected')
'config matched expected'
)
})

@@ -56,11 +68,15 @@

t.deepEqual(config, {
resources: {
test: {
globals: {
nonIgnoredGlobal: true,
t.deepEqual(
config,
{
resources: {
test: {
globals: {
nonIgnoredGlobal: true,
},
},
},
},
}, 'config matched expected')
'config matched expected'
)
})

@@ -74,12 +90,16 @@

t.deepEqual(config, {
resources: {
test: {
globals: {
'location.href': true,
XMLHttpRequest: true,
t.deepEqual(
config,
{
resources: {
test: {
globals: {
'location.href': true,
XMLHttpRequest: true,
},
},
},
},
}, 'config matches expected')
'config matches expected'
)
})

@@ -92,5 +112,9 @@

t.deepEqual(config, {
resources: {},
}, 'config matches expected')
t.deepEqual(
config,
{
resources: {},
},
'config matches expected'
)
})

@@ -103,5 +127,9 @@

t.deepEqual(config, {
resources: {},
}, 'config matches expected')
t.deepEqual(
config,
{
resources: {},
},
'config matches expected'
)
})

@@ -114,5 +142,9 @@

t.deepEqual(config, {
resources: {},
}, 'config matches expected')
t.deepEqual(
config,
{
resources: {},
},
'config matches expected'
)
})

@@ -119,0 +151,0 @@

const test = require('ava')
const {
createScenarioFromScaffold,
runScenario,
} = require('./util')
const { createScenarioFromScaffold, runScenario } = require('./util')

@@ -21,3 +18,3 @@ test('globals - ensure global property this-value unwrapped', async (t) => {

return (this === scenario.globalThis || this === scenario.vmContext)
return this === scenario.globalThis || this === scenario.vmContext
},

@@ -36,6 +33,10 @@ },

const testResult = await runScenario({ scenario })
t.deepEqual(testResult, {
direct: true,
indirect: false,
}, 'expected result, did not error')
t.deepEqual(
testResult,
{
direct: true,
indirect: false,
},
'expected result, did not error'
)
})

@@ -64,3 +65,5 @@

// chrome: Uncaught TypeError: Illegal invocation
throw new TypeError('\'get document\' called on an object that does not implement interface Window')
throw new TypeError(
"'get document' called on an object that does not implement interface Window"
)
}

@@ -93,3 +96,2 @@ return {

test('globals - ensure circular refs on package compartment global', async (t) => {

@@ -130,6 +132,8 @@ const scenario = createScenarioFromScaffold({

context: {
setTimeout () {
setTimeout() {
if (this !== scenario.globalThis && this !== scenario.vmContext) {
// chrome: Uncaught TypeError: Illegal invocation
throw new TypeError('\'setTimeout\' called on an object that does not implement interface Window')
throw new TypeError(
"'setTimeout' called on an object that does not implement interface Window"
)
}

@@ -166,3 +170,3 @@ },

abc: function () {
return this
return this
},

@@ -181,9 +185,13 @@ },

const testResult = await runScenario({ scenario })
t.deepEqual(testResult, {
typeof: true,
isUndefined: true,
isTrue: true,
is42: true,
isXyz: true,
}, 'expected result, did not error')
t.deepEqual(
testResult,
{
typeof: true,
isUndefined: true,
isTrue: true,
is42: true,
isXyz: true,
},
'expected result, did not error'
)
})

@@ -207,3 +215,3 @@

globals: {
'abc': true,
abc: true,
},

@@ -226,3 +234,3 @@ },

abc: function () {
return 42
return 42
},

@@ -234,3 +242,3 @@ },

globals: {
'abc': false,
abc: false,
},

@@ -241,3 +249,5 @@ },

})
await t.throwsAsync(runScenario({ scenario }), { message: 'globalThis.abc is not a function' })
await t.throwsAsync(runScenario({ scenario }), {
message: 'globalThis.abc is not a function',
})
})

@@ -253,3 +263,3 @@

abc: function () {
return 42
return 42
},

@@ -261,3 +271,3 @@ },

globals: {
'abc': true,
abc: true,
},

@@ -271,3 +281,3 @@ },

globals: {
'abc': false,
abc: false,
},

@@ -278,3 +288,5 @@ },

})
await t.throwsAsync(runScenario({ scenario }), { message: 'globalThis.abc is not a function' })
await t.throwsAsync(runScenario({ scenario }), {
message: 'globalThis.abc is not a function',
})
})

@@ -287,3 +299,3 @@

context: {
a: { ok: 42, b: { c: () => 42, notOk:41 }},
a: { ok: 42, b: { c: () => 42, notOk: 41 } },
},

@@ -294,5 +306,5 @@ config: {

globals: {
'a': true,
a: true,
'a.b': false,
'a.b.c':true,
'a.b.c': true,
},

@@ -320,3 +332,2 @@ },

t.is(testResult.a_b_notOk, false)
})

@@ -328,4 +339,4 @@

context: {
a: { b: { c: () => 42, notOk:41 }},
x: { notOk:41, y: () => 42 },
a: { b: { c: () => 42, notOk: 41 } },
x: { notOk: 41, y: () => 42 },
},

@@ -337,4 +348,4 @@ config: {

'a.b': false,
'a.b.c':true,
'x': false,
'a.b.c': true,
x: false,
'x.y': true,

@@ -372,3 +383,3 @@ },

context: {
a: { ok: 42, b: { c: () => 42, notOk:41 }},
a: { ok: 42, b: { c: () => 42, notOk: 41 } },
},

@@ -379,3 +390,3 @@ config: {

globals: {
'a': true,
a: true,
'a.b': false,

@@ -415,3 +426,3 @@ },

function exportLazyGetter(object, prop, getter) {
let redefine = value => {
let redefine = (value) => {
if (value === undefined) {

@@ -437,7 +448,7 @@ delete object[prop]

get: function() {
get: function () {
return redefine(getter.call(this))
},
set: function(value) {
set: function (value) {
redefine(value)

@@ -453,3 +464,3 @@ },

globals: {
'chrome': true,
chrome: true,
},

@@ -456,0 +467,0 @@ },

@@ -5,94 +5,104 @@ /* eslint-disable no-undef, no-unused-vars, no-unused-expressions, no-extend-native */

testMerge('merge with resources', {
resources: {
babel: {
globals: {
abc: true,
xyz: false,
'a.b.c': true,
testMerge(
'merge with resources',
{
resources: {
babel: {
globals: {
abc: true,
xyz: false,
'a.b.c': true,
},
builtin: {
derp: true,
qwerty: false,
},
},
builtin: {
derp: true,
qwerty: false,
},
},
},
}, {
resources: {
babel: {
globals: {
def: true,
ghi: false,
'a.b': true,
{
resources: {
babel: {
globals: {
def: true,
ghi: false,
'a.b': true,
},
builtin: {
derp: true,
qwerty: false,
},
},
builtin: {
derp: true,
qwerty: false,
},
},
},
}, {
resources: {
babel: {
globals: {
abc: true,
xyz: false,
def: true,
ghi: false,
'a.b': true,
{
resources: {
babel: {
globals: {
abc: true,
xyz: false,
def: true,
ghi: false,
'a.b': true,
},
builtin: {
derp: true,
qwerty: false,
},
},
builtin: {
derp: true,
qwerty: false,
},
},
},
})
}
)
testMerge('overrides to disallow', {
resources: {
babel: {
globals: {
abc: true,
xyz: false,
'a.b': true,
'q.w.e': true,
testMerge(
'overrides to disallow',
{
resources: {
babel: {
globals: {
abc: true,
xyz: false,
'a.b': true,
'q.w.e': true,
},
builtin: {
derp: true,
qwerty: false,
},
},
builtin: {
derp: true,
qwerty: false,
},
},
},
}, {
resources: {
babel: {
globals: {
abc: false,
'a.b.c': false, // this is not supported
'q.w': false,
{
resources: {
babel: {
globals: {
abc: false,
'a.b.c': false, // this is not supported
'q.w': false,
},
builtin: {
derp: false,
},
},
builtin: {
derp: false,
},
},
},
}, {
resources: {
babel: {
builtin: {
derp: false,
qwerty: false,
{
resources: {
babel: {
builtin: {
derp: false,
qwerty: false,
},
globals: {
'a.b': true,
abc: false,
'q.w': false,
xyz: false,
},
},
globals: {
'a.b': true,
abc: false,
'q.w': false,
xyz: false,
},
},
},
})
}
)
function testMerge (label, configA, configB, expectedResultObj) {
function testMerge(label, configA, configB, expectedResultObj) {
test(label, (t) => {

@@ -99,0 +109,0 @@ const result = mergePolicy(configA, configB)

@@ -8,3 +8,5 @@ const test = require('ava')

console.log(`Running Core Scenario: ${scenario.name}`)
await runAndTestScenario(t, scenario, ({ scenario }) => runScenario({ scenario }))
await runAndTestScenario(t, scenario, ({ scenario }) =>
runScenario({ scenario })
)
}

@@ -16,4 +18,6 @@ })

console.log(`Running Core Scenario: ${scenario.name}`)
await runAndTestScenario(t, scenario, ({ scenario }) => runScenario({ scenario, runWithPrecompiledModules: true }))
await runAndTestScenario(t, scenario, ({ scenario }) =>
runScenario({ scenario, runWithPrecompiledModules: true })
)
}
})

@@ -1,2 +0,5 @@

const { createScenarioFromScaffold, autoConfigForScenario } = require('../util.js')
const {
createScenarioFromScaffold,
autoConfigForScenario,
} = require('../util.js')

@@ -3,0 +6,0 @@ module.exports = [

@@ -12,3 +12,3 @@ const { createScenarioFromScaffold } = require('../util.js')

module.exports = function (n) {
return n * 111
return n * 111
}

@@ -26,3 +26,3 @@ },

// https://github.com/feross/buffer/blob/795bbb5bda1b39f1370ebd784bea6107b087e3a7/index.js#L611
function Buffer (_arg, _encodingOrOffset, _length) {}
function Buffer(_arg, _encodingOrOffset, _length) {}
Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype)

@@ -43,4 +43,4 @@ Object.setPrototypeOf(Buffer, Uint8Array)

const abc = new Function('this.value = this.derp()')
abc.prototype.derp = function() {
return 123
abc.prototype.derp = function () {
return 123
}

@@ -47,0 +47,0 @@ const xyz = new abc()

@@ -221,3 +221,3 @@ const { createScenarioFromScaffold } = require('../util.js')

resources: {
'$root$': {
$root$: {
packages: {

@@ -241,3 +241,3 @@ two: true,

resources: {
'$root$': {
$root$: {
packages: {

@@ -244,0 +244,0 @@ two: true,

@@ -66,3 +66,3 @@ const { createScenarioFromScaffold } = require('../util.js')

t.is(isRecent(result.non), true)
function isRecent (time) {
function isRecent(time) {
// more recent than 2020-01-01T00:00:00.000Z

@@ -69,0 +69,0 @@ return time > 1577836800000

@@ -10,10 +10,10 @@ const { createScenarioFromScaffold } = require('../util.js')

try {
testResults.objCheckThis = this.Object === Object
} catch (_) { }
testResults.objCheckThis = this.Object === Object
} catch (_) {}
try {
testResults.objCheckGlobal = globalThis.Object === Object
} catch (_) { }
testResults.objCheckGlobal = globalThis.Object === Object
} catch (_) {}
try {
testResults.thisIsExports = exports === this
} catch (_) { }
testResults.thisIsExports = exports === this
} catch (_) {}
module.exports = testResults

@@ -55,3 +55,7 @@ },

// test webpack result against globalThis
module.exports = { match: g === globalThis, type: typeof g, error: error && error.message }
module.exports = {
match: g === globalThis,
type: typeof g,
error: error && error.message,
}
},

@@ -58,0 +62,0 @@ expectedResult: { match: true, type: 'object' },

@@ -28,3 +28,3 @@ const autogen = require('./autogen')

async function * loadScenarios () {
async function* loadScenarios() {
for (const scenarioCreator of scenarios) {

@@ -31,0 +31,0 @@ yield await scenarioCreator()

@@ -56,3 +56,3 @@ const { createScenarioFromScaffold } = require('../util.js')

class ModernClass {
constructor () {
constructor() {
this.abc = 123

@@ -78,3 +78,3 @@ }

class NewClass extends BaseClass {
constructor () {
constructor() {
super()

@@ -91,3 +91,3 @@ this.abc = 456

class BaseClass {
constructor () {
constructor() {
this.abc = 123

@@ -190,3 +190,3 @@ }

resources: {
'$root$': {
$root$: {
packages: {

@@ -193,0 +193,0 @@ one: true,

@@ -1,2 +0,5 @@

const { createScenarioFromScaffold, autoConfigForScenario } = require('../util.js')
const {
createScenarioFromScaffold,
autoConfigForScenario,
} = require('../util.js')

@@ -23,3 +26,15 @@ const one = () => {

enabled: true,
exceptions: ['WebAssembly', 'process', '/[0-9]+/', 'Set', 'Reflect', 'Object', 'console', 'Array', 'RegExp', 'Date', 'Math'],
exceptions: [
'WebAssembly',
'process',
'/[0-9]+/',
'Set',
'Reflect',
'Object',
'console',
'Array',
'RegExp',
'Date',
'Math',
],
},

@@ -38,7 +53,12 @@ },

enabled: true,
exceptions: ['WebAssembly', 'process', '/[0-9]+/' /*'Set', 'Reflect', 'Object', 'console', 'Array', 'RegExp', 'Date', 'Math'*/],
exceptions: [
'WebAssembly',
'process',
'/[0-9]+/' /*'Set', 'Reflect', 'Object', 'console', 'Array', 'RegExp', 'Date', 'Math'*/,
],
},
},
expectedFailure: true,
expectedFailureMessageRegex: /SES_UNHANDLED_REJECTION|inaccessible under scuttling mode./,
expectedFailureMessageRegex:
/SES_UNHANDLED_REJECTION|inaccessible under scuttling mode./,
})

@@ -45,0 +65,0 @@ await autoConfigForScenario({ scenario })

@@ -9,11 +9,14 @@ const { createScenarioFromScaffold } = require('../util.js')

require('two')
module.exports = { objectXyz: 'xyz' in Object, protoXyz: 'xyz' in Object.prototype }
module.exports = {
objectXyz: 'xyz' in Object,
protoXyz: 'xyz' in Object.prototype,
}
},
defineTwo: () => {
try {
Object.xyz = 123
} catch (_) { }
Object.xyz = 123
} catch (_) {}
try {
Object.protoype.xyz = 123
} catch (_) { }
Object.protoype.xyz = 123
} catch (_) {}
},

@@ -32,3 +35,3 @@ expectedResult: { objectXyz: false, protoXyz: false },

try {
module.exports = setTimeout
module.exports = setTimeout
} catch (_) {}

@@ -35,0 +38,0 @@ },

@@ -30,6 +30,9 @@ const { createScenarioFromScaffold } = require('../util.js')

defineTwo: () => {
function SubError () {}
function SubError() {}
_inheritsLoose(SubError, TypeError)
/* eslint-disable */
function _inheritsLoose (t, e) { t.prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e }
function _inheritsLoose(t, e) {
;(t.prototype = Object.create(e.prototype)),
((t.prototype.constructor = t).__proto__ = e)
}
module.exports = SubError

@@ -48,10 +51,13 @@ /* eslint-enable */

const SuperClass = require('two')
function SubClass () {}
function SubClass() {}
_inheritsLoose(SubClass, SuperClass)
module.exports = { SubClass }
function _inheritsLoose (t, e) { t.prototype = Object.create(e.prototype), (t.prototype.constructor = t).__proto__ = e }
function _inheritsLoose(t, e) {
;(t.prototype = Object.create(e.prototype)),
((t.prototype.constructor = t).__proto__ = e)
}
/* eslint-enable */
},
defineTwo: () => {
function SuperClass () {}
function SuperClass() {}
module.exports = SuperClass

@@ -58,0 +64,0 @@ },

@@ -1,2 +0,7 @@

const { parseForPolicy, LavamoatModuleRecord, generateKernel, getDefaultPaths } = require('../src/index.js')
const {
parseForPolicy,
LavamoatModuleRecord,
generateKernel,
getDefaultPaths,
} = require('../src/index.js')
const mergeDeep = require('merge-deep')

@@ -25,10 +30,14 @@ const { runInContext, createContext } = require('vm')

async function generatePolicyFromFiles ({ files, ...opts }) {
async function generatePolicyFromFiles({ files, ...opts }) {
const config = await parseForPolicy({
moduleSpecifier: files.find(file => file.entry).specifier,
moduleSpecifier: files.find((file) => file.entry).specifier,
resolveHook: (requestedName, parentAddress) => {
return files.find(file => file.specifier === parentAddress).importMap[requestedName]
return files.find((file) => file.specifier === parentAddress).importMap[
requestedName
]
},
importHook: async (address) => {
return new LavamoatModuleRecord(files.find(file => file.specifier === address))
return new LavamoatModuleRecord(
files.find((file) => file.specifier === address)
)
},

@@ -43,3 +52,3 @@ isBuiltin: () => false,

function createScenarioFromScaffold ({
function createScenarioFromScaffold({
name = 'template scenario',

@@ -60,7 +69,11 @@ expectedResult = {

t.truthy(err, `Scenario fails as expected: ${scenario.name} - ${err}`)
t.regex(err.message, scenario.expectedFailureMessageRegex, 'Error message expects to match regex')
t.regex(
err.message,
scenario.expectedFailureMessageRegex,
'Error message expects to match regex'
)
} else {
if (err) {
t.fail(`Unexpected error in scenario: ${scenario.name} - ${err}`)
throw (err)
throw err
}

@@ -71,7 +84,14 @@ }

if (scenario.testType === 'truthy') {
t.assert(result, `${scenario.name} - scenario gives expected truthy result`)
t.assert(
result,
`${scenario.name} - scenario gives expected truthy result`
)
} else if (scenario.testType === 'falsy') {
t.falsy(result, `${scenario.name} - scenario gives expected falsy result`)
} else {
t.deepEqual(result, scenario.expectedResult, `${scenario.name} - scenario gives expected result`)
t.deepEqual(
result,
scenario.expectedResult,
`${scenario.name} - scenario gives expected result`
)
}

@@ -84,3 +104,3 @@ },

context = {},
opts = {scuttleGlobalThis: {}},
opts = { scuttleGlobalThis: {} },
config,

@@ -96,3 +116,3 @@ configOverride,

} = {}) {
function _defineEntry () {
function _defineEntry() {
const testResult = require('one')

@@ -102,7 +122,7 @@ console.log(JSON.stringify(testResult, null, 2))

function _defineOne () {
function _defineOne() {
module.exports = require('two')
}
function _defineTwo () {
function _defineTwo() {
module.exports = {

@@ -113,3 +133,3 @@ value: 'this is module two',

function _defineThree () {
function _defineThree() {
module.exports = {

@@ -132,11 +152,14 @@ value: 'this is module three',

'package.json': {
content: `${JSON.stringify({
dependencies: {
one: '1.0.0',
two: '1.0.0',
three: '1.0.0',
content: `${JSON.stringify(
{
dependencies: {
one: '1.0.0',
two: '1.0.0',
three: '1.0.0',
},
devDependencies: {},
},
devDependencies: {
},
}, null, 2)}`,
null,
2
)}`,
},

@@ -152,8 +175,12 @@ 'node_modules/one/index.js': {

'node_modules/one/package.json': {
content: `${JSON.stringify({
dependencies: {
two: '1.0.0',
three: '1.0.0',
content: `${JSON.stringify(
{
dependencies: {
two: '1.0.0',
three: '1.0.0',
},
},
}, null, 2)}`,
null,
2
)}`,
},

@@ -168,7 +195,11 @@ 'node_modules/two/index.js': {

'node_modules/two/package.json': {
content: `${JSON.stringify({
dependencies: {
three: '1.0.0',
content: `${JSON.stringify(
{
dependencies: {
three: '1.0.0',
},
},
}, null, 2)}`,
null,
2
)}`,
},

@@ -183,7 +214,11 @@ 'node_modules/three/index.js': {

'node_modules/three/package.json': {
content: `${JSON.stringify({
dependencies: {
one: '1.0.0',
content: `${JSON.stringify(
{
dependencies: {
one: '1.0.0',
},
},
}, null, 2)}`,
null,
2
)}`,
},

@@ -195,17 +230,20 @@ ...files,

if (defaultPolicy) {
_config = mergeDeep({
resources: {
one: {
packages: {
two: true,
three: true,
_config = mergeDeep(
{
resources: {
one: {
packages: {
two: true,
three: true,
},
},
},
two: {
packages: {
three: true,
two: {
packages: {
three: true,
},
},
},
},
}, config)
config
)
} else {

@@ -215,11 +253,14 @@ _config = config

const _configOverride = mergeDeep({
resources: {
one: {
packages: {
five: true,
const _configOverride = mergeDeep(
{
resources: {
one: {
packages: {
five: true,
},
},
},
},
}, configOverride)
configOverride
)

@@ -247,6 +288,6 @@ return {

function createHookedConsole () {
function createHookedConsole() {
let hasResolved = false
let resolve
const firstLogEventPromise = new Promise(_resolve => {
const firstLogEventPromise = new Promise((_resolve) => {
resolve = _resolve

@@ -277,6 +318,3 @@ })

async function runScenario ({
scenario,
runWithPrecompiledModules = false,
}) {
async function runScenario({ scenario, runWithPrecompiledModules = false }) {
const {

@@ -296,3 +334,12 @@ entries,

Object.assign(scenario.context, { console: hookedConsole })
const { result: createKernel, vmGlobalThis, vmContext, vmFeralFunction } = evaluateWithSourceUrl('LavaMoat/core-test/kernel', kernelSrc, scenario.context)
const {
result: createKernel,
vmGlobalThis,
vmContext,
vmFeralFunction,
} = evaluateWithSourceUrl(
'LavaMoat/core-test/kernel',
kernelSrc,
scenario.context
)
// root global for test realm

@@ -321,3 +368,5 @@ scenario.globalThis = vmGlobalThis

// append the source or prepare the precompiledInitializer
const intializerSource = `(function(exports, require, module, __filename, __dirname){\n${applySourceTransforms(moduleRecord.content)}\n})`
const intializerSource = `(function(exports, require, module, __filename, __dirname){\n${applySourceTransforms(
moduleRecord.content
)}\n})`
if (runWithPrecompiledModules) {

@@ -346,3 +395,3 @@ moduleData.precompiledInitializer = vmFeralFunction(`

entries.forEach(id => kernel.internalRequire(id))
entries.forEach((id) => kernel.internalRequire(id))
const testResult = await firstLogEventPromise

@@ -352,5 +401,9 @@ return testResult

async function prepareScenarioOnDisk ({ scenario, policyName = 'policies', projectDir }) {
async function prepareScenarioOnDisk({
scenario,
policyName = 'policies',
projectDir,
}) {
if (projectDir === undefined) {
({ path: projectDir } = await tmp.dir())
;({ path: projectDir } = await tmp.dir())
}

@@ -360,19 +413,36 @@ const filesToWrite = Object.values(scenario.files)

const defaultPaths = getDefaultPaths(policyName)
const primaryPath = typeof scenario.opts.policy === 'string' ? scenario.opts.policy : defaultPaths.primary
filesToWrite.push({ file: primaryPath, content: stringify(scenario.config) })
const primaryPath =
typeof scenario.opts.policy === 'string'
? scenario.opts.policy
: defaultPaths.primary
filesToWrite.push({
file: primaryPath,
content: stringify(scenario.config),
})
if (scenario.configOverride) {
const overridePath = typeof scenario.opts.policyOverride === 'string' ? scenario.opts.policyOverride : defaultPaths.override
filesToWrite.push({ file: overridePath, content: stringify(scenario.configOverride) })
const overridePath =
typeof scenario.opts.policyOverride === 'string'
? scenario.opts.policyOverride
: defaultPaths.override
filesToWrite.push({
file: overridePath,
content: stringify(scenario.configOverride),
})
}
}
await Promise.all(filesToWrite.map(async (file) => {
const fullPath = path.join(projectDir, file.file)
const dirname = path.dirname(fullPath)
await fs.mkdir(dirname, { recursive: true })
await fs.writeFile(fullPath, file.content)
}))
return { projectDir, policyDir: path.join(projectDir, `/lavamoat/${policyName}/`) }
await Promise.all(
filesToWrite.map(async (file) => {
const fullPath = path.join(projectDir, file.file)
const dirname = path.dirname(fullPath)
await fs.mkdir(dirname, { recursive: true })
await fs.writeFile(fullPath, file.content)
})
)
return {
projectDir,
policyDir: path.join(projectDir, `/lavamoat/${policyName}/`),
}
}
function fillInFileDetails (files) {
function fillInFileDetails(files) {
Object.entries(files).forEach(([file, fileObj]) => {

@@ -390,3 +460,3 @@ fileObj.file = fileObj.file || file

function moduleDataForBuiltin (builtinObj, name) {
function moduleDataForBuiltin(builtinObj, name) {
return {

@@ -403,3 +473,7 @@ id: name,

function prepareModuleInitializerArgs (requireRelativeWithContext, moduleObj, moduleData) {
function prepareModuleInitializerArgs(
requireRelativeWithContext,
moduleObj,
moduleData
) {
const require = requireRelativeWithContext

@@ -411,3 +485,5 @@ const module = moduleObj

require.resolve = (requestedName) => {
throw new Error('require.resolve not implemented in lavamoat-core test harness')
throw new Error(
'require.resolve not implemented in lavamoat-core test harness'
)
}

@@ -417,4 +493,3 @@ return [exports, require, module, __filename, __dirname]

function evaluateWithSourceUrl (filename, content, context) {
function evaluateWithSourceUrl(filename, content, context) {
const vmContext = createContext()

@@ -424,3 +499,6 @@ const vmGlobalThis = runInContext('this', vmContext)

Object.defineProperties(vmGlobalThis, Object.getOwnPropertyDescriptors(context))
Object.defineProperties(
vmGlobalThis,
Object.getOwnPropertyDescriptors(context)
)

@@ -448,22 +526,25 @@ // circular ref (used when globalThis is not present)

async function createConfigForTest (testFn, opts = {}) {
const files = [{
type: 'js',
specifier: './entry.js',
file: './entry.js',
packageName: '$root$',
importMap: {
test: './node_modules/test/index.js',
async function createConfigForTest(testFn, opts = {}) {
const files = [
{
type: 'js',
specifier: './entry.js',
file: './entry.js',
packageName: '$root$',
importMap: {
test: './node_modules/test/index.js',
},
content: 'require("test")',
entry: true,
},
content: 'require("test")',
entry: true,
}, {
// non-entry
type: 'js',
specifier: './node_modules/test/index.js',
file: './node_modules/test/index.js',
packageName: 'test',
importMap: {},
content: `(${testFn})()`,
}]
{
// non-entry
type: 'js',
specifier: './node_modules/test/index.js',
file: './node_modules/test/index.js',
packageName: 'test',
importMap: {},
content: `(${testFn})()`,
},
]
const policy = await generatePolicyFromFiles({ files, ...opts })

@@ -473,3 +554,3 @@ return policy

async function autoConfigForScenario ({ scenario, opts = {} }) {
async function autoConfigForScenario({ scenario, opts = {} }) {
const files = Object.values(scenario.files)

@@ -480,3 +561,3 @@ const policy = await generatePolicyFromFiles({ files, ...opts })

function convertOptsToArgs ({ scenario }) {
function convertOptsToArgs({ scenario }) {
const { entries } = scenario

@@ -483,0 +564,0 @@ if (entries.length !== 1) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc