Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
[![NPM version](https://img.shields.io/npm/v/x-value?color=%23cb3837&style=flat-square)](https://www.npmjs.com/package/x-value) [![Repository package.json version](https://img.shields.io/github/package-json/v/vilic/x-value?color=%230969da&label=repo&style
X-Value (X stands for "cross") is a medium-somewhat-neutral runtime type validation library.
Comparing to alternatives like io-ts and Zod, X-Value uses medium/value concept and allows values to be decoded from and encoded to different mediums.
yarn add x-value
# or
npm install x-value
Defining types with X-Value is similar to io-ts/Zod.
import * as x from 'x-value';
const Oops = x.object({
foo: x.string,
bar: x.optional(x.number),
});
const Rock = x.record(x.string, x.number);
const Aha = x.array(Oops);
const Tick = x.tuple(x.string, x.number);
const Um = x.union(Oops, x.boolean);
const I = x.intersection(
Oops,
x.object({
yoha: x.boolean,
}),
);
Get static type of type object:
type Oops = x.TypeOf<typeof Oops>;
type JSONOops = x.MediumTypeOf<typeof Oops, x.JSONTypes>;
Refine type:
const Email = x.string.refine(value => value.includes('@'));
// Or with refined or nominal type.
const Email = x.string.refine<`${string}@${string}`>(value =>
value.includes('@'),
);
const Email = x.string.refine<Nominal<'email'>>(value => value.includes('@'));
Decode from medium:
let value = Oops.decode(x.json, '{"foo":"abc","bar":123}');
Encode to medium:
let json = Oops.encode(x.json, {foo: 'abc', bar: 123});
Transform from medium to medium:
let json = Oops.transform(x.queryString, x.json, 'foo=abc&bar=123');
Type is
guard:
if (Oops.is(value)) {
// ...
}
Type satisfies
assertion (will throw if does not satisfy):
let oops = Oops.satisfies(value);
Diagnose for type issues:
let issues = Oops.diagnose(value);
Mediums are what's used to store values: JSON strings, query strings, buffers etc.
For example, a string "2022-03-31T16:00:00.000Z"
in JSON medium with type Date
represents value new Date('2022-03-31T16:00:00.000Z')
.
Assuming we have 3 mediums: browser
, server
, rpc
; and 2 types: ObjectId
, Date
. Their types in mediums and value are listed below.
Type\Medium | Browser | RPC | Server | Value |
---|---|---|---|---|
ObjectId | string | packed as string | ObjectId | string |
Date | Date | packed as string | Date | Date |
We can encode values to mediums:
let id = '6246056b1be8cbf6ca18401f';
ObjectId.encode(browser, id); // string '6246056b1be8cbf6ca18401f'
ObjectId.encode(rpc, id); // packed string '"6246056b1be8cbf6ca18401f"'
ObjectId.encode(server, id); // new ObjectId('6246056b1be8cbf6ca18401f')
let date = new Date('2022-03-31T16:00:00.000Z');
Date.encode(browser, date); // new Date('2022-03-31T16:00:00.000Z')
Date.encode(rpc, date); // packed string '"2022-03-31T16:00:00.000Z"'
Date.encode(server, date); // new Date('2022-03-31T16:00:00.000Z')
Or decode packed data of mediums to values:
// All result in '6246056b1be8cbf6ca18401f'
ObjectId.decode(browser, '6246056b1be8cbf6ca18401f');
ObjectId.decode(rpc, '"6246056b1be8cbf6ca18401f"');
ObjectId.decode(server, new ObjectId('6246056b1be8cbf6ca18401f'));
// All result in new Date('2022-03-31T16:00:00.000Z')
Date.decode(browser, new Date('2022-03-31T16:00:00.000Z'));
Date.decode(rpc, '"2022-03-31T16:00:00.000Z"');
Date.decode(server, new Date('2022-03-31T16:00:00.000Z'));
Ideally there's no need to have "value" as a separate concept because it's essentially "ECMAScript runtime medium". But to make decode/encode easier among different mediums, "value" is promoted as an interchangeable medium.
Before we can add medium support for a new type of atomic value, we need to add new atomic value. It is quite easy to do so:
import * as x from 'x-value';
// 1. Create a symbol for the new atomic type.
const newAtomicTypeSymbol = Symbol();
// 3. Create the new atomic type with constraint.
const NewAtomic = x.atomic(newAtomicTypeSymbol, value =>
Buffer.isBuffer(value),
);
declare global {
namespace XValue {
interface Types {
// 2. Define the symbol to value type mapping.
[newAtomicTypeSymbol]: Buffer;
}
}
}
After creating the new atomic type, we need to create/extend a new medium that supports this type:
interface SuperJSONTypes extends x.JSONTypes {
// 1. Define the unpacked type for decode/encode operation.
[newAtomicTypeSymbol]: string;
}
const superJSON = x.json.extend<SuperJSONTypes>('Super JSON', {
codecs: {
// 2. Define the codec.
[newAtomicTypeSymbol]: {
decode(value) {
if (typeof value !== 'string') {
throw new TypeError(
`Expected hex string, getting ${Object.prototype.toString.call(
value,
)}`,
);
}
return Buffer.from(value, 'hex');
},
encode(value) {
return value.toString('hex');
},
},
},
});
When decode()
from a medium, X-Value unpacks data for a structured input (e.g., JSON.parse()
). It packs the data again on encode()
(e.g., JSON.stringify()
).
For medium that requires packing:
interface PackedTypes {
// 1. Define the packed type.
packed: string;
}
const packed = x.medium<PackedTypes>('Packed ', {
// 2. Define packing methods.
packing: {
pack(data) {
return JSON.stringify(data);
},
unpack(json) {
return JSON.parse(json);
},
},
});
The
superJSON
medium is actually a packed medium. However, the related definitions are inherited fromx.JSONTypes
.
MIT License.
FAQs
[![NPM version](https://img.shields.io/npm/v/x-value?color=%23cb3837&style=flat-square)](https://www.npmjs.com/package/x-value) [![Repository package.json version](https://img.shields.io/github/package-json/v/vilic/x-value?color=%230969da&label=repo&style
The npm package x-value receives a total of 12 weekly downloads. As such, x-value popularity was classified as not popular.
We found that x-value demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.