@pawells/typescript-common 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/build/array/array-chunk.d.ts +10 -0
- package/build/array/array-chunk.d.ts.map +1 -0
- package/build/array/array-chunk.js +19 -0
- package/build/array/array-chunk.js.map +1 -0
- package/build/array/array-compact.d.ts +18 -0
- package/build/array/array-compact.d.ts.map +1 -0
- package/build/array/array-compact.js +22 -0
- package/build/array/array-compact.js.map +1 -0
- package/build/array/array-contains.d.ts +9 -0
- package/build/array/array-contains.d.ts.map +1 -0
- package/build/array/array-contains.js +13 -0
- package/build/array/array-contains.js.map +1 -0
- package/build/array/array-count-by.d.ts +21 -0
- package/build/array/array-count-by.d.ts.map +1 -0
- package/build/array/array-count-by.js +29 -0
- package/build/array/array-count-by.js.map +1 -0
- package/build/array/array-difference.d.ts +25 -0
- package/build/array/array-difference.d.ts.map +1 -0
- package/build/array/array-difference.js +34 -0
- package/build/array/array-difference.js.map +1 -0
- package/build/array/array-element.d.ts +7 -0
- package/build/array/array-element.d.ts.map +1 -0
- package/build/array/array-element.js +2 -0
- package/build/array/array-element.js.map +1 -0
- package/build/array/array-filter.d.ts +37 -0
- package/build/array/array-filter.d.ts.map +1 -0
- package/build/array/array-filter.js +78 -0
- package/build/array/array-filter.js.map +1 -0
- package/build/array/array-flatten.d.ts +16 -0
- package/build/array/array-flatten.d.ts.map +1 -0
- package/build/array/array-flatten.js +20 -0
- package/build/array/array-flatten.js.map +1 -0
- package/build/array/array-group-by.d.ts +13 -0
- package/build/array/array-group-by.d.ts.map +1 -0
- package/build/array/array-group-by.js +24 -0
- package/build/array/array-group-by.js.map +1 -0
- package/build/array/array-intersection.d.ts +25 -0
- package/build/array/array-intersection.d.ts.map +1 -0
- package/build/array/array-intersection.js +39 -0
- package/build/array/array-intersection.js.map +1 -0
- package/build/array/array-partition.d.ts +19 -0
- package/build/array/array-partition.d.ts.map +1 -0
- package/build/array/array-partition.js +32 -0
- package/build/array/array-partition.js.map +1 -0
- package/build/array/array-range.d.ts +18 -0
- package/build/array/array-range.d.ts.map +1 -0
- package/build/array/array-range.js +30 -0
- package/build/array/array-range.js.map +1 -0
- package/build/array/array-sample.d.ts +29 -0
- package/build/array/array-sample.d.ts.map +1 -0
- package/build/array/array-sample.js +19 -0
- package/build/array/array-sample.js.map +1 -0
- package/build/array/array-shuffle.d.ts +8 -0
- package/build/array/array-shuffle.d.ts.map +1 -0
- package/build/array/array-shuffle.js +19 -0
- package/build/array/array-shuffle.js.map +1 -0
- package/build/array/array-sort-by.d.ts +20 -0
- package/build/array/array-sort-by.d.ts.map +1 -0
- package/build/array/array-sort-by.js +31 -0
- package/build/array/array-sort-by.js.map +1 -0
- package/build/array/array-zip.d.ts +22 -0
- package/build/array/array-zip.d.ts.map +1 -0
- package/build/array/array-zip.js +25 -0
- package/build/array/array-zip.js.map +1 -0
- package/build/array/array.test.d.ts +2 -0
- package/build/array/array.test.d.ts.map +1 -0
- package/build/array/array.test.js +347 -0
- package/build/array/array.test.js.map +1 -0
- package/build/array/index.d.ts +27 -0
- package/build/array/index.d.ts.map +1 -0
- package/build/array/index.js +25 -0
- package/build/array/index.js.map +1 -0
- package/build/array/types.d.ts +32 -0
- package/build/array/types.d.ts.map +1 -0
- package/build/array/types.js +2 -0
- package/build/array/types.js.map +1 -0
- package/build/array/unique.d.ts +8 -0
- package/build/array/unique.d.ts.map +1 -0
- package/build/array/unique.js +13 -0
- package/build/array/unique.js.map +1 -0
- package/build/enum/enum-entries.d.ts +11 -0
- package/build/enum/enum-entries.d.ts.map +1 -0
- package/build/enum/enum-entries.js +14 -0
- package/build/enum/enum-entries.js.map +1 -0
- package/build/enum/enum-key-by-value.d.ts +13 -0
- package/build/enum/enum-key-by-value.d.ts.map +1 -0
- package/build/enum/enum-key-by-value.js +21 -0
- package/build/enum/enum-key-by-value.js.map +1 -0
- package/build/enum/enum-keys.d.ts +10 -0
- package/build/enum/enum-keys.d.ts.map +1 -0
- package/build/enum/enum-keys.js +14 -0
- package/build/enum/enum-keys.js.map +1 -0
- package/build/enum/enum-safe-value.d.ts +14 -0
- package/build/enum/enum-safe-value.d.ts.map +1 -0
- package/build/enum/enum-safe-value.js +16 -0
- package/build/enum/enum-safe-value.js.map +1 -0
- package/build/enum/enum-values.d.ts +19 -0
- package/build/enum/enum-values.d.ts.map +1 -0
- package/build/enum/enum-values.js +25 -0
- package/build/enum/enum-values.js.map +1 -0
- package/build/enum/enum.test.d.ts +2 -0
- package/build/enum/enum.test.d.ts.map +1 -0
- package/build/enum/enum.test.js +122 -0
- package/build/enum/enum.test.js.map +1 -0
- package/build/enum/index.d.ts +17 -0
- package/build/enum/index.d.ts.map +1 -0
- package/build/enum/index.js +17 -0
- package/build/enum/index.js.map +1 -0
- package/build/enum/types.d.ts +9 -0
- package/build/enum/types.d.ts.map +1 -0
- package/build/enum/types.js +2 -0
- package/build/enum/types.js.map +1 -0
- package/build/enum/validate-enum-value.d.ts +13 -0
- package/build/enum/validate-enum-value.d.ts.map +1 -0
- package/build/enum/validate-enum-value.js +18 -0
- package/build/enum/validate-enum-value.js.map +1 -0
- package/build/function/compose.d.ts +37 -0
- package/build/function/compose.d.ts.map +1 -0
- package/build/function/compose.js +7 -0
- package/build/function/compose.js.map +1 -0
- package/build/function/debounce.d.ts +25 -0
- package/build/function/debounce.d.ts.map +1 -0
- package/build/function/debounce.js +37 -0
- package/build/function/debounce.js.map +1 -0
- package/build/function/function.test.d.ts +2 -0
- package/build/function/function.test.d.ts.map +1 -0
- package/build/function/function.test.js +158 -0
- package/build/function/function.test.js.map +1 -0
- package/build/function/index.d.ts +17 -0
- package/build/function/index.d.ts.map +1 -0
- package/build/function/index.js +16 -0
- package/build/function/index.js.map +1 -0
- package/build/function/memoize.d.ts +23 -0
- package/build/function/memoize.d.ts.map +1 -0
- package/build/function/memoize.js +34 -0
- package/build/function/memoize.js.map +1 -0
- package/build/function/once.d.ts +17 -0
- package/build/function/once.d.ts.map +1 -0
- package/build/function/once.js +27 -0
- package/build/function/once.js.map +1 -0
- package/build/function/sleep.d.ts +20 -0
- package/build/function/sleep.d.ts.map +1 -0
- package/build/function/sleep.js +22 -0
- package/build/function/sleep.js.map +1 -0
- package/build/function/throttle.d.ts +17 -0
- package/build/function/throttle.d.ts.map +1 -0
- package/build/function/throttle.js +37 -0
- package/build/function/throttle.js.map +1 -0
- package/build/function/types.d.ts +5 -0
- package/build/function/types.d.ts.map +1 -0
- package/build/function/types.js +2 -0
- package/build/function/types.js.map +1 -0
- package/build/index.d.ts +32 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +51 -0
- package/build/index.js.map +1 -0
- package/build/object/assert-object.d.ts +8 -0
- package/build/object/assert-object.d.ts.map +1 -0
- package/build/object/assert-object.js +10 -0
- package/build/object/assert-object.js.map +1 -0
- package/build/object/clone.d.ts +39 -0
- package/build/object/clone.d.ts.map +1 -0
- package/build/object/clone.js +85 -0
- package/build/object/clone.js.map +1 -0
- package/build/object/equals.d.ts +44 -0
- package/build/object/equals.d.ts.map +1 -0
- package/build/object/equals.js +104 -0
- package/build/object/equals.js.map +1 -0
- package/build/object/filter-cached.d.ts +9 -0
- package/build/object/filter-cached.d.ts.map +1 -0
- package/build/object/filter-cached.js +108 -0
- package/build/object/filter-cached.js.map +1 -0
- package/build/object/filter.d.ts +85 -0
- package/build/object/filter.d.ts.map +1 -0
- package/build/object/filter.js +248 -0
- package/build/object/filter.js.map +1 -0
- package/build/object/has-circular-reference.d.ts +8 -0
- package/build/object/has-circular-reference.d.ts.map +1 -0
- package/build/object/has-circular-reference.js +40 -0
- package/build/object/has-circular-reference.js.map +1 -0
- package/build/object/hash.d.ts +20 -0
- package/build/object/hash.d.ts.map +1 -0
- package/build/object/hash.js +39 -0
- package/build/object/hash.js.map +1 -0
- package/build/object/index.d.ts +32 -0
- package/build/object/index.d.ts.map +1 -0
- package/build/object/index.js +30 -0
- package/build/object/index.js.map +1 -0
- package/build/object/key-value-pairs.d.ts +21 -0
- package/build/object/key-value-pairs.d.ts.map +1 -0
- package/build/object/key-value-pairs.js +28 -0
- package/build/object/key-value-pairs.js.map +1 -0
- package/build/object/map-cached.d.ts +9 -0
- package/build/object/map-cached.d.ts.map +1 -0
- package/build/object/map-cached.js +97 -0
- package/build/object/map-cached.js.map +1 -0
- package/build/object/map.d.ts +29 -0
- package/build/object/map.d.ts.map +1 -0
- package/build/object/map.js +40 -0
- package/build/object/map.js.map +1 -0
- package/build/object/merge.d.ts +14 -0
- package/build/object/merge.d.ts.map +1 -0
- package/build/object/merge.js +41 -0
- package/build/object/merge.js.map +1 -0
- package/build/object/object-diff.d.ts +40 -0
- package/build/object/object-diff.d.ts.map +1 -0
- package/build/object/object-diff.js +45 -0
- package/build/object/object-diff.js.map +1 -0
- package/build/object/object-flatten.d.ts +22 -0
- package/build/object/object-flatten.d.ts.map +1 -0
- package/build/object/object-flatten.js +38 -0
- package/build/object/object-flatten.js.map +1 -0
- package/build/object/object-invert.d.ts +20 -0
- package/build/object/object-invert.d.ts.map +1 -0
- package/build/object/object-invert.js +26 -0
- package/build/object/object-invert.js.map +1 -0
- package/build/object/object.test.d.ts +2 -0
- package/build/object/object.test.d.ts.map +1 -0
- package/build/object/object.test.js +432 -0
- package/build/object/object.test.js.map +1 -0
- package/build/object/omit.d.ts +8 -0
- package/build/object/omit.d.ts.map +1 -0
- package/build/object/omit.js +17 -0
- package/build/object/omit.js.map +1 -0
- package/build/object/pick.d.ts +50 -0
- package/build/object/pick.d.ts.map +1 -0
- package/build/object/pick.js +60 -0
- package/build/object/pick.js.map +1 -0
- package/build/object/property-paths.d.ts +115 -0
- package/build/object/property-paths.d.ts.map +1 -0
- package/build/object/property-paths.js +170 -0
- package/build/object/property-paths.js.map +1 -0
- package/build/object/security-utils.d.ts +59 -0
- package/build/object/security-utils.d.ts.map +1 -0
- package/build/object/security-utils.js +165 -0
- package/build/object/security-utils.js.map +1 -0
- package/build/object/sort-keys.d.ts +26 -0
- package/build/object/sort-keys.d.ts.map +1 -0
- package/build/object/sort-keys.js +52 -0
- package/build/object/sort-keys.js.map +1 -0
- package/build/object/types.d.ts +152 -0
- package/build/object/types.d.ts.map +1 -0
- package/build/object/types.js +6 -0
- package/build/object/types.js.map +1 -0
- package/build/string/case-conversion.d.ts +50 -0
- package/build/string/case-conversion.d.ts.map +1 -0
- package/build/string/case-conversion.js +94 -0
- package/build/string/case-conversion.js.map +1 -0
- package/build/string/formatting.d.ts +108 -0
- package/build/string/formatting.d.ts.map +1 -0
- package/build/string/formatting.js +171 -0
- package/build/string/formatting.js.map +1 -0
- package/build/string/index.d.ts +15 -0
- package/build/string/index.d.ts.map +1 -0
- package/build/string/index.js +14 -0
- package/build/string/index.js.map +1 -0
- package/build/string/string.test.d.ts +2 -0
- package/build/string/string.test.d.ts.map +1 -0
- package/build/string/string.test.js +321 -0
- package/build/string/string.test.js.map +1 -0
- package/build/string/transformation.d.ts +18 -0
- package/build/string/transformation.d.ts.map +1 -0
- package/build/string/transformation.js +32 -0
- package/build/string/transformation.js.map +1 -0
- package/build/string/types.d.ts +44 -0
- package/build/string/types.d.ts.map +1 -0
- package/build/string/types.js +2 -0
- package/build/string/types.js.map +1 -0
- package/build/string/validation.d.ts +18 -0
- package/build/string/validation.d.ts.map +1 -0
- package/build/string/validation.js +26 -0
- package/build/string/validation.js.map +1 -0
- package/build/time/elapsed-time/constants.d.ts +10 -0
- package/build/time/elapsed-time/constants.d.ts.map +1 -0
- package/build/time/elapsed-time/constants.js +96 -0
- package/build/time/elapsed-time/constants.js.map +1 -0
- package/build/time/elapsed-time/elapsed-time.d.ts +412 -0
- package/build/time/elapsed-time/elapsed-time.d.ts.map +1 -0
- package/build/time/elapsed-time/elapsed-time.js +652 -0
- package/build/time/elapsed-time/elapsed-time.js.map +1 -0
- package/build/time/elapsed-time/types.d.ts +150 -0
- package/build/time/elapsed-time/types.d.ts.map +1 -0
- package/build/time/elapsed-time/types.js +2 -0
- package/build/time/elapsed-time/types.js.map +1 -0
- package/build/time/elapsed-time/utils.d.ts +18 -0
- package/build/time/elapsed-time/utils.d.ts.map +1 -0
- package/build/time/elapsed-time/utils.js +24 -0
- package/build/time/elapsed-time/utils.js.map +1 -0
- package/build/time/index.d.ts +18 -0
- package/build/time/index.d.ts.map +1 -0
- package/build/time/index.js +17 -0
- package/build/time/index.js.map +1 -0
- package/build/time/stopwatch/entry-types.d.ts +13 -0
- package/build/time/stopwatch/entry-types.d.ts.map +1 -0
- package/build/time/stopwatch/entry-types.js +2 -0
- package/build/time/stopwatch/entry-types.js.map +1 -0
- package/build/time/stopwatch/entry.d.ts +80 -0
- package/build/time/stopwatch/entry.d.ts.map +1 -0
- package/build/time/stopwatch/entry.js +105 -0
- package/build/time/stopwatch/entry.js.map +1 -0
- package/build/time/stopwatch/stopwatch.d.ts +232 -0
- package/build/time/stopwatch/stopwatch.d.ts.map +1 -0
- package/build/time/stopwatch/stopwatch.js +315 -0
- package/build/time/stopwatch/stopwatch.js.map +1 -0
- package/build/time/time.test.d.ts +2 -0
- package/build/time/time.test.d.ts.map +1 -0
- package/build/time/time.test.js +211 -0
- package/build/time/time.test.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new object with only the specified keys from the source object.
|
|
3
|
+
* Type-safe implementation that preserves the original property types.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The type of the source object
|
|
6
|
+
* @template K - The keys to pick (must be keys of T)
|
|
7
|
+
* @param obj - Source object to pick properties from
|
|
8
|
+
* @param keys - Array of keys to include in the result
|
|
9
|
+
* @returns New object containing only the specified keys with their original types
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* interface User {
|
|
14
|
+
* id: number;
|
|
15
|
+
* name: string;
|
|
16
|
+
* email: string;
|
|
17
|
+
* password: string;
|
|
18
|
+
* age: number;
|
|
19
|
+
* active: boolean;
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* const user: User = {
|
|
23
|
+
* id: 1,
|
|
24
|
+
* name: 'John Doe',
|
|
25
|
+
* email: 'john@example.com',
|
|
26
|
+
* password: 'secret',
|
|
27
|
+
* age: 30,
|
|
28
|
+
* active: true
|
|
29
|
+
* };
|
|
30
|
+
*
|
|
31
|
+
* // Pick specific properties for API response
|
|
32
|
+
* const publicUser = ObjectPick(user, ['id', 'name', 'email']);
|
|
33
|
+
* // Result: { id: 1, name: 'John Doe', email: 'john@example.com' }
|
|
34
|
+
* // Type: Pick<User, 'id' | 'name' | 'email'>
|
|
35
|
+
*
|
|
36
|
+
* // Pick minimal user info
|
|
37
|
+
* const minimalUser = ObjectPick(user, ['id', 'name']);
|
|
38
|
+
* // Result: { id: 1, name: 'John Doe' }
|
|
39
|
+
* // Type: Pick<User, 'id' | 'name'>
|
|
40
|
+
*
|
|
41
|
+
* // Type safety - prevents picking non-existent properties
|
|
42
|
+
* // const invalid = ObjectPick(user, ['id', 'nonExistent']); // TypeScript error
|
|
43
|
+
*
|
|
44
|
+
* // Handle null/undefined safely
|
|
45
|
+
* ObjectPick(null, ['id']); // Returns {}
|
|
46
|
+
* ObjectPick(undefined, ['name']); // Returns {}
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function ObjectPick(obj, keys) {
|
|
50
|
+
if (!obj) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
return keys.reduce((result, key) => {
|
|
54
|
+
if (Object.hasOwn(obj, key)) {
|
|
55
|
+
result[key] = obj[key];
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}, {});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=pick.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick.js","sourceRoot":"","sources":["../../src/object/pick.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,UAAU,UAAU,CAAsC,GAAM,EAAE,IAAS;IAChF,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,OAAO,EAAgB,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QAClC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC,EAAE,EAAgB,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safely gets a nested property from an object using a path string with dot notation.
|
|
3
|
+
* Returns the default value if the path doesn't exist or any intermediate value is null/undefined.
|
|
4
|
+
*
|
|
5
|
+
* **Security Features:**
|
|
6
|
+
* - Prevents access to dangerous internal properties
|
|
7
|
+
* - Validates property paths to prevent path traversal attacks
|
|
8
|
+
* - Blocks access to prototype chain properties
|
|
9
|
+
*
|
|
10
|
+
* @template T - The expected type of the property value
|
|
11
|
+
* @param obj - Source object to navigate
|
|
12
|
+
* @param path - Path to the property using dot notation (e.g., 'user.address.street')
|
|
13
|
+
* @param defaultValue - Default value to return if the path doesn't exist
|
|
14
|
+
* @returns The value at the path or the default value
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const user = {
|
|
19
|
+
* name: 'John',
|
|
20
|
+
* profile: {
|
|
21
|
+
* address: {
|
|
22
|
+
* street: '123 Main St',
|
|
23
|
+
* city: 'NYC',
|
|
24
|
+
* coordinates: { lat: 40.7128, lng: -74.0060 }
|
|
25
|
+
* },
|
|
26
|
+
* preferences: {
|
|
27
|
+
* theme: 'dark',
|
|
28
|
+
* notifications: true
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* };
|
|
32
|
+
*
|
|
33
|
+
* // Basic property access
|
|
34
|
+
* ObjectGetPropertyByPath(user, 'name'); // 'John'
|
|
35
|
+
* ObjectGetPropertyByPath(user, 'profile.address.street'); // '123 Main St'
|
|
36
|
+
* ObjectGetPropertyByPath(user, 'profile.address.city'); // 'NYC'
|
|
37
|
+
*
|
|
38
|
+
* // Deep nested access
|
|
39
|
+
* ObjectGetPropertyByPath(user, 'profile.address.coordinates.lat'); // 40.7128
|
|
40
|
+
* ObjectGetPropertyByPath(user, 'profile.preferences.theme'); // 'dark'
|
|
41
|
+
*
|
|
42
|
+
* // Non-existent paths with defaults
|
|
43
|
+
* ObjectGetPropertyByPath(user, 'profile.avatar', 'default.jpg'); // 'default.jpg'
|
|
44
|
+
* ObjectGetPropertyByPath(user, 'settings.language', 'en'); // 'en'
|
|
45
|
+
* ObjectGetPropertyByPath(user, 'profile.social.twitter'); // undefined
|
|
46
|
+
*
|
|
47
|
+
* // Type safety with generics
|
|
48
|
+
* const lat = ObjectGetPropertyByPath<number>(user, 'profile.address.coordinates.lat', 0);
|
|
49
|
+
* const theme = ObjectGetPropertyByPath<string>(user, 'profile.preferences.theme', 'light');
|
|
50
|
+
*
|
|
51
|
+
* // Handles null/undefined safely
|
|
52
|
+
* ObjectGetPropertyByPath(null, 'any.path', 'default'); // 'default'
|
|
53
|
+
* ObjectGetPropertyByPath(user, '', 'default'); // 'default'
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function ObjectGetPropertyByPath<T = any>(obj: any, path: string, defaultValue?: T): T | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Sets a nested property on an object using a path string with dot notation.
|
|
59
|
+
* Automatically creates intermediate objects if they don't exist or are not objects.
|
|
60
|
+
* Modifies the original object in place.
|
|
61
|
+
*
|
|
62
|
+
* **Security Features:**
|
|
63
|
+
* - Prevents prototype pollution by blocking dangerous property names
|
|
64
|
+
* - Validates property paths to prevent path traversal attacks
|
|
65
|
+
* - Sanitizes input to prevent injection attacks
|
|
66
|
+
*
|
|
67
|
+
* @template T - The type of the value being set
|
|
68
|
+
* @param obj - Target object to modify
|
|
69
|
+
* @param path - Path to the property using dot notation (e.g., 'user.address.street')
|
|
70
|
+
* @param value - Value to set at the specified path
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const user = { name: 'John' };
|
|
75
|
+
*
|
|
76
|
+
* // Set simple nested properties
|
|
77
|
+
* ObjectSetPropertyByPath(user, 'profile.age', 30);
|
|
78
|
+
* ObjectSetPropertyByPath(user, 'profile.email', 'john@example.com');
|
|
79
|
+
* console.log(user);
|
|
80
|
+
* // {
|
|
81
|
+
* // name: 'John',
|
|
82
|
+
* // profile: { age: 30, email: 'john@example.com' }
|
|
83
|
+
* // }
|
|
84
|
+
*
|
|
85
|
+
* // Set deeply nested properties
|
|
86
|
+
* ObjectSetPropertyByPath(user, 'profile.address.street', '123 Main St');
|
|
87
|
+
* ObjectSetPropertyByPath(user, 'profile.address.city', 'NYC');
|
|
88
|
+
* ObjectSetPropertyByPath(user, 'profile.preferences.theme', 'dark');
|
|
89
|
+
* console.log(user);
|
|
90
|
+
* // {
|
|
91
|
+
* // name: 'John',
|
|
92
|
+
* // profile: {
|
|
93
|
+
* // age: 30,
|
|
94
|
+
* // email: 'john@example.com',
|
|
95
|
+
* // address: { street: '123 Main St', city: 'NYC' },
|
|
96
|
+
* // preferences: { theme: 'dark' }
|
|
97
|
+
* // }
|
|
98
|
+
* // }
|
|
99
|
+
*
|
|
100
|
+
* // Overwrite existing values
|
|
101
|
+
* ObjectSetPropertyByPath(user, 'profile.age', 31);
|
|
102
|
+
* ObjectSetPropertyByPath(user, 'profile.address.street', '456 Oak Ave');
|
|
103
|
+
*
|
|
104
|
+
* // Handle different value types
|
|
105
|
+
* ObjectSetPropertyByPath(user, 'profile.active', true);
|
|
106
|
+
* ObjectSetPropertyByPath(user, 'profile.tags', ['developer', 'javascript']);
|
|
107
|
+
* ObjectSetPropertyByPath(user, 'profile.metadata.created', new Date());
|
|
108
|
+
*
|
|
109
|
+
* // Safe handling of edge cases
|
|
110
|
+
* ObjectSetPropertyByPath(null, 'any.path', 'value'); // No effect, safely ignored
|
|
111
|
+
* ObjectSetPropertyByPath(user, '', 'value'); // No effect, safely ignored
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function ObjectSetPropertyByPath<T = any>(obj: any, path: string, value: T): void;
|
|
115
|
+
//# sourceMappingURL=property-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"property-paths.d.ts","sourceRoot":"","sources":["../../src/object/property-paths.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAgCxG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAoCvF"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { isPropertyPathSafe, isPropertyKeySafe } from './security-utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Safely gets a nested property from an object using a path string with dot notation.
|
|
4
|
+
* Returns the default value if the path doesn't exist or any intermediate value is null/undefined.
|
|
5
|
+
*
|
|
6
|
+
* **Security Features:**
|
|
7
|
+
* - Prevents access to dangerous internal properties
|
|
8
|
+
* - Validates property paths to prevent path traversal attacks
|
|
9
|
+
* - Blocks access to prototype chain properties
|
|
10
|
+
*
|
|
11
|
+
* @template T - The expected type of the property value
|
|
12
|
+
* @param obj - Source object to navigate
|
|
13
|
+
* @param path - Path to the property using dot notation (e.g., 'user.address.street')
|
|
14
|
+
* @param defaultValue - Default value to return if the path doesn't exist
|
|
15
|
+
* @returns The value at the path or the default value
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const user = {
|
|
20
|
+
* name: 'John',
|
|
21
|
+
* profile: {
|
|
22
|
+
* address: {
|
|
23
|
+
* street: '123 Main St',
|
|
24
|
+
* city: 'NYC',
|
|
25
|
+
* coordinates: { lat: 40.7128, lng: -74.0060 }
|
|
26
|
+
* },
|
|
27
|
+
* preferences: {
|
|
28
|
+
* theme: 'dark',
|
|
29
|
+
* notifications: true
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* // Basic property access
|
|
35
|
+
* ObjectGetPropertyByPath(user, 'name'); // 'John'
|
|
36
|
+
* ObjectGetPropertyByPath(user, 'profile.address.street'); // '123 Main St'
|
|
37
|
+
* ObjectGetPropertyByPath(user, 'profile.address.city'); // 'NYC'
|
|
38
|
+
*
|
|
39
|
+
* // Deep nested access
|
|
40
|
+
* ObjectGetPropertyByPath(user, 'profile.address.coordinates.lat'); // 40.7128
|
|
41
|
+
* ObjectGetPropertyByPath(user, 'profile.preferences.theme'); // 'dark'
|
|
42
|
+
*
|
|
43
|
+
* // Non-existent paths with defaults
|
|
44
|
+
* ObjectGetPropertyByPath(user, 'profile.avatar', 'default.jpg'); // 'default.jpg'
|
|
45
|
+
* ObjectGetPropertyByPath(user, 'settings.language', 'en'); // 'en'
|
|
46
|
+
* ObjectGetPropertyByPath(user, 'profile.social.twitter'); // undefined
|
|
47
|
+
*
|
|
48
|
+
* // Type safety with generics
|
|
49
|
+
* const lat = ObjectGetPropertyByPath<number>(user, 'profile.address.coordinates.lat', 0);
|
|
50
|
+
* const theme = ObjectGetPropertyByPath<string>(user, 'profile.preferences.theme', 'light');
|
|
51
|
+
*
|
|
52
|
+
* // Handles null/undefined safely
|
|
53
|
+
* ObjectGetPropertyByPath(null, 'any.path', 'default'); // 'default'
|
|
54
|
+
* ObjectGetPropertyByPath(user, '', 'default'); // 'default'
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function ObjectGetPropertyByPath(obj, path, defaultValue) {
|
|
58
|
+
if (!obj || !path) {
|
|
59
|
+
return defaultValue;
|
|
60
|
+
}
|
|
61
|
+
// Security validation: Check if the path is safe
|
|
62
|
+
if (!isPropertyPathSafe(path)) {
|
|
63
|
+
return defaultValue;
|
|
64
|
+
}
|
|
65
|
+
const keys = path.split('.');
|
|
66
|
+
let result = obj;
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
// Security check for each key
|
|
69
|
+
if (!isPropertyKeySafe(key)) {
|
|
70
|
+
return defaultValue;
|
|
71
|
+
}
|
|
72
|
+
if (result === null || result === undefined || typeof result !== 'object') {
|
|
73
|
+
return defaultValue;
|
|
74
|
+
}
|
|
75
|
+
// Use Object.hasOwnProperty to prevent prototype chain access
|
|
76
|
+
if (!Object.prototype.hasOwnProperty.call(result, key)) {
|
|
77
|
+
return defaultValue;
|
|
78
|
+
}
|
|
79
|
+
result = result[key];
|
|
80
|
+
}
|
|
81
|
+
return result === undefined ? defaultValue : result;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Sets a nested property on an object using a path string with dot notation.
|
|
85
|
+
* Automatically creates intermediate objects if they don't exist or are not objects.
|
|
86
|
+
* Modifies the original object in place.
|
|
87
|
+
*
|
|
88
|
+
* **Security Features:**
|
|
89
|
+
* - Prevents prototype pollution by blocking dangerous property names
|
|
90
|
+
* - Validates property paths to prevent path traversal attacks
|
|
91
|
+
* - Sanitizes input to prevent injection attacks
|
|
92
|
+
*
|
|
93
|
+
* @template T - The type of the value being set
|
|
94
|
+
* @param obj - Target object to modify
|
|
95
|
+
* @param path - Path to the property using dot notation (e.g., 'user.address.street')
|
|
96
|
+
* @param value - Value to set at the specified path
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const user = { name: 'John' };
|
|
101
|
+
*
|
|
102
|
+
* // Set simple nested properties
|
|
103
|
+
* ObjectSetPropertyByPath(user, 'profile.age', 30);
|
|
104
|
+
* ObjectSetPropertyByPath(user, 'profile.email', 'john@example.com');
|
|
105
|
+
* console.log(user);
|
|
106
|
+
* // {
|
|
107
|
+
* // name: 'John',
|
|
108
|
+
* // profile: { age: 30, email: 'john@example.com' }
|
|
109
|
+
* // }
|
|
110
|
+
*
|
|
111
|
+
* // Set deeply nested properties
|
|
112
|
+
* ObjectSetPropertyByPath(user, 'profile.address.street', '123 Main St');
|
|
113
|
+
* ObjectSetPropertyByPath(user, 'profile.address.city', 'NYC');
|
|
114
|
+
* ObjectSetPropertyByPath(user, 'profile.preferences.theme', 'dark');
|
|
115
|
+
* console.log(user);
|
|
116
|
+
* // {
|
|
117
|
+
* // name: 'John',
|
|
118
|
+
* // profile: {
|
|
119
|
+
* // age: 30,
|
|
120
|
+
* // email: 'john@example.com',
|
|
121
|
+
* // address: { street: '123 Main St', city: 'NYC' },
|
|
122
|
+
* // preferences: { theme: 'dark' }
|
|
123
|
+
* // }
|
|
124
|
+
* // }
|
|
125
|
+
*
|
|
126
|
+
* // Overwrite existing values
|
|
127
|
+
* ObjectSetPropertyByPath(user, 'profile.age', 31);
|
|
128
|
+
* ObjectSetPropertyByPath(user, 'profile.address.street', '456 Oak Ave');
|
|
129
|
+
*
|
|
130
|
+
* // Handle different value types
|
|
131
|
+
* ObjectSetPropertyByPath(user, 'profile.active', true);
|
|
132
|
+
* ObjectSetPropertyByPath(user, 'profile.tags', ['developer', 'javascript']);
|
|
133
|
+
* ObjectSetPropertyByPath(user, 'profile.metadata.created', new Date());
|
|
134
|
+
*
|
|
135
|
+
* // Safe handling of edge cases
|
|
136
|
+
* ObjectSetPropertyByPath(null, 'any.path', 'value'); // No effect, safely ignored
|
|
137
|
+
* ObjectSetPropertyByPath(user, '', 'value'); // No effect, safely ignored
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export function ObjectSetPropertyByPath(obj, path, value) {
|
|
141
|
+
if (!obj || !path) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Security validation: Check if the path is safe
|
|
145
|
+
if (!isPropertyPathSafe(path)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const keys = path.split('.');
|
|
149
|
+
const lastKey = keys.pop();
|
|
150
|
+
if (lastKey === undefined) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Additional security check for the final key
|
|
154
|
+
if (!isPropertyKeySafe(lastKey)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
let current = obj;
|
|
158
|
+
for (const key of keys) {
|
|
159
|
+
// Security check for each intermediate key
|
|
160
|
+
if (!isPropertyKeySafe(key)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (current[key] === null || current[key] === undefined || typeof current[key] !== 'object') {
|
|
164
|
+
current[key] = {};
|
|
165
|
+
}
|
|
166
|
+
current = current[key];
|
|
167
|
+
}
|
|
168
|
+
current[lastKey] = value;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=property-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"property-paths.js","sourceRoot":"","sources":["../../src/object/property-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,UAAU,uBAAuB,CAAU,GAAQ,EAAE,IAAY,EAAE,YAAgB;IACxF,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,MAAM,GAAG,GAAG,CAAC;IAEjB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,8BAA8B;QAC9B,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,YAAY,CAAC;QACrB,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC3E,OAAO,YAAY,CAAC;QACrB,CAAC;QAED,8DAA8D;QAC9D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;YACxD,OAAO,YAAY,CAAC;QACrB,CAAC;QAED,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAW,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,UAAU,uBAAuB,CAAU,GAAQ,EAAE,IAAY,EAAE,KAAQ;IAChF,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO;IACR,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,OAAO;IACR,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO;IACR,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,OAAO;IACR,CAAC;IAED,IAAI,OAAO,GAAG,GAAG,CAAC;IAElB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,2CAA2C;QAC3C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO;QACR,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC7F,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for object manipulation functions
|
|
3
|
+
* Provides protection against prototype pollution, path traversal, and other security vulnerabilities
|
|
4
|
+
*
|
|
5
|
+
* @author Security Auditor Agent
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Validates if a property key is safe to use (not dangerous for prototype pollution)
|
|
10
|
+
*
|
|
11
|
+
* @param key - The property key to validate
|
|
12
|
+
* @returns True if the key is safe, false if it's dangerous
|
|
13
|
+
*/
|
|
14
|
+
export declare function isPropertyKeySafe(key: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Validates a property path for security issues
|
|
17
|
+
*
|
|
18
|
+
* @param path - The property path to validate (dot notation)
|
|
19
|
+
* @returns True if the path is safe, false if it contains security risks
|
|
20
|
+
*/
|
|
21
|
+
export declare function isPropertyPathSafe(path: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Sanitizes a property key by removing or replacing dangerous characters
|
|
24
|
+
*
|
|
25
|
+
* @param key - The property key to sanitize
|
|
26
|
+
* @returns Sanitized key or null if the key is too dangerous to sanitize
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizePropertyKey(key: string): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Filters out dangerous keys from an object
|
|
31
|
+
*
|
|
32
|
+
* @param obj - The object to filter
|
|
33
|
+
* @returns New object with only safe keys
|
|
34
|
+
*/
|
|
35
|
+
export declare function filterDangerousKeys<T extends Record<string, any>>(obj: T): Partial<T>;
|
|
36
|
+
/**
|
|
37
|
+
* Interface for circular reference detector
|
|
38
|
+
*/
|
|
39
|
+
export interface ICircularReferenceDetector {
|
|
40
|
+
markVisited(obj: object): void;
|
|
41
|
+
isVisited(obj: object): boolean;
|
|
42
|
+
clear(): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a circular reference detection utility using WeakSet
|
|
46
|
+
*
|
|
47
|
+
* @returns Object with methods to track and check for circular references
|
|
48
|
+
*/
|
|
49
|
+
export declare function createCircularReferenceDetector(): ICircularReferenceDetector;
|
|
50
|
+
/**
|
|
51
|
+
* Validates input for common security issues.
|
|
52
|
+
* Designed for property path validation, not general string validation.
|
|
53
|
+
*
|
|
54
|
+
* @param input - Input to validate
|
|
55
|
+
* @param maxLength - Maximum allowed length
|
|
56
|
+
* @returns True if input is safe
|
|
57
|
+
*/
|
|
58
|
+
export declare function isInputSafe(input: any, maxLength?: number): boolean;
|
|
59
|
+
//# sourceMappingURL=security-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-utils.d.ts","sourceRoot":"","sources":["../../src/object/security-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAuBtD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkBxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAUrF;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,KAAK,IAAI,IAAI,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,IAAI,0BAA0B,CA6B5E;AAKD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,GAAE,MAAiC,GAAG,OAAO,CAqB7F"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for object manipulation functions
|
|
3
|
+
* Provides protection against prototype pollution, path traversal, and other security vulnerabilities
|
|
4
|
+
*
|
|
5
|
+
* @author Security Auditor Agent
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* List of dangerous property names that should be blocked to prevent prototype pollution.
|
|
10
|
+
* Only the three canonical prototype-pollution vectors are blocked; other Object.prototype
|
|
11
|
+
* methods (toString, valueOf, hasOwnProperty, etc.) are legitimate property names.
|
|
12
|
+
*/
|
|
13
|
+
const DANGEROUS_PROPERTY_NAMES = new Set([
|
|
14
|
+
'__proto__',
|
|
15
|
+
'constructor',
|
|
16
|
+
'prototype',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Regular expression patterns for detecting path traversal attempts
|
|
20
|
+
*/
|
|
21
|
+
const PATH_TRAVERSAL_PATTERNS = [
|
|
22
|
+
/%2e%2e/i, // URL encoded ..
|
|
23
|
+
/%252e%252e/i, // Double URL encoded ..
|
|
24
|
+
new RegExp(String.fromCharCode(0)), // Null byte injection
|
|
25
|
+
/%00/i, // URL encoded null byte
|
|
26
|
+
/\uFEFF|\uFFFE|\uFFFF/, // Unicode BOM and invalid characters
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Validates if a property key is safe to use (not dangerous for prototype pollution)
|
|
30
|
+
*
|
|
31
|
+
* @param key - The property key to validate
|
|
32
|
+
* @returns True if the key is safe, false if it's dangerous
|
|
33
|
+
*/
|
|
34
|
+
export function isPropertyKeySafe(key) {
|
|
35
|
+
if (typeof key !== 'string') {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
// Check against dangerous property names
|
|
39
|
+
if (DANGEROUS_PROPERTY_NAMES.has(key)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// Check for properties starting with __ (dunder methods)
|
|
43
|
+
if (key.startsWith('__')) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Check for path traversal patterns
|
|
47
|
+
for (const pattern of PATH_TRAVERSAL_PATTERNS) {
|
|
48
|
+
if (pattern.test(key)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validates a property path for security issues
|
|
56
|
+
*
|
|
57
|
+
* @param path - The property path to validate (dot notation)
|
|
58
|
+
* @returns True if the path is safe, false if it contains security risks
|
|
59
|
+
*/
|
|
60
|
+
export function isPropertyPathSafe(path) {
|
|
61
|
+
if (!path || typeof path !== 'string') {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
// Check if path starts or ends with a dot
|
|
65
|
+
if (path.startsWith('.') || path.endsWith('.')) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Check for consecutive dots (potential traversal)
|
|
69
|
+
if (path.includes('..')) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Validate each segment of the path
|
|
73
|
+
const segments = path.split('.');
|
|
74
|
+
return segments.every((segment) => isPropertyKeySafe(segment));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Sanitizes a property key by removing or replacing dangerous characters
|
|
78
|
+
*
|
|
79
|
+
* @param key - The property key to sanitize
|
|
80
|
+
* @returns Sanitized key or null if the key is too dangerous to sanitize
|
|
81
|
+
*/
|
|
82
|
+
export function sanitizePropertyKey(key) {
|
|
83
|
+
if (!isPropertyKeySafe(key)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
// Additional sanitization for edge cases
|
|
87
|
+
return key.trim();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Filters out dangerous keys from an object
|
|
91
|
+
*
|
|
92
|
+
* @param obj - The object to filter
|
|
93
|
+
* @returns New object with only safe keys
|
|
94
|
+
*/
|
|
95
|
+
export function filterDangerousKeys(obj) {
|
|
96
|
+
const filtered = {};
|
|
97
|
+
for (const key in obj) {
|
|
98
|
+
if (Object.hasOwn(obj, key) && isPropertyKeySafe(key)) {
|
|
99
|
+
filtered[key] = obj[key];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return filtered;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates a circular reference detection utility using WeakSet
|
|
106
|
+
*
|
|
107
|
+
* @returns Object with methods to track and check for circular references
|
|
108
|
+
*/
|
|
109
|
+
export function createCircularReferenceDetector() {
|
|
110
|
+
let visited = new WeakSet();
|
|
111
|
+
return {
|
|
112
|
+
/**
|
|
113
|
+
* Marks an object as visited
|
|
114
|
+
* @param obj - Object to mark as visited
|
|
115
|
+
*/
|
|
116
|
+
markVisited(obj) {
|
|
117
|
+
visited.add(obj);
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Checks if an object has been visited (circular reference)
|
|
121
|
+
* @param obj - Object to check
|
|
122
|
+
* @returns True if object was already visited
|
|
123
|
+
*/
|
|
124
|
+
isVisited(obj) {
|
|
125
|
+
return visited.has(obj);
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Clears the visited objects set
|
|
129
|
+
*/
|
|
130
|
+
clear() {
|
|
131
|
+
// WeakSet doesn't have a clear method, so we create a new one
|
|
132
|
+
visited = new WeakSet();
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Default maximum input length for security validation */
|
|
137
|
+
const DEFAULT_MAX_INPUT_LENGTH = 10000;
|
|
138
|
+
/**
|
|
139
|
+
* Validates input for common security issues.
|
|
140
|
+
* Designed for property path validation, not general string validation.
|
|
141
|
+
*
|
|
142
|
+
* @param input - Input to validate
|
|
143
|
+
* @param maxLength - Maximum allowed length
|
|
144
|
+
* @returns True if input is safe
|
|
145
|
+
*/
|
|
146
|
+
export function isInputSafe(input, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
|
|
147
|
+
// Check for null/undefined
|
|
148
|
+
if (input === null || input === undefined) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
// Check string length to prevent DoS
|
|
152
|
+
if (typeof input === 'string' && input.length > maxLength) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
// Check for dangerous string patterns
|
|
156
|
+
if (typeof input === 'string') {
|
|
157
|
+
for (const pattern of PATH_TRAVERSAL_PATTERNS) {
|
|
158
|
+
if (pattern.test(input)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=security-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-utils.js","sourceRoot":"","sources":["../../src/object/security-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sorts the keys of an object alphabetically and returns a new object with the same type
|
|
3
|
+
*
|
|
4
|
+
* @template T - The type of the input object
|
|
5
|
+
* @param object - Object whose keys should be sorted
|
|
6
|
+
* @returns A new object with sorted keys maintaining the original type
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const input = { z: 1, a: 2, m: 3 };
|
|
11
|
+
* const sorted = ObjectSortKeys(input);
|
|
12
|
+
* // Result: { a: 2, m: 3, z: 1 }
|
|
13
|
+
*
|
|
14
|
+
* // With complex object
|
|
15
|
+
* const user = {
|
|
16
|
+
* name: 'John',
|
|
17
|
+
* age: 30,
|
|
18
|
+
* email: 'john@example.com',
|
|
19
|
+
* active: true
|
|
20
|
+
* };
|
|
21
|
+
* const sortedUser = ObjectSortKeys(user);
|
|
22
|
+
* // Result: { active: true, age: 30, email: 'john@example.com', name: 'John' }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function ObjectSortKeys<T extends Record<string, any>>(object: T): T;
|
|
26
|
+
//# sourceMappingURL=sort-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort-keys.d.ts","sourceRoot":"","sources":["../../src/object/sort-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CA+B1E"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sorts the keys of an object alphabetically and returns a new object with the same type
|
|
3
|
+
*
|
|
4
|
+
* @template T - The type of the input object
|
|
5
|
+
* @param object - Object whose keys should be sorted
|
|
6
|
+
* @returns A new object with sorted keys maintaining the original type
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const input = { z: 1, a: 2, m: 3 };
|
|
11
|
+
* const sorted = ObjectSortKeys(input);
|
|
12
|
+
* // Result: { a: 2, m: 3, z: 1 }
|
|
13
|
+
*
|
|
14
|
+
* // With complex object
|
|
15
|
+
* const user = {
|
|
16
|
+
* name: 'John',
|
|
17
|
+
* age: 30,
|
|
18
|
+
* email: 'john@example.com',
|
|
19
|
+
* active: true
|
|
20
|
+
* };
|
|
21
|
+
* const sortedUser = ObjectSortKeys(user);
|
|
22
|
+
* // Result: { active: true, age: 30, email: 'john@example.com', name: 'John' }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function ObjectSortKeys(object) {
|
|
26
|
+
if (!object || typeof object !== 'object' || Array.isArray(object)) {
|
|
27
|
+
return object;
|
|
28
|
+
}
|
|
29
|
+
const sorted = Object.keys(object).sort((a, b) => {
|
|
30
|
+
if (a < b)
|
|
31
|
+
return -1;
|
|
32
|
+
if (a > b)
|
|
33
|
+
return 1;
|
|
34
|
+
return 0;
|
|
35
|
+
})
|
|
36
|
+
.reduce((entry, key) => {
|
|
37
|
+
entry[key] = object[key];
|
|
38
|
+
return entry;
|
|
39
|
+
}, {});
|
|
40
|
+
// Preserve non-enumerable properties
|
|
41
|
+
const allKeys = Object.getOwnPropertyNames(object);
|
|
42
|
+
for (const key of allKeys) {
|
|
43
|
+
if (!Object.prototype.hasOwnProperty.call(sorted, key)) {
|
|
44
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
45
|
+
if (descriptor) {
|
|
46
|
+
Object.defineProperty(sorted, key, descriptor);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return sorted;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=sort-keys.js.map
|