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

object-path

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

object-path - npm Package Compare versions

Comparing version 0.11.7 to 0.11.8

SECURITY.md

282

index.js

@@ -1,20 +0,21 @@

(function (root, factory){
'use strict';
(function (root, factory) {
'use strict'
/*istanbul ignore next:cant test*/
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = factory();
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
define([], factory)
} else {
// Browser globals
root.objectPath = factory();
root.objectPath = factory()
}
})(this, function(){
'use strict';
})(this, function () {
'use strict'
var toStr = Object.prototype.toString;
function hasOwnProperty(obj, prop) {
if(obj == null) {
var toStr = Object.prototype.toString
function hasOwnProperty (obj, prop) {
if (obj == null) {
return false

@@ -26,51 +27,51 @@ }

function isEmpty(value){
function isEmpty (value) {
if (!value) {
return true;
return true
}
if (isArray(value) && value.length === 0) {
return true;
return true
} else if (typeof value !== 'string') {
for (var i in value) {
if (hasOwnProperty(value, i)) {
return false;
}
for (var i in value) {
if (hasOwnProperty(value, i)) {
return false
}
return true;
}
return true
}
return false;
return false
}
function toString(type){
return toStr.call(type);
function toString (type) {
return toStr.call(type)
}
function isObject(obj){
return typeof obj === 'object' && toString(obj) === "[object Object]";
function isObject (obj) {
return typeof obj === 'object' && toString(obj) === '[object Object]'
}
var isArray = Array.isArray || function(obj){
var isArray = Array.isArray || function (obj) {
/*istanbul ignore next:cant test*/
return toStr.call(obj) === '[object Array]';
return toStr.call(obj) === '[object Array]'
}
function isBoolean(obj){
return typeof obj === 'boolean' || toString(obj) === '[object Boolean]';
function isBoolean (obj) {
return typeof obj === 'boolean' || toString(obj) === '[object Boolean]'
}
function getKey(key){
var intKey = parseInt(key);
function getKey (key) {
var intKey = parseInt(key)
if (intKey.toString() === key) {
return intKey;
return intKey
}
return key;
return key
}
function factory(options) {
function factory (options) {
options = options || {}
var objectPath = function(obj) {
return Object.keys(objectPath).reduce(function(proxy, prop) {
if(prop === 'create') {
return proxy;
var objectPath = function (obj) {
return Object.keys(objectPath).reduce(function (proxy, prop) {
if (prop === 'create') {
return proxy
}

@@ -80,8 +81,8 @@

if (typeof objectPath[prop] === 'function') {
proxy[prop] = objectPath[prop].bind(objectPath, obj);
proxy[prop] = objectPath[prop].bind(objectPath, obj)
}
return proxy;
}, {});
};
return proxy
}, {})
}

@@ -99,32 +100,44 @@ var hasShallowProperty

function getShallowProperty(obj, prop) {
function getShallowProperty (obj, prop) {
if (hasShallowProperty(obj, prop)) {
return obj[prop];
return obj[prop]
}
}
function set(obj, path, value, doNotReplace){
var getShallowPropertySafely
if (options.includeInheritedProps) {
getShallowPropertySafely = function (obj, currentPath) {
if (typeof currentPath !== 'string' && typeof currentPath !== 'number') {
currentPath = String(currentPath)
}
var currentValue = getShallowProperty(obj, currentPath)
if (currentPath === '__proto__' || currentPath === 'prototype' ||
(currentPath === 'constructor' && typeof currentValue === 'function')) {
throw new Error('For security reasons, object\'s magic properties cannot be set')
}
return currentValue
}
} else {
getShallowPropertySafely = function (obj, currentPath) {
return getShallowProperty(obj, currentPath)
}
}
function set (obj, path, value, doNotReplace) {
if (typeof path === 'number') {
path = [path];
path = [path]
}
if (!path || path.length === 0) {
return obj;
return obj
}
if (typeof path === 'string') {
return set(obj, path.split('.').map(getKey), value, doNotReplace);
return set(obj, path.split('.').map(getKey), value, doNotReplace)
}
var currentPath = path[0];
if (typeof currentPath !== 'string' && typeof currentPath !== 'number') {
currentPath = String(currentPath)
}
var currentValue = getShallowProperty(obj, currentPath);
if (options.includeInheritedProps && (currentPath === '__proto__' ||
(currentPath === 'constructor' && typeof currentValue === 'function'))) {
throw new Error('For security reasons, object\'s magic properties cannot be set')
}
var currentPath = path[0]
var currentValue = getShallowPropertySafely(obj, currentPath)
if (path.length === 1) {
if (currentValue === void 0 || !doNotReplace) {
obj[currentPath] = value;
obj[currentPath] = value
}
return currentValue;
return currentValue
}

@@ -134,10 +147,10 @@

//check if we assume an array
if(typeof path[1] === 'number') {
obj[currentPath] = [];
if (typeof path[1] === 'number') {
obj[currentPath] = []
} else {
obj[currentPath] = {};
obj[currentPath] = {}
}
}
return set(obj[currentPath], path.slice(1), value, doNotReplace);
return set(obj[currentPath], path.slice(1), value, doNotReplace)
}

@@ -147,165 +160,166 @@

if (typeof path === 'number') {
path = [path];
path = [path]
} else if (typeof path === 'string') {
path = path.split('.');
path = path.split('.')
}
if (!path || path.length === 0) {
return !!obj;
return !!obj
}
for (var i = 0; i < path.length; i++) {
var j = getKey(path[i]);
var j = getKey(path[i])
if((typeof j === 'number' && isArray(obj) && j < obj.length) ||
if ((typeof j === 'number' && isArray(obj) && j < obj.length) ||
(options.includeInheritedProps ? (j in Object(obj)) : hasOwnProperty(obj, j))) {
obj = obj[j];
obj = obj[j]
} else {
return false;
return false
}
}
return true;
};
return true
}
objectPath.ensureExists = function (obj, path, value){
return set(obj, path, value, true);
};
objectPath.ensureExists = function (obj, path, value) {
return set(obj, path, value, true)
}
objectPath.set = function (obj, path, value, doNotReplace){
return set(obj, path, value, doNotReplace);
};
objectPath.set = function (obj, path, value, doNotReplace) {
return set(obj, path, value, doNotReplace)
}
objectPath.insert = function (obj, path, value, at){
var arr = objectPath.get(obj, path);
at = ~~at;
objectPath.insert = function (obj, path, value, at) {
var arr = objectPath.get(obj, path)
at = ~~at
if (!isArray(arr)) {
arr = [];
objectPath.set(obj, path, arr);
arr = []
objectPath.set(obj, path, arr)
}
arr.splice(at, 0, value);
};
arr.splice(at, 0, value)
}
objectPath.empty = function(obj, path) {
objectPath.empty = function (obj, path) {
if (isEmpty(path)) {
return void 0;
return void 0
}
if (obj == null) {
return void 0;
return void 0
}
var value, i;
var value, i
if (!(value = objectPath.get(obj, path))) {
return void 0;
return void 0
}
if (typeof value === 'string') {
return objectPath.set(obj, path, '');
return objectPath.set(obj, path, '')
} else if (isBoolean(value)) {
return objectPath.set(obj, path, false);
return objectPath.set(obj, path, false)
} else if (typeof value === 'number') {
return objectPath.set(obj, path, 0);
return objectPath.set(obj, path, 0)
} else if (isArray(value)) {
value.length = 0;
value.length = 0
} else if (isObject(value)) {
for (i in value) {
if (hasShallowProperty(value, i)) {
delete value[i];
delete value[i]
}
}
} else {
return objectPath.set(obj, path, null);
return objectPath.set(obj, path, null)
}
};
}
objectPath.push = function (obj, path /*, values */){
var arr = objectPath.get(obj, path);
objectPath.push = function (obj, path /*, values */) {
var arr = objectPath.get(obj, path)
if (!isArray(arr)) {
arr = [];
objectPath.set(obj, path, arr);
arr = []
objectPath.set(obj, path, arr)
}
arr.push.apply(arr, Array.prototype.slice.call(arguments, 2));
};
arr.push.apply(arr, Array.prototype.slice.call(arguments, 2))
}
objectPath.coalesce = function (obj, paths, defaultValue) {
var value;
var value
for (var i = 0, len = paths.length; i < len; i++) {
if ((value = objectPath.get(obj, paths[i])) !== void 0) {
return value;
return value
}
}
return defaultValue;
};
return defaultValue
}
objectPath.get = function (obj, path, defaultValue){
objectPath.get = function (obj, path, defaultValue) {
if (typeof path === 'number') {
path = [path];
path = [path]
}
if (!path || path.length === 0) {
return obj;
return obj
}
if (obj == null) {
return defaultValue;
return defaultValue
}
if (typeof path === 'string') {
return objectPath.get(obj, path.split('.'), defaultValue);
return objectPath.get(obj, path.split('.'), defaultValue)
}
var currentPath = getKey(path[0]);
var nextObj = getShallowProperty(obj, currentPath)
var currentPath = getKey(path[0])
var nextObj = getShallowPropertySafely(obj, currentPath)
if (nextObj === void 0) {
return defaultValue;
return defaultValue
}
if (path.length === 1) {
return nextObj;
return nextObj
}
return objectPath.get(obj[currentPath], path.slice(1), defaultValue);
};
return objectPath.get(obj[currentPath], path.slice(1), defaultValue)
}
objectPath.del = function del(obj, path) {
objectPath.del = function del (obj, path) {
if (typeof path === 'number') {
path = [path];
path = [path]
}
if (obj == null) {
return obj;
return obj
}
if (isEmpty(path)) {
return obj;
return obj
}
if(typeof path === 'string') {
return objectPath.del(obj, path.split('.'));
if (typeof path === 'string') {
return objectPath.del(obj, path.split('.'))
}
var currentPath = getKey(path[0]);
var currentPath = getKey(path[0])
getShallowPropertySafely(obj, currentPath)
if (!hasShallowProperty(obj, currentPath)) {
return obj;
return obj
}
if(path.length === 1) {
if (path.length === 1) {
if (isArray(obj)) {
obj.splice(currentPath, 1);
obj.splice(currentPath, 1)
} else {
delete obj[currentPath];
delete obj[currentPath]
}
} else {
return objectPath.del(obj[currentPath], path.slice(1));
return objectPath.del(obj[currentPath], path.slice(1))
}
return obj;
return obj
}
return objectPath;
return objectPath
}
var mod = factory();
mod.create = factory;
var mod = factory()
mod.create = factory
mod.withInheritedProps = factory({includeInheritedProps: true})
return mod;
});
return mod
})
{
"name": "object-path",
"description": "Access deep object properties using a path",
"version": "0.11.7",
"version": "0.11.8",
"author": {

@@ -6,0 +6,0 @@ "name": "Mario Casciaro"

@@ -16,2 +16,6 @@

### 0.11.8
* **SECURITY FIX**. Fix a prototype pollution vulnerability in the `del()`, `empty()`, `push()`, `insert()` functions when using the "inherited props" mode (e.g. when a new `object-path` instance is created with the `includeInheritedProps` option set to `true` or when using the `withInheritedProps` default instance. To help with preventing this type of vulnerability in the client code, also the `get()` function will now throw an exception if an object's magic properties are accessed. The vulnerability does not exist in the default instance exposed by object path (e.g `objectPath.del()`) if using version >= `0.11.0`.
### 0.11.6

@@ -180,2 +184,4 @@

**NOTE**: For security reasons `object-path` will throw an exception when trying to access an object's magic properties (e.g. `__proto__`, `constructor`) when in "inherited props" mode.
### Immutability

@@ -182,0 +188,0 @@

@@ -136,2 +136,39 @@ 'use strict'

})
it('[security] should not get magic properties in default mode', function () {
expect(objectPath.get({}, '__proto__')).to.be.undefined
expect(objectPath.get({}, [['__proto__']])).to.be.undefined
function Clazz() {}
Clazz.prototype.test = []
expect(objectPath.get(new Clazz(), '__proto__')).to.be.undefined
expect(objectPath.get(new Clazz(), [['__proto__']])).to.be.undefined
expect(objectPath.get(new Clazz(), ['constructor', 'prototype'])).to.be.undefined
})
it('[security] should not get magic properties in inheritedProps mode', function () {
expect(function() {
objectPath.withInheritedProps.get({}, '__proto__')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.get({}, [['__proto__']])
}).to.throw('For security reasons')
function Clazz() {}
Clazz.prototype.test = 'original'
expect(function() {
objectPath.withInheritedProps.get(new Clazz(), '__proto__')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.get(new Clazz(), [['__proto__']])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.get(new Clazz(), ['constructor', 'prototype'])
}).to.throw('For security reasons')
})
})

@@ -248,5 +285,11 @@

objectPath.set({}, ['__proto__'], {})
expect(Object.prototype.toString).to.be.a('function')
function Clazz() {}
Clazz.prototype.test = 'original'
objectPath.set({}, ['__proto__'], {test: 'this is bad'})
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.set(new Clazz(), '__proto__.test', 'this is bad')

@@ -263,5 +306,6 @@ expect(Clazz.prototype.test).to.be.equal('original')

it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () {
expect(function() {objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad')})
.to.throw('For security reasons')
expect(Object.prototype.injected).to.be.undefined
expect(function() {
objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')

@@ -276,17 +320,21 @@ expect(function() {

expect(function() {objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad')})
.to.throw('For security reasons')
expect(Clazz.prototype.test).to.be.equal('original')
expect(function() {
objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad')})
.to.throw('For security reasons')
expect(Clazz.prototype.test).to.be.equal('original')
expect(function() {
objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')})
.to.throw('For security reasons')
expect(Object.prototype.injected).to.be.undefined
expect(function() {
objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')
expect(function() {objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')})
.to.throw('For security reasons')
expect(Object.prototype.injected).to.be.undefined
expect(function() {
objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')
})

@@ -335,2 +383,35 @@ })

it('[security] should not push within prototype properties in default mode', function () {
function Clazz() {}
Clazz.prototype.test = []
objectPath.push(new Clazz(), '__proto__.test', 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
objectPath.push(new Clazz(), [['__proto__'], 'test'], 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
objectPath.push(new Clazz(), 'constructor.prototype.test', 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
})
it('[security] should not push within prototype properties in inheritedProps mode', function () {
function Clazz() {}
Clazz.prototype.test = []
expect(function() {
objectPath.withInheritedProps.push(new Clazz(), '__proto__.test', 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.push(new Clazz(), [['__proto__'], 'test'], 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.push(new Clazz(), 'constructor.prototype.test', 'pushed')
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
})
})

@@ -369,2 +450,63 @@

})
it('[security] should not set magic properties in default mode', function () {
objectPath.ensureExists({}, '__proto__.injected', 'this is bad')
expect(Object.prototype.injected).to.be.undefined
objectPath.ensureExists({}, [['__proto__'], 'injected'], 'this is bad')
expect(Object.prototype.injected).to.be.undefined
objectPath.ensureExists({}, ['__proto__'], {})
expect(Object.prototype.toString).to.be.a('function')
function Clazz() {}
Clazz.prototype.test = 'original'
objectPath.ensureExists({}, ['__proto__'], {test: 'this is bad'})
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.ensureExists(new Clazz(), '__proto__.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.ensureExists(new Clazz(), [['__proto__'], 'test'], 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
})
it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () {
expect(function() {objectPath.withInheritedProps.ensureExists({}, '__proto__.injected', 'this is bad')})
.to.throw('For security reasons')
expect(Object.prototype.injected).to.be.undefined
expect(function() {
objectPath.withInheritedProps.ensureExists({}, [['__proto__'], 'injected'], 'this is bad')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')
function Clazz() {}
Clazz.prototype.test = 'original'
expect(function() {
objectPath.withInheritedProps.ensureExists(new Clazz(), '__proto__.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.ensureExists({}, 'constructor.prototype.injected', 'this is OK')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.ensureExists({}, [['constructor'], 'prototype', 'injected'], 'this is bad')
expect(Object.prototype.injected).to.be.undefined
}).to.throw('For security reasons')
})
})

@@ -521,2 +663,36 @@

})
it('[security] should not empty prototype properties in default mode', function () {
function Clazz() {}
Clazz.prototype.test = 'original'
objectPath.empty(new Clazz(), '__proto__')
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.empty(new Clazz(), [['__proto__']])
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.empty(new Clazz(), 'constructor.prototype')
expect(Clazz.prototype.test).to.be.equal('original')
})
it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () {
function Clazz() {}
Clazz.prototype.test = 'original'
expect(function() {
objectPath.withInheritedProps.empty(new Clazz(), '__proto__')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.empty(new Clazz(), 'constructor.prototype')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.empty({}, [['constructor'], 'prototype'])
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
})
})

@@ -598,2 +774,52 @@

})
it('[security] should not delete prototype properties in default mode', function () {
objectPath.del({}, '__proto__.valueOf')
expect(Object.prototype.valueOf).to.be.a('function')
objectPath.del({}, [['__proto__'], 'valueOf'])
expect(Object.prototype.valueOf).to.be.a('function')
function Clazz() {}
Clazz.prototype.test = 'original'
objectPath.del(new Clazz(), '__proto__.test')
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.del(new Clazz(), [['__proto__'], 'test'])
expect(Clazz.prototype.test).to.be.equal('original')
objectPath.del(new Clazz(), 'constructor.prototype.test')
expect(Clazz.prototype.test).to.be.equal('original')
})
it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () {
expect(function() {
objectPath.withInheritedProps.del({}, '__proto__.valueOf')
expect(Object.prototype.valueOf).to.be.a('function')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.del({}, [['__proto__'], 'valueOf'])
expect(Object.prototype.valueOf).to.be.a('function')
}).to.throw('For security reasons')
function Clazz() {}
Clazz.prototype.test = 'original'
expect(function() {
objectPath.withInheritedProps.del(new Clazz(), '__proto__.test')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.del(new Clazz(), 'constructor.prototype.test', 'this is bad')
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.del({}, [['constructor'], 'prototype', 'test'])
expect(Clazz.prototype.test).to.be.equal('original')
}).to.throw('For security reasons')
})
})

@@ -641,2 +867,41 @@

})
it('[security] should not insert within prototype properties in default mode', function () {
function Clazz() {}
Clazz.prototype.test = []
objectPath.insert(new Clazz(), '__proto__.test', 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
objectPath.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
objectPath.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
})
it('[security] should not insert within prototype properties in inheritedProps mode', function () {
function Clazz() {}
Clazz.prototype.test = []
expect(function() {
objectPath.withInheritedProps.insert(new Clazz(), '__proto__.test', 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
expect(function() {
objectPath.withInheritedProps.insert(new Clazz().constructor, 'prototype.test', 'inserted', 0)
expect(Clazz.prototype.test).to.be.deep.equal([])
}).to.throw('For security reasons')
})
})

@@ -643,0 +908,0 @@

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