Skip to content

Commit c78b484

Browse files
fix(paths): fix #445, update toPath(), disallowProtoPath()
- update docs - add tests
1 parent ded7c5e commit c78b484

File tree

3 files changed

+39
-19
lines changed

3 files changed

+39
-19
lines changed

packages/paths/src/mutator.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
Path8,
1414
PathVal,
1515
} from "@thi.ng/api";
16-
import { disallowProtoPath, toPath } from "./path.js";
16+
import { disallowProtoPath } from "./path.js";
1717

1818
/**
1919
* Unchecked version of {@link defMutator}.
@@ -72,8 +72,7 @@ export function defMutator<T, A, B, C, D, E, F, G, H>(
7272
path: DeepPath<T, A, B, C, D, E, F, G, H>
7373
): Fn2<T, any, any>;
7474
export function defMutator(path: Path): any {
75-
const ks = toPath(path);
76-
disallowProtoPath(ks);
75+
const ks = disallowProtoPath(path);
7776
let [a, b, c, d] = ks;
7877
switch (ks.length) {
7978
case 0:

packages/paths/src/path.ts

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import type { NumOrString, Path } from "@thi.ng/api";
22
import { isArray } from "@thi.ng/checks/is-array";
3+
import { isNumber } from "@thi.ng/checks/is-number";
34
import { isProtoPath } from "@thi.ng/checks/is-proto-path";
45
import { isString } from "@thi.ng/checks/is-string";
5-
import { assert } from "@thi.ng/errors/assert";
6+
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
67

78
/**
89
* Converts the given key path to canonical form (array).
910
*
11+
* @remarks
12+
* If given path is an array, performs a safety check to ensure that all path
13+
* items are strings or numbers and that illegal paths like `[["__proto__"],
14+
* "foo"]` will be disallowed (throws an error).
15+
*
16+
* Also see {@link disallowProtoPath}.
17+
*
1018
* ```
1119
* toPath("a.b.c");
1220
* // ["a", "b", "c"]
@@ -20,16 +28,20 @@ import { assert } from "@thi.ng/errors/assert";
2028
*
2129
* @param path -
2230
*/
23-
export const toPath = (path: Path): readonly NumOrString[] =>
24-
isArray(path)
25-
? <any[]>path
26-
: isString(path)
27-
? path.length > 0
28-
? path.split(".")
29-
: []
30-
: path != null
31-
? [path]
32-
: [];
31+
export const toPath = (path: Path): readonly NumOrString[] => {
32+
if (isArray(path)) {
33+
if (!path.every((x) => isString(x) || isNumber(x))) __illegal(path);
34+
return <any[]>path;
35+
} else {
36+
return isString(path)
37+
? path.length > 0
38+
? path.split(".")
39+
: []
40+
: path != null
41+
? <any[]>[path]
42+
: [];
43+
}
44+
};
3345

3446
/**
3547
* Takes an arbitrary object and lookup path. Descends into object along
@@ -59,10 +71,11 @@ export const exists = (obj: any, path: Path) => {
5971
};
6072

6173
/**
62-
* Helper function to analyze given `path` using
74+
* Helper function. First converts given `path` using {@link toPath} and then
75+
* analyzes it via
6376
* [`isProtoPath()`](https://docs.thi.ng/umbrella/checks/functions/isProtoPath.html).
6477
* Throws an error if path contains any property which might lead to prototype
65-
* poisoning.
78+
* poisoning. Returns converted path if valid.
6679
*
6780
* @remarks
6881
* The following properties are considered illegal.
@@ -73,6 +86,12 @@ export const exists = (obj: any, path: Path) => {
7386
*
7487
* @param path -
7588
*/
76-
export const disallowProtoPath = (path: Path) => (
77-
assert(!isProtoPath(path), `unsafe path: '${path}'`), path
78-
);
89+
export const disallowProtoPath = (path: Path): readonly NumOrString[] => {
90+
const $path = toPath(path);
91+
if (isProtoPath($path)) __illegal(path);
92+
return $path;
93+
};
94+
95+
/** @internal */
96+
const __illegal = (path: any) =>
97+
illegalArgs(`illegal path: ${JSON.stringify(path)}`);

packages/paths/test/main.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,6 @@ test("exists", () => {
170170
test("mutIn", () => {
171171
const a: any = {};
172172
expect(() => mutIn(a, ["__proto__", "polluted"], true)).toThrow();
173+
expect(() => mutIn(a, <any>[["__proto__"], "polluted"], true)).toThrow();
174+
expect(() => mutIn(a, <any>[[["__proto__"]], "polluted"], true)).toThrow();
173175
});

0 commit comments

Comments
 (0)