chore(NA): avoid more native modules on production dependencies (#190183)

This PR is the final step needed for us to re-enable pointer compression
on serverless.

It introduces a cli later used in a CI check that will prevent the
addition of new native modules on production as discussed and agreed
with Luke and Brandon previously.

It also removes the usages of `cbor-x` and replaces those with a
wrapping package `@kbn/cbor` which relies on the implementation coming
from `borc` with no native modules involved.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Tiago Costa 2024-08-24 00:03:47 +01:00 committed by GitHub
parent 259518214a
commit 433cc1e3d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 1029 additions and 67 deletions

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
source .buildkite/scripts/common/util.sh
echo --- Check Production Native Node Modules
node scripts/check_prod_native_modules

View file

@ -17,3 +17,4 @@
.buildkite/scripts/steps/checks/yarn_deduplicate.sh
.buildkite/scripts/steps/checks/prettier_topology.sh
.buildkite/scripts/steps/checks/renovate.sh
.buildkite/scripts/steps/checks/native_modules.sh

2
.github/CODEOWNERS vendored
View file

@ -68,11 +68,13 @@ packages/kbn-capture-oas-snapshot-cli @elastic/kibana-core
x-pack/test/cases_api_integration/common/plugins/cases @elastic/response-ops
packages/kbn-cases-components @elastic/response-ops
x-pack/plugins/cases @elastic/response-ops
packages/kbn-cbor @elastic/kibana-operations
packages/kbn-cell-actions @elastic/security-threat-hunting-explore
src/plugins/chart_expressions/common @elastic/kibana-visualizations
packages/kbn-chart-icons @elastic/kibana-visualizations
src/plugins/charts @elastic/kibana-visualizations
packages/kbn-check-mappings-update-cli @elastic/kibana-core
packages/kbn-check-prod-native-modules-cli @elastic/kibana-operations
packages/kbn-ci-stats-core @elastic/kibana-operations
packages/kbn-ci-stats-performance-metrics @elastic/kibana-operations
packages/kbn-ci-stats-reporter @elastic/kibana-operations

View file

@ -199,6 +199,7 @@
"@kbn/cases-api-integration-test-plugin": "link:x-pack/test/cases_api_integration/common/plugins/cases",
"@kbn/cases-components": "link:packages/kbn-cases-components",
"@kbn/cases-plugin": "link:x-pack/plugins/cases",
"@kbn/cbor": "link:packages/kbn-cbor",
"@kbn/cell-actions": "link:packages/kbn-cell-actions",
"@kbn/chart-expressions-common": "link:src/plugins/chart_expressions/common",
"@kbn/chart-icons": "link:packages/kbn-chart-icons",
@ -1028,13 +1029,13 @@
"base64-js": "^1.3.1",
"bitmap-sdf": "^1.0.3",
"blurhash": "^2.0.1",
"borc": "3.0.0",
"brace": "0.11.1",
"brok": "^5.0.2",
"byte-size": "^8.1.0",
"cacheable-lookup": "6",
"camelcase-keys": "7.0.2",
"canvg": "^3.0.9",
"cbor-x": "^1.3.3",
"chalk": "^4.1.0",
"cheerio": "^1.0.0-rc.12",
"chroma-js": "^2.1.0",
@ -1313,6 +1314,7 @@
"@kbn/bazel-runner": "link:packages/kbn-bazel-runner",
"@kbn/capture-oas-snapshot-cli": "link:packages/kbn-capture-oas-snapshot-cli",
"@kbn/check-mappings-update-cli": "link:packages/kbn-check-mappings-update-cli",
"@kbn/check-prod-native-modules-cli": "link:packages/kbn-check-prod-native-modules-cli",
"@kbn/ci-stats-core": "link:packages/kbn-ci-stats-core",
"@kbn/ci-stats-performance-metrics": "link:packages/kbn-ci-stats-performance-metrics",
"@kbn/ci-stats-reporter": "link:packages/kbn-ci-stats-reporter",

View file

@ -0,0 +1,3 @@
# @kbn/cbor
Simple wrapper around borc to expose CBOR encode and decode methods with reasonable performance and no native modules

View file

@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { encode, decode } from '.';
describe('KbnCbor', () => {
it('should correctly encode and decode data', () => {
const data = { hello: 'world', count: 123, isValid: true };
// encoding
const encoded = encode(data);
expect(encoded).toBeInstanceOf(Buffer);
expect(encoded.length).toBeGreaterThan(0);
// decoding
const decoded = decode(encoded);
expect(decoded).toEqual(data);
});
it('should encode data to Buffer', () => {
const data = { foo: 'bar' };
const encoded = encode(data);
expect(Buffer.isBuffer(encoded)).toBe(true);
});
it('should decode Buffer to original data', () => {
const data = { foo: 'bar', num: 42, arr: [1, 2, 3] };
const encoded = encode(data);
const decoded = decode(encoded);
expect(decoded).toEqual(data);
});
});

View file

@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
// NOTE: This can possibly be replaced with node-cbor using encode, and decodeFirstSync if we do need
// to change into something better maintained but for now we are going to stick with borc as it is
// a little faster
import { encode as encodeJS, decode as decodeJS } from 'borc';
export class KbnCbor {
static encode(data: unknown) {
return encodeJS(data);
}
static decode(uint8: any) {
return decodeJS(uint8);
}
}
export const encode = KbnCbor.encode;
export const decode = KbnCbor.decode;

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-cbor'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/cbor",
"owner": "@elastic/kibana-operations"
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/cbor",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"main": "./index.ts"
}

View file

@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
"../../typings/borc.d.ts"
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -0,0 +1,3 @@
# @kbn/check-prod-native-modules-cli
Simple and straightforward CLI for searching for native modules installed as prod dependencies or as a result of any prod dependency.

View file

@ -0,0 +1,243 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { promises as fs, existsSync } from 'fs';
import { ToolingLog } from '@kbn/tooling-log';
import { findProductionDependencies, readYarnLock } from '@kbn/yarn-lock-validator';
import {
checkProdNativeModules,
checkDependencies,
isNativeModule,
} from './check_prod_native_modules';
jest.mock('fs', () => ({
promises: {
readdir: jest.fn(),
},
existsSync: jest.fn(),
}));
jest.mock('@kbn/repo-info', () => ({
REPO_ROOT: '/mocked/repo/root',
}));
jest.mock('@kbn/tooling-log', () => ({
ToolingLog: jest.fn().mockImplementation(() => ({
info: jest.fn(),
error: jest.fn(),
success: jest.fn(),
})),
}));
jest.mock('@kbn/yarn-lock-validator', () => ({
findProductionDependencies: jest.fn(),
readYarnLock: jest.fn(),
}));
jest.mock(
// eslint-disable-next-line @kbn/imports/no_unresolvable_imports
'/test/node_modules/test-package/package.json',
() => ({
name: 'test-package',
version: '1.0.0',
}),
{ virtual: true }
);
jest.mock(
// eslint-disable-next-line @kbn/imports/no_unresolvable_imports
'/test/node_modules/@scope/package/package.json',
() => ({
name: '@scope/package',
version: '1.0.0',
}),
{ virtual: true }
);
describe('Check Prod Native Modules', () => {
let mockLog: jest.Mocked<ToolingLog>;
beforeEach(() => {
jest.clearAllMocks();
mockLog = new ToolingLog() as jest.Mocked<ToolingLog>;
});
describe('isNativeModule', () => {
it('should return true if binding.gyp is found', async () => {
(fs.readdir as jest.Mock).mockResolvedValueOnce([
{ name: 'binding.gyp', isDirectory: () => false },
]);
const result = await isNativeModule('/test/path', mockLog);
expect(result).toBe(true);
});
it('should return true if .node file is found', async () => {
(fs.readdir as jest.Mock).mockResolvedValueOnce([
{ name: 'test.node', isDirectory: () => false },
]);
const result = await isNativeModule('/test/path', mockLog);
expect(result).toBe(true);
});
it('should return false if no native module indicators are found', async () => {
(fs.readdir as jest.Mock).mockResolvedValueOnce([
{ name: 'regular.js', isDirectory: () => false },
]);
const result = await isNativeModule('/test/path', mockLog);
expect(result).toBe(false);
});
it('should log an error if there is an issue reading the directory', async () => {
(fs.readdir as jest.Mock).mockRejectedValueOnce(new Error('Read error'));
await isNativeModule('/test/path', mockLog);
expect(mockLog.error).toHaveBeenCalledWith('Error when reading /test/path: Read error');
});
});
describe('checkDependencies', () => {
it('should identify native modules in production dependencies', async () => {
const mockProductionDependencies = new Map([['test-package@1.0.0', true]]);
const mockProdNativeModulesFound: Array<{ name: string; version: string; path: string }> = [];
(fs.readdir as jest.Mock).mockResolvedValueOnce([
{ name: 'test-package', isDirectory: () => true },
]);
(fs.readdir as jest.Mock)
.mockResolvedValueOnce([{ name: 'binding.gyp', isDirectory: () => false }])
.mockResolvedValueOnce([]);
(existsSync as jest.Mock).mockReturnValue(true);
jest
// eslint-disable-next-line @typescript-eslint/no-var-requires
.spyOn(require('./check_prod_native_modules'), 'isNativeModule')
.mockResolvedValueOnce(true);
await checkDependencies(
'/test/node_modules',
mockProductionDependencies,
mockProdNativeModulesFound,
mockLog
);
expect(mockProdNativeModulesFound).toEqual([
{ name: 'test-package', version: '1.0.0', path: '/test/node_modules/test-package' },
]);
});
it('should handle scoped packages', async () => {
const mockProductionDependencies = new Map([['@scope/package@1.0.0', true]]);
const mockProdNativeModulesFound: Array<{ name: string; version: string; path: string }> = [];
(fs.readdir as jest.Mock)
.mockResolvedValueOnce([{ name: '@scope', isDirectory: () => true }])
.mockResolvedValueOnce([{ name: 'package', isDirectory: () => true }]);
(fs.readdir as jest.Mock)
.mockResolvedValueOnce([{ name: 'binding.gyp', isDirectory: () => false }])
.mockResolvedValueOnce([]);
(existsSync as jest.Mock).mockReturnValue(true);
(existsSync as jest.Mock).mockReturnValue(true);
jest
// eslint-disable-next-line @typescript-eslint/no-var-requires
.spyOn(require('./check_prod_native_modules'), 'isNativeModule')
.mockResolvedValueOnce(true);
await checkDependencies(
'/test/node_modules',
mockProductionDependencies,
mockProdNativeModulesFound,
mockLog
);
expect(mockProdNativeModulesFound).toEqual([
{ name: '@scope/package', version: '1.0.0', path: '/test/node_modules/@scope/package' },
]);
});
});
describe('checkProdNativeModules', () => {
it('should return false when no native modules are found', async () => {
(existsSync as jest.Mock).mockReturnValue(true);
(findProductionDependencies as jest.Mock).mockReturnValue(new Map());
(readYarnLock as jest.Mock).mockResolvedValueOnce({});
(fs.readdir as jest.Mock).mockResolvedValue([]);
jest
// eslint-disable-next-line @typescript-eslint/no-var-requires
.spyOn(require('./check_prod_native_modules'), 'checkDependencies')
.mockResolvedValue(undefined);
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(false);
expect(mockLog.success).toHaveBeenCalledWith(
'No production native modules installed were found'
);
});
it('should return true and log errors when native modules are found', async () => {
(existsSync as jest.Mock).mockReturnValueOnce(true).mockReturnValueOnce(true);
(findProductionDependencies as jest.Mock).mockReturnValue(
new Map([['native-module@1.0.0', { name: 'native-module', version: '1.0.0' }]])
);
(readYarnLock as jest.Mock).mockResolvedValueOnce({});
// Mock loadPackageJson to return a mock package JSON object
jest
// eslint-disable-next-line @typescript-eslint/no-var-requires
.spyOn(require('./helpers'), 'loadPackageJson')
.mockImplementation((packageJsonPath: any) => {
return {
name: 'native-module',
version: '1.0.0',
};
});
(fs.readdir as jest.Mock)
.mockResolvedValueOnce([{ name: 'native-module', isDirectory: () => true }])
// .mockResolvedValueOnce([{ name: 'package.json', isDirectory: () => false }])
.mockResolvedValueOnce([{ name: 'binding.gyp', isDirectory: () => false }]);
jest
// eslint-disable-next-line @typescript-eslint/no-var-requires
.spyOn(require('./check_prod_native_modules'), 'checkDependencies')
.mockImplementationOnce((_, __, prodNativeModulesFound: any) => {
prodNativeModulesFound.push({
name: 'native-module',
version: '1.0.0',
path: '/path/to/native-module',
});
});
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(true);
expect(mockLog.error).toHaveBeenNthCalledWith(
1,
'Production native module detected: node_modules/native-module'
);
expect(mockLog.error).toHaveBeenNthCalledWith(
2,
'Production native modules were detected and logged above'
);
});
it('should throw an error if root node_modules folder is not found', async () => {
(existsSync as jest.Mock).mockReturnValue(false);
(findProductionDependencies as jest.Mock).mockReturnValue(new Map());
(readYarnLock as jest.Mock).mockResolvedValueOnce({});
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(true);
expect(mockLog.error).toHaveBeenCalledWith(
'No root node_modules folder was found in the project. Impossible to continue'
);
});
});
});

View file

@ -0,0 +1,150 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as path from 'path';
import { promises as fs, existsSync } from 'fs';
import { REPO_ROOT } from '@kbn/repo-info';
import type { ToolingLog } from '@kbn/tooling-log';
import { findProductionDependencies, readYarnLock } from '@kbn/yarn-lock-validator';
import { loadPackageJson } from './helpers';
// Checks if a given path contains a native module or not recursively
async function isNativeModule(modulePath: string, log: ToolingLog): Promise<boolean> {
const stack: string[] = [modulePath];
while (stack.length > 0) {
const currentPath = stack.pop() as string;
// Skip processing if the current directory is a node_modules folder
if (path.basename(currentPath) === 'node_modules') {
continue;
}
try {
const entries = await fs.readdir(currentPath, { withFileTypes: true });
for (const entry of entries) {
const entryPath = path.join(currentPath, entry.name);
if (entry.isDirectory()) {
stack.push(entryPath);
} else if (entry.name === 'binding.gyp' || entry.name.endsWith('.node')) {
return true;
}
}
} catch (err) {
log.error(`Error when reading ${currentPath}: ${err.message}`);
}
}
return false;
}
// Searches through node_modules and for each module which is a prod dep (or a direct result of one) checks recursively for native modules
async function checkDependencies(
rootNodeModulesDir: string,
productionDependencies: Map<string, boolean>,
prodNativeModulesFound: Array<{ name: string; version: string; path: string }>,
log: ToolingLog
) {
const stack: string[] = [rootNodeModulesDir];
while (stack.length > 0) {
const currentDir = stack.pop() as string;
try {
const entries = await fs.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const entryPath = path.join(currentDir, entry.name);
if (entry.name.startsWith('@')) {
// Handle scoped packages (e.g., @scope/package)
stack.push(entryPath);
continue;
}
const packageJsonPath = path.join(entryPath, 'package.json');
if (existsSync(packageJsonPath)) {
const packageJson = loadPackageJson(packageJsonPath);
const dependencyKey = `${packageJson.name}@${packageJson.version}`;
if (productionDependencies.has(dependencyKey)) {
const isNative = await isNativeModule(entryPath, log);
if (isNative) {
prodNativeModulesFound.push({
name: packageJson.name,
version: packageJson.version,
path: entryPath,
});
}
}
}
// Adds nested node_modules to the stack to check for further dependencies
const nestedNodeModulesPath = path.join(entryPath, 'node_modules');
if (existsSync(nestedNodeModulesPath)) {
stack.push(nestedNodeModulesPath);
}
}
} catch (err) {
throw new Error(`Error processing directory ${currentDir}: ${err.message}`);
}
}
}
// Checks if there are native modules in the production dependencies
async function checkProdNativeModules(log: ToolingLog) {
log.info('Checking for native modules on production dependencies...');
const rootNodeModulesDir = path.join(REPO_ROOT, 'node_modules');
const prodNativeModulesFound: Array<{ name: string; version: string; path: string }> = [];
try {
// Gets all production dependencies based on package.json and then searches across transient dependencies using lock file
const rawProductionDependencies = findProductionDependencies(log, await readYarnLock());
// Converts rawProductionDependencies into a simple Map of production dependencies
const productionDependencies: Map<string, boolean> = new Map();
rawProductionDependencies.forEach((depInfo, depKey) => {
productionDependencies.set(`${depInfo.name}@${depInfo.version}`, true);
});
// Fail if no root node_modules folder
if (!existsSync(rootNodeModulesDir)) {
throw new Error(
'No root node_modules folder was found in the project. Impossible to continue'
);
}
// Goes into the node_modules folder and for each node_module which is a production dependency (or a result of one) checks recursively if there are native modules
await checkDependencies(
rootNodeModulesDir,
productionDependencies,
prodNativeModulesFound,
log
);
// In that case no prod native modules were found
if (!prodNativeModulesFound.length) {
log.success('No production native modules installed were found');
return false;
}
// Logs every detected native module at once
prodNativeModulesFound.forEach((dep) => {
log.error(`Production native module detected: ${path.relative(REPO_ROOT, dep.path)}`);
});
throw new Error('Production native modules were detected and logged above');
} catch (err) {
log.error(err.message);
return true;
}
}
export { checkProdNativeModules, checkDependencies, isNativeModule };

View file

@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
// helper function to load package.json
function loadPackageJson(packageJsonPath: string) {
return require(packageJsonPath);
}
export { loadPackageJson };

View file

@ -0,0 +1,4 @@
{
"name": "package-a",
"version": "1.0.0"
}

View file

@ -0,0 +1,4 @@
{
"name": "package-b",
"version": "2.0.0"
}

View file

@ -0,0 +1,8 @@
{
"name": "no-native-modules-project",
"version": "1.0.0",
"dependencies": {
"package-a": "^1.0.0",
"package-b": "^2.0.0"
}
}

View file

@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
package-a@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/package-a/-/package-a-1.0.0.tgz"
integrity sha1-example123
package-b@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/package-b/-/package-b-2.0.0.tgz"
integrity sha1-example456

View file

@ -0,0 +1,8 @@
{
"name": "no-node-modules-project",
"version": "1.0.0",
"dependencies": {
"package-a": "^1.0.0",
"package-b": "^2.0.0"
}
}

View file

@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
package-a@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/package-a/-/package-a-1.0.0.tgz"
integrity sha1-example123
package-b@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/package-b/-/package-b-2.0.0.tgz"
integrity sha1-example456

View file

@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "native_module",
"sources": [ "" ]
}
]
}

View file

@ -0,0 +1,5 @@
{
"name": "native-module",
"version": "1.0.0",
"gypfile": true
}

View file

@ -0,0 +1,4 @@
{
"name": "package-b",
"version": "2.0.0"
}

View file

@ -0,0 +1,10 @@
{
"name": "with-native-modules-project",
"version": "1.0.0",
"devDependencies": {
"native-module": "^1.0.0"
},
"dependencies": {
"package-b": "^2.0.0"
}
}

View file

@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
native-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/native-module/-/native-module-1.0.0.tgz"
integrity sha1-example789
package-b@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/package-b/-/package-b-2.0.0.tgz"
integrity sha1-example456

View file

@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "native_module",
"sources": [ "" ]
}
]
}

View file

@ -0,0 +1,5 @@
{
"name": "native-module",
"version": "1.0.0",
"gypfile": true
}

View file

@ -0,0 +1,4 @@
{
"name": "native-module2",
"version": "1.0.0"
}

View file

@ -0,0 +1,4 @@
{
"name": "package-b",
"version": "2.0.0"
}

View file

@ -0,0 +1,9 @@
{
"name": "with-native-modules-project",
"version": "1.0.0",
"dependencies": {
"native-module": "^1.0.0",
"native-module2": "^1.0.0",
"package-b": "^2.0.0"
}
}

View file

@ -0,0 +1,16 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
native-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/native-module/-/native-module-1.0.0.tgz"
integrity sha1-example789
native-module2@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/native-module/-/native-module2-1.0.0.tgz"
integrity sha1-example789
package-b@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/package-b/-/package-b-2.0.0.tgz"
integrity sha1-example456

View file

@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "native_module",
"sources": [ "" ]
}
]
}

View file

@ -0,0 +1,5 @@
{
"name": "native-module",
"version": "1.0.0",
"gypfile": true
}

View file

@ -0,0 +1,7 @@
{
"name": "packaga-a",
"version": "1.0.0",
"dependencies": {
"native-module": "^1.0.0"
}
}

View file

@ -0,0 +1,4 @@
{
"name": "package-b",
"version": "2.0.0"
}

View file

@ -0,0 +1,8 @@
{
"name": "with-native-modules-project",
"version": "1.0.0",
"dependencies": {
"package-a": "^1.0.0",
"package-b": "^2.0.0"
}
}

View file

@ -0,0 +1,18 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
native-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/native-module/-/native-module-1.0.0.tgz"
integrity sha1-example789
package-a@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/package-a/-/package-a-1.0.0.tgz"
integrity sha1-example789
dependencies:
native-module "^1.0.0"
package-b@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/package-b/-/package-b-2.0.0.tgz"
integrity sha1-example456

View file

@ -0,0 +1,151 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import path from 'path';
import fs from 'fs';
import { ToolingLog } from '@kbn/tooling-log';
import { checkProdNativeModules } from '../check_prod_native_modules';
describe('checkProdNativeModules', () => {
let mockLog: jest.Mocked<ToolingLog>;
const fixturesDir = path.join(__dirname, '__fixtures__');
beforeEach(() => {
mockLog = {
info: jest.fn(),
success: jest.fn(),
error: jest.fn(),
} as unknown as jest.Mocked<ToolingLog>;
jest.clearAllMocks();
});
it('should return false when no native modules are found', async () => {
// Use a fixture without native modules
const noNativeModulesDir = path.join(fixturesDir, 'no_native_modules');
const noNativeModulesPkgJsonPath = path.join(noNativeModulesDir, 'package.json');
jest.spyOn(process, 'cwd').mockReturnValue(noNativeModulesDir);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'REPO_ROOT', noNativeModulesDir);
const noNativeModulesPkgJson = JSON.parse(fs.readFileSync(noNativeModulesPkgJsonPath, 'utf8'));
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'kibanaPackageJson', noNativeModulesPkgJson);
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(false);
expect(mockLog.success).toHaveBeenCalledWith(
'No production native modules installed were found'
);
});
it('should return true and log errors when native modules are found', async () => {
// Use a fixture with native modules
const withNativeModulesDir = path.join(fixturesDir, 'with_native_modules');
const withNativeModulesPkgJsonPath = path.join(withNativeModulesDir, 'package.json');
jest.spyOn(process, 'cwd').mockReturnValue(withNativeModulesDir);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'REPO_ROOT', withNativeModulesDir);
const withNativeModulesPkgJson = JSON.parse(
fs.readFileSync(withNativeModulesPkgJsonPath, 'utf8')
);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'kibanaPackageJson', withNativeModulesPkgJson);
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(true);
expect(mockLog.error).toHaveBeenCalledWith(
expect.stringContaining('Production native module detected:')
);
expect(mockLog.error).toHaveBeenCalledWith(
'Production native modules were detected and logged above'
);
});
it('should throw an error when root node_modules folder is not found', async () => {
// Use a fixture without node_modules
const noNodeModulesDir = path.join(fixturesDir, 'no_node_modules');
const noNodeModulesPkgJsonPath = path.join(noNodeModulesDir, 'package.json');
jest.spyOn(process, 'cwd').mockReturnValue(noNodeModulesDir);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'REPO_ROOT', noNodeModulesDir);
const noNodeModulesPkgJson = JSON.parse(fs.readFileSync(noNodeModulesPkgJsonPath, 'utf8'));
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'kibanaPackageJson', noNodeModulesPkgJson);
expect(await checkProdNativeModules(mockLog)).toBe(true);
expect(mockLog.error).toHaveBeenCalledWith(
'No root node_modules folder was found in the project. Impossible to continue'
);
});
it('should return false when no prod native modules are found', async () => {
// Use a fixture without native modules
const withDevNativeModulesDir = path.join(fixturesDir, 'with_dev_native_modules');
const withDevNativeModulesPkgJsonPath = path.join(withDevNativeModulesDir, 'package.json');
jest.spyOn(process, 'cwd').mockReturnValue(withDevNativeModulesDir);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'REPO_ROOT', withDevNativeModulesDir);
const withDevNativeModulesPkgJson = JSON.parse(
fs.readFileSync(withDevNativeModulesPkgJsonPath, 'utf8')
);
jest.replaceProperty(
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@kbn/repo-info'),
'kibanaPackageJson',
withDevNativeModulesPkgJson
);
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(false);
expect(mockLog.success).toHaveBeenCalledWith(
'No production native modules installed were found'
);
});
it('should return true and log errors when prod transient native modules are found', async () => {
// Use a fixture with native modules
const withTransientNativeModulesDir = path.join(fixturesDir, 'with_transient_native_modules');
const withTransientNativeModulesPkgJsonPath = path.join(
withTransientNativeModulesDir,
'package.json'
);
jest.spyOn(process, 'cwd').mockReturnValue(withTransientNativeModulesDir);
// eslint-disable-next-line @typescript-eslint/no-var-requires
jest.replaceProperty(require('@kbn/repo-info'), 'REPO_ROOT', withTransientNativeModulesDir);
const withTransientNativeModulesPkgJson = JSON.parse(
fs.readFileSync(withTransientNativeModulesPkgJsonPath, 'utf8')
);
jest.replaceProperty(
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@kbn/repo-info'),
'kibanaPackageJson',
withTransientNativeModulesPkgJson
);
const result = await checkProdNativeModules(mockLog);
expect(result).toBe(true);
expect(mockLog.error).toHaveBeenCalledWith(
expect.stringContaining('Production native module detected:')
);
expect(mockLog.error).toHaveBeenCalledWith(
'Production native modules were detected and logged above'
);
});
});

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test/jest_node',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-check-prod-native-modules-cli'],
};

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test/jest_integration_node',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-check-prod-native-modules-cli'],
};

View file

@ -0,0 +1,6 @@
{
"type": "shared-server",
"id": "@kbn/check-prod-native-modules-cli",
"owner": "@elastic/kibana-operations",
"devOnly": true
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/check-prod-native-modules-cli",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
"main": "./run_check_prod_native_modules_cli"
}

View file

@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { run } from '@kbn/dev-cli-runner';
import { createFailError } from '@kbn/dev-cli-errors';
import { checkProdNativeModules } from './check_prod_native_modules';
run(
async ({ log }) => {
const foundProdNativeModules = await checkProdNativeModules(log);
if (foundProdNativeModules) {
throw createFailError(
'Failed: check all previous errors before continuing. Chat with the Kibana Operations Team if you do need help.'
);
}
},
{
usage: `node scripts/check_prod_native_modules`,
description:
'Check if there are production dependencies that contains or installs dependencies that contain native modules and errors out on those cases',
}
);

View file

@ -0,0 +1,23 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node"
]
},
"include": [
"**/*.ts",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/repo-info",
"@kbn/tooling-log",
"@kbn/dev-cli-runner",
"@kbn/dev-cli-errors",
"@kbn/yarn-lock-validator",
]
}

View file

@ -9,3 +9,4 @@
export { readYarnLock } from './src/yarn_lock';
export type { YarnLock } from './src/yarn_lock';
export { validateDependencies } from './src/validate_yarn_lock';
export { findProductionDependencies } from './src/find_production_dependencies';

View file

@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
require('../src/setup_node_env');
require('@kbn/check-prod-native-modules-cli');

View file

@ -9,13 +9,12 @@
import type { Logger } from '@kbn/core/server';
import { set } from '@kbn/safer-lodash-set';
import { Readable } from 'stream';
import { encode } from 'cbor-x';
import { encode, decode } from '@kbn/cbor';
import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks';
import { ContentStream, ContentStreamEncoding, ContentStreamParameters } from './content_stream';
import type { GetResponse } from '@elastic/elasticsearch/lib/api/types';
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { FileDocument } from '../../../../file_client/file_metadata_client/adapters/es_index';
import * as cborx from 'cbor-x';
import { IndexRequest } from '@elastic/elasticsearch/lib/api/types';
describe('ContentStream', () => {
@ -415,7 +414,7 @@ describe('ContentStream', () => {
stream.end('some data');
await new Promise((resolve) => stream.once('finish', resolve));
const docBuffer = (client.index.mock.calls[0][0] as IndexRequest).document as Buffer;
const docData = cborx.decode(docBuffer);
const docData = decode(docBuffer);
expect(docData).toHaveProperty('@timestamp');
});

View file

@ -7,7 +7,7 @@
*/
import { createId } from '@paralleldrive/cuid2';
import * as cborx from 'cbor-x';
import { encode, decode } from '@kbn/cbor';
import { errors as esErrors } from '@elastic/elasticsearch';
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
import { ByteSizeValue } from '@kbn/config-schema';
@ -144,7 +144,7 @@ export class ContentStream extends Duplex {
}
const buffer = Buffer.concat(chunks);
const decodedChunkDoc: GetResponse<FileChunkDocument> | undefined = buffer.byteLength
? (cborx.decode(buffer) as GetResponse<FileChunkDocument>)
? (decode(buffer) as GetResponse<FileChunkDocument>)
: undefined;
// Because `asStream` was used in retrieving the document, errors are also not be processed
@ -245,7 +245,7 @@ export class ContentStream extends Duplex {
id,
index,
op_type: 'create',
document: cborx.encode({
document: encode({
data,
bid,
// Mark it as last?

View file

@ -7,7 +7,7 @@
*/
import { Readable } from 'stream';
import { encode } from 'cbor-x';
import { encode } from '@kbn/cbor';
import { promisify } from 'util';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';

View file

@ -33,6 +33,7 @@
"@kbn/logging",
"@kbn/core-http-common",
"@kbn/core-lifecycle-server-internal",
"@kbn/cbor",
],
"exclude": [
"target/**/*",

View file

@ -130,6 +130,8 @@
"@kbn/cases-components/*": ["packages/kbn-cases-components/*"],
"@kbn/cases-plugin": ["x-pack/plugins/cases"],
"@kbn/cases-plugin/*": ["x-pack/plugins/cases/*"],
"@kbn/cbor": ["packages/kbn-cbor"],
"@kbn/cbor/*": ["packages/kbn-cbor/*"],
"@kbn/cell-actions": ["packages/kbn-cell-actions"],
"@kbn/cell-actions/*": ["packages/kbn-cell-actions/*"],
"@kbn/chart-expressions-common": ["src/plugins/chart_expressions/common"],
@ -140,6 +142,8 @@
"@kbn/charts-plugin/*": ["src/plugins/charts/*"],
"@kbn/check-mappings-update-cli": ["packages/kbn-check-mappings-update-cli"],
"@kbn/check-mappings-update-cli/*": ["packages/kbn-check-mappings-update-cli/*"],
"@kbn/check-prod-native-modules-cli": ["packages/kbn-check-prod-native-modules-cli"],
"@kbn/check-prod-native-modules-cli/*": ["packages/kbn-check-prod-native-modules-cli/*"],
"@kbn/ci-stats-core": ["packages/kbn-ci-stats-core"],
"@kbn/ci-stats-core/*": ["packages/kbn-ci-stats-core/*"],
"@kbn/ci-stats-performance-metrics": ["packages/kbn-ci-stats-performance-metrics"],

9
typings/borc.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
declare module 'borc';

View file

@ -10,7 +10,7 @@
import type { Client } from '@elastic/elasticsearch';
import type { SearchHit } from '@elastic/elasticsearch/lib/api/types';
import { basename } from 'path';
import * as cborx from 'cbor-x';
import { encode } from '@kbn/cbor';
import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common';
import { FleetActionGenerator } from '../../../common/endpoint/data_generators/fleet_action_generator';
import { EndpointActionGenerator } from '../../../common/endpoint/data_generators/endpoint_action_generator';
@ -236,7 +236,7 @@ export const sendEndpointActionResponse = async (
{
index: FILE_STORAGE_DATA_INDEX,
id: `${fileMeta._id}.0`,
document: cborx.encode({
document: encode({
bid: fileMeta._id,
last: true,
'@timestamp': new Date().toISOString(),

View file

@ -211,6 +211,7 @@
"@kbn/esql-ast",
"@kbn/esql-validation-autocomplete",
"@kbn/config",
"@kbn/cbor",
"@kbn/zod",
]
}

View file

@ -1449,36 +1449,6 @@
"@types/tough-cookie" "^4.0.5"
tough-cookie "^4.1.4"
"@cbor-extract/cbor-extract-darwin-arm64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.0.0.tgz#cf0667e4c22111c9d45e16c29964892b12460a76"
integrity sha512-jebtLrruvsBbGMsUn0QxZW/8Z7caS9OkszVKZ64WTWajUkyohmolUdKL2nbfaTyyi3ABJrxVNM4YO1pvMsNI1g==
"@cbor-extract/cbor-extract-darwin-x64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.0.0.tgz#7bc01e7911b97eee4c78ae074bd3108f2ff208c3"
integrity sha512-LGYjdlyqANBqCDzBujCqXpPcK70rvaQgw98/aquzBuEmK0KXS7i579CoVG1yS/eb3bMqiVPevBri45jbR6Tlsg==
"@cbor-extract/cbor-extract-linux-arm64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.0.0.tgz#e40608afed5f373091560fa9dcd19c7f52f510b0"
integrity sha512-c1rbQcSF01yVgbG60zEfHNsUkXiEEQRNdYqm5qpqEAkLx4gA6DDU91IQbalkqXfwDuQzcMovOc1TC3uJJIi2OQ==
"@cbor-extract/cbor-extract-linux-arm@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.0.0.tgz#f52a7580fb23e305370e66ae9ff136de3729c4b8"
integrity sha512-cOGHEIif5rPbpix6qhpuatrZzm6HeC5rT0nXt8ynLTc7PzfXmovswD9x6d9h5NcHswkV5y3PbkNbpel/tLADYg==
"@cbor-extract/cbor-extract-linux-x64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.0.0.tgz#8c936b8a93f915bf3c2459d5b4b78d244bda0f26"
integrity sha512-WYeE1b5WGf9pbbQH3qeNBXq710gGsuVFUiP148RY8In+2pCp/fxjBpe701ngam9/fF5D+gJs8B1i5wv/PN7JZA==
"@cbor-extract/cbor-extract-win32-x64@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.0.0.tgz#4d4ad91527a8313c3db1e2167a8821dfae9d6211"
integrity sha512-XqVuJEnE0jpl/RkuSp04FF2UE73gY52Y4nZaIE6j9GAeSH2cHYU5CCd4TaVMDi2M18ZpZv7XhL/k+nneQzyJpQ==
"@cfaester/enzyme-adapter-react-18@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cfaester/enzyme-adapter-react-18/-/enzyme-adapter-react-18-0.8.0.tgz#313814eb79658a6e74209f9f1743bcefff14a46f"
@ -3545,6 +3515,10 @@
version "0.0.0"
uid ""
"@kbn/cbor@link:packages/kbn-cbor":
version "0.0.0"
uid ""
"@kbn/cell-actions@link:packages/kbn-cell-actions":
version "0.0.0"
uid ""
@ -3565,6 +3539,10 @@
version "0.0.0"
uid ""
"@kbn/check-prod-native-modules-cli@link:packages/kbn-check-prod-native-modules-cli":
version "0.0.0"
uid ""
"@kbn/ci-stats-core@link:packages/kbn-ci-stats-core":
version "0.0.0"
uid ""
@ -8604,6 +8582,11 @@
"@smithy/util-buffer-from" "^3.0.0"
tslib "^2.6.2"
"@sovpro/delimited-stream@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4"
integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw==
"@statoscope/extensions@5.28.1":
version "5.28.1"
resolved "https://registry.yarnpkg.com/@statoscope/extensions/-/extensions-5.28.1.tgz#bc270f9366c4b2c13342f1a0d138520cf607a5bb"
@ -13369,6 +13352,19 @@ boolbase@^1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
borc@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe"
integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g==
dependencies:
bignumber.js "^9.0.0"
buffer "^6.0.3"
commander "^2.15.0"
ieee754 "^1.1.13"
iso-url "^1.1.5"
json-text-sequence "~0.3.0"
readable-stream "^3.6.0"
bowser@^1.7.3:
version "1.9.4"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a"
@ -13914,27 +13910,6 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
cbor-extract@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/cbor-extract/-/cbor-extract-2.0.2.tgz#8e45339627fb8b47071e8e71138c630019125939"
integrity sha512-QoLGEgPff03ad/L66P91ci5Zmf7Woq8bh4H5XT3+D5annlrPH5ObHf2Yvo53eDQaDkQtF9tJwMKSWANGXDmwUA==
dependencies:
node-gyp-build-optional-packages "5.0.3"
optionalDependencies:
"@cbor-extract/cbor-extract-darwin-arm64" "2.0.0"
"@cbor-extract/cbor-extract-darwin-x64" "2.0.0"
"@cbor-extract/cbor-extract-linux-arm" "2.0.0"
"@cbor-extract/cbor-extract-linux-arm64" "2.0.0"
"@cbor-extract/cbor-extract-linux-x64" "2.0.0"
"@cbor-extract/cbor-extract-win32-x64" "2.0.0"
cbor-x@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/cbor-x/-/cbor-x-1.3.3.tgz#5ba0f6d3f6720ea5ba38804e583c020bccf2f762"
integrity sha512-y3V8GlypWM01t3NtYvXmDehuU3bt4q3tewCrvj5EMfUYT6v9HjRu4NHYH3EgbzJCOaZFroAhzci9PHvIIDuOEQ==
optionalDependencies:
cbor-extract "^2.0.2"
ccount@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
@ -14500,7 +14475,7 @@ comma-separated-tokens@^1.0.0:
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==
commander@2, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1:
commander@2, commander@^2.15.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@ -20718,6 +20693,11 @@ isnumber@~1.0.0:
resolved "https://registry.yarnpkg.com/isnumber/-/isnumber-1.0.0.tgz#0e3f9759b581d99dd85086f0ec2a74909cfadd01"
integrity sha1-Dj+XWbWB2Z3YUIbw7Cp0kJz63QE=
iso-url@^1.1.5:
version "1.2.1"
resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811"
integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@ -21606,6 +21586,13 @@ json-stringify-safe@5.0.1, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json-text-sequence@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce"
integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA==
dependencies:
"@sovpro/delimited-stream" "^1.1.0"
json5@*, json5@^2.1.2, json5@^2.2.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
@ -23894,11 +23881,6 @@ node-forge@^1, node-forge@^1.2.1, node-forge@^1.3.1:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
node-gyp-build-optional-packages@5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
node-gyp-build-optional-packages@5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3"