GithubHelp home page GithubHelp logo

support for sanitization about runtypes HOT 5 OPEN

geekflyer avatar geekflyer commented on May 13, 2024 6
support for sanitization

from runtypes.

Comments (5)

kjvalencik avatar kjvalencik commented on May 13, 2024 8

I packaged my filtering into a small lib in case it's useful for anyone else. runtypes-filter.

Usage:

import { Literal, Number, Record } from "runtypes";
import CheckFilter from "runtypes-filter";
 
const Asteroid = Record({
    type: Literal("asteroid"),
    mass: Number
});
 
const filterAsteroid = CheckFilter(Asteroid);
 
const untrustedAsteroid: unknown = {
    type: "asteroid",
    mass: 100
};
 
const trustedAteroid = filterAsteroid(untrustedAsteroid);

from runtypes.

binki avatar binki commented on May 13, 2024 1

I think this is a feature that I want or similar. I’d like a clone() or copy() provided by runtypes which only copies the properties known by runtypes for a particular type. As the OP mentions, this would be useful in APIs. For example, I might have a RunType defined with properties I am willing to expose to a client in response to a request. But if I get the data from the database, it might include extra information which shouldn’t be disclosed to the client.

This library already has logic to iterate over properties in its check(). It’d be great if it could reuse this same logic to do a copy() that contains only properties specified in the runtype to sanitize output/avoid accidental disclosure of private data.

from runtypes.

pelotom avatar pelotom commented on May 13, 2024 1

That makes sense. I think it should be possible to write such a function against runtype's Reflect API. If so, I would say that it makes sense to define it as a separate library, so as to keep the core of runtypes as simple as possible. What do you think?

from runtypes.

pelotom avatar pelotom commented on May 13, 2024

Can you be more specific about what you're proposing, maybe with some example code? It's not clear to me what you mean by sanitization in this context.

from runtypes.

kjvalencik avatar kjvalencik commented on May 13, 2024

This is what I threw together real quick. It's not complete, but covers the types that I needed.

function assertUnreachable(_x: never): never {
	throw new Error('Uncovered branch');
}

function filter<T, R extends Runtype<T>>(t: R, x: T): T {
	const r = t.reflect;

	switch (r.tag) {
		case 'instanceof':
		case 'intersect':
		case 'function':
		case 'unknown':
			throw new Error(`Type "${r.tag}" is not filterable`);
		case 'never':
		case 'literal':
		case 'boolean':
		case 'number':
		case 'string':
		case 'void':
		case 'symbol':
			return x;
		case 'array':
			return (x as any).map((v: any) => filter(r.element, v));
		case 'tuple':
			return (x as any).map((v: any, i: number) => filter(r.components[i], v));
		case 'dictionary':
			return Object.entries(x).reduce((acc, [k, v]) => ({
				...acc,
				[k]: filter(r.value, v)
			}), <T>{});
		case 'partial':
		case 'record':
			return Object.entries(r.fields)
				.filter(([k]) => x.hasOwnProperty(k))
				.reduce(
					(acc, [k, v]) => ({
						...acc,
						[k]: filter(v, (x as any)[k] as any)
					}),
					<T>{}
				);
		case 'union':
			const alt = r.alternatives.find(a => a.guard(x));

			return filter(<any>alt, x);
		case 'constraint':
			return filter(<any>r.underlying, x);
		case 'brand':
			return filter(<any>r.entity, x);
	}

	assertUnreachable(r);
}

// Usage
const Thing = Record({
  a: string,
  b: number
});

const thing = { a: 'a', b: 1, c: 'extra' };
const filteredThing = filter(Thing, thing);

The most notable type that is missing is intersect. Intersect is weird because it supports polymorphic data that may or may not be filterable. For example, a string with properties set on it. The only straight forward approach that I could think of is create a version of filter for intersect that specializes on certain common monomorphic intersections (e.g., Record).

Another significant limitation is that this will explode at runtime for types that can't be filtered. This is fine for me because I'm using it on config loading. However, this could also be worked around by creating a filter method for the type that you need to filter. An exception could be thrown when generating the filter method--which would likely be at a top level that happens on start up. Something like,

const filter = filterable(Thing);
const filtered = filter(thing);

from runtypes.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.