@signaltree/core 4.0.15 → 4.1.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/README.md +106 -38
- package/dist/constants.js +6 -0
- package/dist/deep-clone.js +80 -0
- package/dist/deep-equal.js +41 -0
- package/dist/enhancers/batching/lib/batching.js +141 -135
- package/dist/enhancers/computed/lib/computed.js +18 -16
- package/dist/enhancers/devtools/lib/devtools.js +303 -260
- package/dist/enhancers/entities/lib/entities.js +109 -104
- package/dist/enhancers/index.js +65 -77
- package/dist/enhancers/memoization/lib/memoization.js +339 -351
- package/dist/enhancers/middleware/lib/async-helpers.js +71 -79
- package/dist/enhancers/middleware/lib/middleware.js +126 -169
- package/dist/enhancers/presets/lib/presets.js +82 -71
- package/dist/enhancers/serialization/constants.js +14 -13
- package/dist/enhancers/serialization/lib/serialization.js +615 -623
- package/dist/enhancers/time-travel/lib/time-travel.js +178 -177
- package/dist/index.d.ts +1 -17
- package/dist/index.js +19 -16
- package/dist/is-built-in-object.js +23 -0
- package/dist/lib/constants.js +51 -55
- package/dist/lib/memory/memory-manager.js +152 -154
- package/dist/lib/performance/diff-engine.js +141 -141
- package/dist/lib/performance/path-index.js +139 -137
- package/dist/lib/performance/update-engine.js +171 -176
- package/dist/lib/security/security-validator.js +110 -128
- package/dist/lib/signal-tree.js +577 -611
- package/dist/lib/types.js +3 -9
- package/dist/lib/utils.js +236 -268
- package/dist/lru-cache.js +64 -0
- package/dist/parse-path.js +13 -0
- package/package.json +30 -16
- package/src/index.d.ts +17 -0
- package/{dist → src}/lib/utils.d.ts +1 -0
- package/dist/enhancers/batching/index.js +0 -1
- package/dist/enhancers/batching/jest.config.js +0 -21
- package/dist/enhancers/batching/test-setup.js +0 -5
- package/dist/enhancers/computed/index.js +0 -1
- package/dist/enhancers/computed/jest.config.js +0 -21
- package/dist/enhancers/devtools/index.js +0 -1
- package/dist/enhancers/devtools/jest.config.js +0 -21
- package/dist/enhancers/devtools/test-setup.js +0 -5
- package/dist/enhancers/entities/index.js +0 -1
- package/dist/enhancers/entities/jest.config.js +0 -21
- package/dist/enhancers/entities/test-setup.js +0 -5
- package/dist/enhancers/memoization/index.js +0 -1
- package/dist/enhancers/memoization/jest.config.js +0 -21
- package/dist/enhancers/memoization/test-setup.js +0 -5
- package/dist/enhancers/middleware/index.js +0 -2
- package/dist/enhancers/middleware/jest.config.js +0 -21
- package/dist/enhancers/middleware/test-setup.js +0 -5
- package/dist/enhancers/presets/index.js +0 -1
- package/dist/enhancers/presets/jest.config.js +0 -21
- package/dist/enhancers/presets/test-setup.js +0 -5
- package/dist/enhancers/serialization/index.js +0 -2
- package/dist/enhancers/serialization/jest.config.js +0 -21
- package/dist/enhancers/serialization/test-setup.js +0 -5
- package/dist/enhancers/time-travel/index.js +0 -1
- package/dist/enhancers/time-travel/jest.config.js +0 -21
- package/dist/enhancers/time-travel/lib/utils.js +0 -1
- package/dist/enhancers/time-travel/test-setup.js +0 -5
- package/dist/enhancers/types.js +0 -0
- /package/{dist → src}/enhancers/batching/index.d.ts +0 -0
- /package/{dist → src}/enhancers/batching/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/batching/lib/batching.d.ts +0 -0
- /package/{dist → src}/enhancers/batching/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/computed/index.d.ts +0 -0
- /package/{dist → src}/enhancers/computed/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/computed/lib/computed.d.ts +0 -0
- /package/{dist → src}/enhancers/devtools/index.d.ts +0 -0
- /package/{dist → src}/enhancers/devtools/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/devtools/lib/devtools.d.ts +0 -0
- /package/{dist → src}/enhancers/devtools/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/entities/index.d.ts +0 -0
- /package/{dist → src}/enhancers/entities/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/entities/lib/entities.d.ts +0 -0
- /package/{dist → src}/enhancers/entities/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/index.d.ts +0 -0
- /package/{dist → src}/enhancers/memoization/index.d.ts +0 -0
- /package/{dist → src}/enhancers/memoization/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/memoization/lib/memoization.d.ts +0 -0
- /package/{dist → src}/enhancers/memoization/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/middleware/index.d.ts +0 -0
- /package/{dist → src}/enhancers/middleware/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/middleware/lib/async-helpers.d.ts +0 -0
- /package/{dist → src}/enhancers/middleware/lib/middleware.d.ts +0 -0
- /package/{dist → src}/enhancers/middleware/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/presets/index.d.ts +0 -0
- /package/{dist → src}/enhancers/presets/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/presets/lib/presets.d.ts +0 -0
- /package/{dist → src}/enhancers/presets/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/serialization/constants.d.ts +0 -0
- /package/{dist → src}/enhancers/serialization/index.d.ts +0 -0
- /package/{dist → src}/enhancers/serialization/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/serialization/lib/serialization.d.ts +0 -0
- /package/{dist → src}/enhancers/serialization/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/time-travel/index.d.ts +0 -0
- /package/{dist → src}/enhancers/time-travel/jest.config.d.ts +0 -0
- /package/{dist → src}/enhancers/time-travel/lib/time-travel.d.ts +0 -0
- /package/{dist → src}/enhancers/time-travel/lib/utils.d.ts +0 -0
- /package/{dist → src}/enhancers/time-travel/test-setup.d.ts +0 -0
- /package/{dist → src}/enhancers/types.d.ts +0 -0
- /package/{dist → src}/lib/constants.d.ts +0 -0
- /package/{dist → src}/lib/memory/memory-manager.d.ts +0 -0
- /package/{dist → src}/lib/performance/diff-engine.d.ts +0 -0
- /package/{dist → src}/lib/performance/path-index.d.ts +0 -0
- /package/{dist → src}/lib/performance/update-engine.d.ts +0 -0
- /package/{dist → src}/lib/security/security-validator.d.ts +0 -0
- /package/{dist → src}/lib/signal-tree.d.ts +0 -0
- /package/{dist → src}/lib/types.d.ts +0 -0
|
@@ -1,139 +1,121 @@
|
|
|
1
1
|
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
2
2
|
const HTML_TAG_PATTERN = /<[^>]*>/g;
|
|
3
|
-
const DANGEROUS_HTML_PATTERNS = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
(() => {
|
|
22
|
-
}),
|
|
23
|
-
sanitizationMode: config.sanitizationMode ?? 'strict',
|
|
24
|
-
};
|
|
25
|
-
this.dangerousKeys = new Set([
|
|
26
|
-
...DANGEROUS_KEYS,
|
|
27
|
-
...this.config.customDangerousKeys,
|
|
28
|
-
]);
|
|
3
|
+
const DANGEROUS_HTML_PATTERNS = [/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, /on\w+\s*=/gi, /javascript:/gi, /<iframe\b/gi, /<object\b/gi, /<embed\b/gi];
|
|
4
|
+
class SecurityValidator {
|
|
5
|
+
config;
|
|
6
|
+
dangerousKeys;
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.config = {
|
|
9
|
+
preventPrototypePollution: config.preventPrototypePollution ?? true,
|
|
10
|
+
preventXSS: config.preventXSS ?? false,
|
|
11
|
+
preventFunctions: config.preventFunctions ?? true,
|
|
12
|
+
customDangerousKeys: config.customDangerousKeys ?? [],
|
|
13
|
+
onSecurityEvent: config.onSecurityEvent ?? (() => {}),
|
|
14
|
+
sanitizationMode: config.sanitizationMode ?? 'strict'
|
|
15
|
+
};
|
|
16
|
+
this.dangerousKeys = new Set([...DANGEROUS_KEYS, ...this.config.customDangerousKeys]);
|
|
17
|
+
}
|
|
18
|
+
validateKey(key) {
|
|
19
|
+
if (!this.config.preventPrototypePollution) {
|
|
20
|
+
return;
|
|
29
21
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
timestamp: Date.now(),
|
|
40
|
-
};
|
|
41
|
-
this.config.onSecurityEvent(event);
|
|
42
|
-
throw new Error(`[SignalTree Security] Dangerous key "${key}" is not allowed. ` +
|
|
43
|
-
`This key can lead to prototype pollution attacks. ` +
|
|
44
|
-
`Blocked keys: ${Array.from(this.dangerousKeys).join(', ')}`);
|
|
45
|
-
}
|
|
22
|
+
if (this.dangerousKeys.has(key)) {
|
|
23
|
+
const event = {
|
|
24
|
+
type: 'dangerous-key-blocked',
|
|
25
|
+
key,
|
|
26
|
+
reason: `Dangerous key "${key}" blocked to prevent prototype pollution`,
|
|
27
|
+
timestamp: Date.now()
|
|
28
|
+
};
|
|
29
|
+
this.config.onSecurityEvent(event);
|
|
30
|
+
throw new Error(`[SignalTree Security] Dangerous key "${key}" is not allowed. ` + `This key can lead to prototype pollution attacks. ` + `Blocked keys: ${Array.from(this.dangerousKeys).join(', ')}`);
|
|
46
31
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`persistence, debugging, and SSR. ` +
|
|
59
|
-
`\n\nTo fix this:` +
|
|
60
|
-
`\n - Store function references outside the tree` +
|
|
61
|
-
`\n - Use method names (strings) and a function registry` +
|
|
62
|
-
`\n - Use computed signals for derived values` +
|
|
63
|
-
`\n\nBlocked value: ${value.toString().substring(0, 100)}...`);
|
|
64
|
-
}
|
|
65
|
-
if (!this.config.preventXSS || typeof value !== 'string') {
|
|
66
|
-
return value;
|
|
67
|
-
}
|
|
68
|
-
let hasDangerousPattern = false;
|
|
69
|
-
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
70
|
-
pattern.lastIndex = 0;
|
|
71
|
-
if (pattern.test(value)) {
|
|
72
|
-
hasDangerousPattern = true;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (hasDangerousPattern) {
|
|
77
|
-
const event = {
|
|
78
|
-
type: 'xss-attempt-blocked',
|
|
79
|
-
value,
|
|
80
|
-
reason: 'Dangerous HTML pattern detected and sanitized',
|
|
81
|
-
timestamp: Date.now(),
|
|
82
|
-
};
|
|
83
|
-
this.config.onSecurityEvent(event);
|
|
84
|
-
return this.sanitize(value);
|
|
85
|
-
}
|
|
86
|
-
return this.sanitize(value);
|
|
32
|
+
}
|
|
33
|
+
validateValue(value) {
|
|
34
|
+
if (this.config.preventFunctions && typeof value === 'function') {
|
|
35
|
+
const event = {
|
|
36
|
+
type: 'function-value-blocked',
|
|
37
|
+
value,
|
|
38
|
+
reason: 'Function values are not allowed - state must be serializable',
|
|
39
|
+
timestamp: Date.now()
|
|
40
|
+
};
|
|
41
|
+
this.config.onSecurityEvent(event);
|
|
42
|
+
throw new Error(`[SignalTree Security] Function values are not allowed in state trees. ` + `Functions cannot be serialized, breaking features like time-travel, ` + `persistence, debugging, and SSR. ` + `\n\nTo fix this:` + `\n - Store function references outside the tree` + `\n - Use method names (strings) and a function registry` + `\n - Use computed signals for derived values` + `\n\nBlocked value: ${value.toString().substring(0, 100)}...`);
|
|
87
43
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let sanitized = value;
|
|
91
|
-
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
92
|
-
pattern.lastIndex = 0;
|
|
93
|
-
sanitized = sanitized.replace(pattern, '');
|
|
94
|
-
}
|
|
95
|
-
return sanitized.replace(HTML_TAG_PATTERN, '');
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
let sanitized = value;
|
|
99
|
-
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
100
|
-
pattern.lastIndex = 0;
|
|
101
|
-
sanitized = sanitized.replace(pattern, '');
|
|
102
|
-
}
|
|
103
|
-
return sanitized;
|
|
104
|
-
}
|
|
44
|
+
if (!this.config.preventXSS || typeof value !== 'string') {
|
|
45
|
+
return value;
|
|
105
46
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
47
|
+
let hasDangerousPattern = false;
|
|
48
|
+
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
49
|
+
pattern.lastIndex = 0;
|
|
50
|
+
if (pattern.test(value)) {
|
|
51
|
+
hasDangerousPattern = true;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
109
54
|
}
|
|
110
|
-
|
|
111
|
-
|
|
55
|
+
if (hasDangerousPattern) {
|
|
56
|
+
const event = {
|
|
57
|
+
type: 'xss-attempt-blocked',
|
|
58
|
+
value,
|
|
59
|
+
reason: 'Dangerous HTML pattern detected and sanitized',
|
|
60
|
+
timestamp: Date.now()
|
|
61
|
+
};
|
|
62
|
+
this.config.onSecurityEvent(event);
|
|
63
|
+
return this.sanitize(value);
|
|
112
64
|
}
|
|
113
|
-
|
|
114
|
-
|
|
65
|
+
return this.sanitize(value);
|
|
66
|
+
}
|
|
67
|
+
sanitize(value) {
|
|
68
|
+
if (this.config.sanitizationMode === 'strict') {
|
|
69
|
+
let sanitized = value;
|
|
70
|
+
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
71
|
+
pattern.lastIndex = 0;
|
|
72
|
+
sanitized = sanitized.replace(pattern, '');
|
|
73
|
+
}
|
|
74
|
+
return sanitized.replace(HTML_TAG_PATTERN, '');
|
|
75
|
+
} else {
|
|
76
|
+
let sanitized = value;
|
|
77
|
+
for (const pattern of DANGEROUS_HTML_PATTERNS) {
|
|
78
|
+
pattern.lastIndex = 0;
|
|
79
|
+
sanitized = sanitized.replace(pattern, '');
|
|
80
|
+
}
|
|
81
|
+
return sanitized;
|
|
115
82
|
}
|
|
83
|
+
}
|
|
84
|
+
validateKeyValue(key, value) {
|
|
85
|
+
this.validateKey(key);
|
|
86
|
+
return this.validateValue(value);
|
|
87
|
+
}
|
|
88
|
+
isDangerousKey(key) {
|
|
89
|
+
return this.dangerousKeys.has(key);
|
|
90
|
+
}
|
|
91
|
+
getConfig() {
|
|
92
|
+
return {
|
|
93
|
+
...this.config
|
|
94
|
+
};
|
|
95
|
+
}
|
|
116
96
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
97
|
+
const SecurityPresets = {
|
|
98
|
+
strict: () => new SecurityValidator({
|
|
99
|
+
preventPrototypePollution: true,
|
|
100
|
+
preventXSS: true,
|
|
101
|
+
preventFunctions: true,
|
|
102
|
+
sanitizationMode: 'strict'
|
|
103
|
+
}),
|
|
104
|
+
standard: () => new SecurityValidator({
|
|
105
|
+
preventPrototypePollution: true,
|
|
106
|
+
preventXSS: false,
|
|
107
|
+
preventFunctions: true
|
|
108
|
+
}),
|
|
109
|
+
permissive: () => new SecurityValidator({
|
|
110
|
+
preventPrototypePollution: true,
|
|
111
|
+
preventXSS: false,
|
|
112
|
+
preventFunctions: false
|
|
113
|
+
}),
|
|
114
|
+
disabled: () => new SecurityValidator({
|
|
115
|
+
preventPrototypePollution: false,
|
|
116
|
+
preventXSS: false,
|
|
117
|
+
preventFunctions: false
|
|
118
|
+
})
|
|
139
119
|
};
|
|
120
|
+
|
|
121
|
+
export { SecurityPresets, SecurityValidator };
|