Skip to content

Commit

Permalink
js[patch]: Inline lodash.set functionality to patch vulnerability (#843)
Browse files Browse the repository at this point in the history
The `lodash.set` package is unmaintained and has a CVE:
https://security.snyk.io/package/npm/lodash.set

The current `lodash` version has no CVEs:
https://security.snyk.io/package/npm/lodash

I've inlined the relevant set functionality from the current version of
`lodash`.

Let's be more careful with adding deps in the future!

@dqbd @nfcampos @agola11
  • Loading branch information
dqbd authored Jul 3, 2024
2 parents b9045e1 + 3b8a033 commit de7416b
Show file tree
Hide file tree
Showing 20 changed files with 533 additions and 22 deletions.
1 change: 1 addition & 0 deletions js/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
ignorePatterns: [
".eslintrc.cjs",
"scripts",
"src/utils/lodash/*",
"node_modules",
"dist",
"dist-cjs",
Expand Down
4 changes: 1 addition & 3 deletions js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "langsmith",
"version": "0.1.35",
"version": "0.1.36",
"description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
"packageManager": "[email protected]",
"files": [
Expand Down Expand Up @@ -95,7 +95,6 @@
"dependencies": {
"@types/uuid": "^9.0.1",
"commander": "^10.0.1",
"lodash.set": "^4.3.2",
"p-queue": "^6.6.2",
"p-retry": "4",
"uuid": "^9.0.0"
Expand All @@ -109,7 +108,6 @@
"@langchain/langgraph": "^0.0.19",
"@tsconfig/recommended": "^1.0.2",
"@types/jest": "^29.5.1",
"@types/lodash.set": "^4.3.9",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"babel-jest": "^29.5.0",
Expand Down
2 changes: 1 addition & 1 deletion js/src/anonymizer/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import set from "lodash.set";
import set from "../utils/lodash/set.js";

export interface StringNode {
value: string;
Expand Down
2 changes: 1 addition & 1 deletion js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export type {
export { RunTree, type RunTreeConfig } from "./run_trees.js";

// Update using yarn bump-version
export const __version__ = "0.1.35";
export const __version__ = "0.1.36";
49 changes: 49 additions & 0 deletions js/src/utils/lodash/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
The MIT License

Copyright JS Foundation and other contributors <https://js.foundation/>

Based on Underscore.js, copyright Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>

This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/lodash/lodash

The following license applies to all parts of this software except as
documented below:

====

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

====

Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code displayed within the prose of the
documentation.

CC0: http://creativecommons.org/publicdomain/zero/1.0/

====

Files located in the node_modules and vendor directories are externally
maintained libraries used by this software which have their own
licenses; we recommend you read them, as their terms may differ from the
terms above.
27 changes: 27 additions & 0 deletions js/src/utils/lodash/assignValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import baseAssignValue from "./baseAssignValue.js";
import eq from "./eq.js";

/** Used to check objects for own properties. */
const hasOwnProperty = Object.prototype.hasOwnProperty;

/**
* Assigns `value` to `key` of `object` if the existing value is not equivalent.
*
* @private
* @param {Object} object The object to modify.
* @param {string} key The key of the property to assign.
* @param {*} value The value to assign.
*/
function assignValue(object: Record<string, any>, key: string, value: any) {
const objValue = object[key];

if (!(hasOwnProperty.call(object, key) && eq(objValue, value))) {
if (value !== 0 || 1 / value === 1 / objValue) {
baseAssignValue(object, key, value);
}
} else if (value === undefined && !(key in object)) {
baseAssignValue(object, key, value);
}
}

export default assignValue;
23 changes: 23 additions & 0 deletions js/src/utils/lodash/baseAssignValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* The base implementation of `assignValue` and `assignMergeValue` without
* value checks.
*
* @private
* @param {Object} object The object to modify.
* @param {string} key The key of the property to assign.
* @param {*} value The value to assign.
*/
function baseAssignValue(object: Record<string, any>, key: string, value: any) {
if (key === "__proto__") {
Object.defineProperty(object, key, {
configurable: true,
enumerable: true,
value: value,
writable: true,
});
} else {
object[key] = value;
}
}

export default baseAssignValue;
52 changes: 52 additions & 0 deletions js/src/utils/lodash/baseSet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @ts-nocheck

import assignValue from "./assignValue.js";
import castPath from "./castPath.js";
import isIndex from "./isIndex.js";
import isObject from "./isObject.js";
import toKey from "./toKey.js";

/**
* The base implementation of `set`.
*
* @private
* @param {Object} object The object to modify.
* @param {Array|string} path The path of the property to set.
* @param {*} value The value to set.
* @param {Function} [customizer] The function to customize path creation.
* @returns {Object} Returns `object`.
*/
function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
return object;
}
path = castPath(path, object);

const length = path.length;
const lastIndex = length - 1;

let index = -1;
let nested = object;

while (nested != null && ++index < length) {
const key = toKey(path[index]);
let newValue = value;

if (index !== lastIndex) {
const objValue = nested[key];
newValue = customizer ? customizer(objValue, key, nested) : undefined;
if (newValue === undefined) {
newValue = isObject(objValue)
? objValue
: isIndex(path[index + 1])
? []
: {};
}
}
assignValue(nested, key, newValue);
nested = nested[key];
}
return object;
}

export default baseSet;
19 changes: 19 additions & 0 deletions js/src/utils/lodash/castPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import isKey from "./isKey.js";
import stringToPath from "./stringToPath.js";

/**
* Casts `value` to a path array if it's not one.
*
* @private
* @param {*} value The value to inspect.
* @param {Object} [object] The object to query keys on.
* @returns {Array} Returns the cast property path array.
*/
function castPath(value: any, object: Record<string, any>) {
if (Array.isArray(value)) {
return value;
}
return isKey(value, object) ? [value] : stringToPath(value);
}

export default castPath;
35 changes: 35 additions & 0 deletions js/src/utils/lodash/eq.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Performs a
* [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
* comparison between two values to determine if they are equivalent.
*
* @since 4.0.0
* @category Lang
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
* const object = { 'a': 1 }
* const other = { 'a': 1 }
*
* eq(object, object)
* // => true
*
* eq(object, other)
* // => false
*
* eq('a', 'a')
* // => true
*
* eq('a', Object('a'))
* // => false
*
* eq(NaN, NaN)
* // => true
*/
function eq(value: any, other: any) {
return value === other || (value !== value && other !== other);
}

export default eq;
19 changes: 19 additions & 0 deletions js/src/utils/lodash/getTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-nocheck

const toString = Object.prototype.toString;

/**
* Gets the `toStringTag` of `value`.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function getTag(value) {
if (value == null) {
return value === undefined ? "[object Undefined]" : "[object Null]";
}
return toString.call(value);
}

export default getTag;
30 changes: 30 additions & 0 deletions js/src/utils/lodash/isIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @ts-nocheck

/** Used as references for various `Number` constants. */
const MAX_SAFE_INTEGER = 9007199254740991;

/** Used to detect unsigned integer values. */
const reIsUint = /^(?:0|[1-9]\d*)$/;

/**
* Checks if `value` is a valid array-like index.
*
* @private
* @param {*} value The value to check.
* @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
* @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
*/
function isIndex(value, length) {
const type = typeof value;
length = length == null ? MAX_SAFE_INTEGER : length;

return (
!!length &&
(type === "number" || (type !== "symbol" && reIsUint.test(value))) &&
value > -1 &&
value % 1 === 0 &&
value < length
);
}

export default isIndex;
36 changes: 36 additions & 0 deletions js/src/utils/lodash/isKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @ts-nocheck
import isSymbol from "./isSymbol.js";

/** Used to match property names within property paths. */
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/;
const reIsPlainProp = /^\w*$/;

/**
* Checks if `value` is a property name and not a property path.
*
* @private
* @param {*} value The value to check.
* @param {Object} [object] The object to query keys on.
* @returns {boolean} Returns `true` if `value` is a property name, else `false`.
*/
function isKey(value, object) {
if (Array.isArray(value)) {
return false;
}
const type = typeof value;
if (
type === "number" ||
type === "boolean" ||
value == null ||
isSymbol(value)
) {
return true;
}
return (
reIsPlainProp.test(value) ||
!reIsDeepProp.test(value) ||
(object != null && value in Object(object))
);
}

export default isKey;
31 changes: 31 additions & 0 deletions js/src/utils/lodash/isObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @ts-nocheck

/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* isObject({})
* // => true
*
* isObject([1, 2, 3])
* // => true
*
* isObject(Function)
* // => true
*
* isObject(null)
* // => false
*/
function isObject(value) {
const type = typeof value;
return value != null && (type === "object" || type === "function");
}

export default isObject;
Loading

0 comments on commit de7416b

Please sign in to comment.