Skip to content

Instantly share code, notes, and snippets.

@sebmck
Created May 3, 2024 10:26
Show Gist options
  • Save sebmck/4ae0e1ef7b00f4f26466caea212c2ec5 to your computer and use it in GitHub Desktop.
Save sebmck/4ae0e1ef7b00f4f26466caea212c2ec5 to your computer and use it in GitHub Desktop.
/**
* @returns `typeof value === 'object' && value != null && !Array.isArray(value)`
* @see {@link UntrustedObject} for type refinement details.
*/
function isUntrustedObject(value: unknown): value is UntrustedObject {
return typeof value === 'object' && value != null && !Array.isArray(value);
}
// Split a string into a union of all it's characters.
type SplitString<S extends string> = S extends `${infer First}${infer Rest}`
? First | SplitString<Rest>
: never;
// Valid JavaScript identifier start characters.
type IdentifierStart =
| SplitString<'abcdefghijklmnopqrstuvwxyz'>
| SplitString<'ABCDEFGHIJKLMNOPQRSTUVWXYZ'>
| SplitString<'_$'>;
type LiteralString = `${IdentifierStart}${string}`;
/**
* `UntrustedObject` provides a safe interface for untrusted objects. Examples of untrusted source
* include URL parameters, forms, cookies, JSON payloads, etc.
*
* It makes property access safe by only allowing literal access, and restricting wide `string`
* computed access.
*
* ```ts
* declare const obj: UnknownObject;
* obj.foo; // Allowed
* obj['foo']; // Allowed
*
* declare const key: string;
* obj[key]; // Error
* ```
*
* This blocks using the object as a map which is a common source of prototype pollution issues,
* while still allowing easy direct access to known properties for normalization.
*/
interface UntrustedObject {
// Only allow known strings
[key: LiteralString]: unknown;
// Not an array
[key: number]: never;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment