@resq-systems/security 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/README.md +280 -0
- package/lib/crypto.d.mts +195 -0
- package/lib/crypto.d.mts.map +1 -0
- package/lib/crypto.mjs +294 -0
- package/lib/crypto.mjs.map +1 -0
- package/lib/index.d.mts +4 -0
- package/lib/index.mjs +4 -0
- package/lib/sanitize.d.mts +316 -0
- package/lib/sanitize.d.mts.map +1 -0
- package/lib/sanitize.mjs +529 -0
- package/lib/sanitize.mjs.map +1 -0
- package/lib/validators.d.mts +269 -0
- package/lib/validators.d.mts.map +1 -0
- package/lib/validators.mjs +487 -0
- package/lib/validators.mjs.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
//#region src/validators.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 ResQ Systems, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Outcome of {@link detectThreatPatterns}.
|
|
19
|
+
*
|
|
20
|
+
* `isSafe` is the boolean shortcut; `threats` is the full list of
|
|
21
|
+
* findings (one per detector that fired). Use
|
|
22
|
+
* {@link getThreatErrorMessage} to render a user-facing message for
|
|
23
|
+
* the first finding.
|
|
24
|
+
*/
|
|
25
|
+
interface ThreatDetectionResult {
|
|
26
|
+
/** `true` when no detectors fired. Equivalent to `threats.length === 0`. */
|
|
27
|
+
isSafe: boolean;
|
|
28
|
+
/** All findings produced by enabled detectors, in detector order. */
|
|
29
|
+
threats: ThreatFinding[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A single detector hit. Detectors that fire return at most one
|
|
33
|
+
* finding per call (one example is enough to reject the input).
|
|
34
|
+
*/
|
|
35
|
+
interface ThreatFinding {
|
|
36
|
+
/** Which detector matched. */
|
|
37
|
+
type: ThreatType;
|
|
38
|
+
/** Human-readable description suitable for log lines (not for end users — use {@link getThreatErrorMessage} instead). */
|
|
39
|
+
description: string;
|
|
40
|
+
/** First 50 chars of the matching substring, for diagnostics. Truncated to prevent leaking large payloads in logs. */
|
|
41
|
+
matchedPattern?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The closed set of threat categories the validators recognize. Add
|
|
45
|
+
* new categories here when adding a new detector.
|
|
46
|
+
*/
|
|
47
|
+
type ThreatType = "xss" | "sql_injection" | "nosql_injection" | "command_injection" | "path_traversal" | "homoglyph";
|
|
48
|
+
/**
|
|
49
|
+
* Detect XSS-style payloads (script tags, event handlers, dangerous
|
|
50
|
+
* URI schemes, prototype pollution, …) in a UTF-8 input.
|
|
51
|
+
*
|
|
52
|
+
* Inputs longer than 100 000 characters are truncated before scanning
|
|
53
|
+
* to bound regex evaluation cost and prevent ReDoS on crafted
|
|
54
|
+
* payloads. Returns at most one finding — the regex catalog is
|
|
55
|
+
* exhaustive enough that the first hit is sufficient for a
|
|
56
|
+
* reject-or-sanitize decision.
|
|
57
|
+
*
|
|
58
|
+
* @param input - String to scan.
|
|
59
|
+
* @returns Empty array when nothing matches, or a single
|
|
60
|
+
* {@link ThreatFinding} of type `"xss"`.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* containsXSSPatterns(`<img src=x onerror="alert(1)">`);
|
|
65
|
+
* // → [{ type: "xss", description: "...", matchedPattern: "onerror=" }]
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function containsXSSPatterns(input: string): ThreatFinding[];
|
|
69
|
+
/**
|
|
70
|
+
* Detect SQL-injection patterns (UNION SELECT, DROP TABLE,
|
|
71
|
+
* comment-based bypasses, always-true tautologies, stacked queries)
|
|
72
|
+
* in input.
|
|
73
|
+
*
|
|
74
|
+
* **Not a replacement for parameterised queries.** Use this as a
|
|
75
|
+
* defense-in-depth signal in addition to a properly bound prepared
|
|
76
|
+
* statement, never as the only barrier.
|
|
77
|
+
*
|
|
78
|
+
* @param input - String to scan. Truncated at 100 000 characters.
|
|
79
|
+
* @returns Empty array, or one finding of type `"sql_injection"`.
|
|
80
|
+
*/
|
|
81
|
+
declare function containsSQLInjection(input: string): ThreatFinding[];
|
|
82
|
+
/**
|
|
83
|
+
* Detect NoSQL-injection patterns — Mongo-style operator injection
|
|
84
|
+
* (`$where`, `$ne`, `$regex`), JavaScript-in-query payloads, and
|
|
85
|
+
* structural manipulators that can bypass auth filters in document
|
|
86
|
+
* stores.
|
|
87
|
+
*
|
|
88
|
+
* @param input - String to scan.
|
|
89
|
+
* @returns Empty array, or one finding of type `"nosql_injection"`.
|
|
90
|
+
*/
|
|
91
|
+
declare function containsNoSQLInjection(input: string): ThreatFinding[];
|
|
92
|
+
/**
|
|
93
|
+
* Detect shell command-injection patterns: command substitution
|
|
94
|
+
* (`$(...)`, backticks), chained dangerous commands (`; rm`, `; curl`,
|
|
95
|
+
* …) and shell-piped exec (`| sh`, `| bash`).
|
|
96
|
+
*
|
|
97
|
+
* **Off by default in {@link detectThreatPatterns}** — these patterns
|
|
98
|
+
* occasionally fire on legitimate user content. Enable explicitly
|
|
99
|
+
* (`checkCommandInjection: true`) only when input flows into a child
|
|
100
|
+
* process or shell.
|
|
101
|
+
*
|
|
102
|
+
* @param input - String to scan. Truncated at 100 000 characters.
|
|
103
|
+
* @returns Empty array, or one finding of type `"command_injection"`.
|
|
104
|
+
*/
|
|
105
|
+
declare function containsCommandInjection(input: string): ThreatFinding[];
|
|
106
|
+
/**
|
|
107
|
+
* Detect path-traversal payloads — `../`, encoded dots, raw absolute
|
|
108
|
+
* paths trying to escape a base directory. Pair with `path.resolve()`
|
|
109
|
+
* + a `startsWith()` containment check on the canonicalised path
|
|
110
|
+
* before reading or writing the file.
|
|
111
|
+
*
|
|
112
|
+
* @param input - String to scan.
|
|
113
|
+
* @returns Empty array, or one finding of type `"path_traversal"`.
|
|
114
|
+
*/
|
|
115
|
+
declare function containsPathTraversal(input: string): ThreatFinding[];
|
|
116
|
+
/**
|
|
117
|
+
* Detect lookalike Unicode characters (Cyrillic / Greek glyphs that
|
|
118
|
+
* render identically to common ASCII letters). The classic phishing
|
|
119
|
+
* trick is `paypaӏ.com` (`ӏ` instead of `l`); this detector catches
|
|
120
|
+
* the building blocks.
|
|
121
|
+
*
|
|
122
|
+
* Use {@link normalizeUnicode} to *replace* homoglyphs with their
|
|
123
|
+
* ASCII equivalents — this function only flags their presence.
|
|
124
|
+
*
|
|
125
|
+
* @param input - String to scan.
|
|
126
|
+
* @returns Empty array, or one finding of type `"homoglyph"` (the
|
|
127
|
+
* first matched lookalike).
|
|
128
|
+
*/
|
|
129
|
+
declare function containsHomoglyphs(input: string): ThreatFinding[];
|
|
130
|
+
/**
|
|
131
|
+
* Per-detector toggles for {@link detectThreatPatterns}.
|
|
132
|
+
*
|
|
133
|
+
* Defaults: XSS, SQL, NoSQL, path-traversal, and homoglyph detectors
|
|
134
|
+
* are **on**; command injection is **off** (false-positive prone).
|
|
135
|
+
* Pass `false` to disable a detector or `true` to force-enable
|
|
136
|
+
* `checkCommandInjection`.
|
|
137
|
+
*/
|
|
138
|
+
interface ThreatDetectionConfig {
|
|
139
|
+
/** Default `true`. */
|
|
140
|
+
checkXSS?: boolean;
|
|
141
|
+
/** Default `true`. */
|
|
142
|
+
checkSQLInjection?: boolean;
|
|
143
|
+
/** Default `true`. */
|
|
144
|
+
checkNoSQLInjection?: boolean;
|
|
145
|
+
/** Default `false` — opt in only when input reaches a shell. */
|
|
146
|
+
checkCommandInjection?: boolean;
|
|
147
|
+
/** Default `true`. */
|
|
148
|
+
checkPathTraversal?: boolean;
|
|
149
|
+
/** Default `true`. */
|
|
150
|
+
checkHomoglyphs?: boolean;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Run every enabled detector against `input` and aggregate findings.
|
|
154
|
+
*
|
|
155
|
+
* Returns early-but-not-immediately: each individual detector still
|
|
156
|
+
* runs to completion, but each detector returns at most one finding,
|
|
157
|
+
* so the aggregate threats array is small (≤ 6 entries).
|
|
158
|
+
*
|
|
159
|
+
* Non-string inputs (`null`, `undefined`, numbers, …) are treated as
|
|
160
|
+
* safe — wrap caller-side validation around this if you want to
|
|
161
|
+
* reject non-strings.
|
|
162
|
+
*
|
|
163
|
+
* @param input - The candidate string.
|
|
164
|
+
* @param config - Detector toggles. Defaults turn on everything
|
|
165
|
+
* except command-injection.
|
|
166
|
+
* @returns `{ isSafe, threats }`.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const result = detectThreatPatterns(req.body.query);
|
|
171
|
+
* if (!result.isSafe) return new Response(getThreatErrorMessage(result), { status: 400 });
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare function detectThreatPatterns(input: string, config?: ThreatDetectionConfig): ThreatDetectionResult;
|
|
175
|
+
/**
|
|
176
|
+
* Boolean shortcut over {@link detectThreatPatterns} — discards the
|
|
177
|
+
* findings list when you only need a yes/no decision.
|
|
178
|
+
*
|
|
179
|
+
* @param input - String to test.
|
|
180
|
+
* @param config - Optional detector toggles.
|
|
181
|
+
* @returns `true` when no detector fires.
|
|
182
|
+
*/
|
|
183
|
+
declare function isSafeInput(input: string, config?: ThreatDetectionConfig): boolean;
|
|
184
|
+
/**
|
|
185
|
+
* HTML-entity escape `&`, `<`, `>`, `"`, `'`, and `/` for safe
|
|
186
|
+
* insertion into HTML text and attribute contexts.
|
|
187
|
+
*
|
|
188
|
+
* **Limited scope.** This is appropriate for plain text destined for
|
|
189
|
+
* `textContent` or attribute values, not for unfiltered HTML
|
|
190
|
+
* rendering. For rich-text use a vetted sanitizer (DOMPurify on the
|
|
191
|
+
* client, sanitize-html or similar on the server).
|
|
192
|
+
*
|
|
193
|
+
* Returns `""` for non-string or empty input.
|
|
194
|
+
*
|
|
195
|
+
* @param input - Untrusted string.
|
|
196
|
+
* @returns Entity-escaped output safe to interpolate into HTML.
|
|
197
|
+
*/
|
|
198
|
+
declare function sanitizeForDisplay(input: string): string;
|
|
199
|
+
/**
|
|
200
|
+
* Canonicalise a string for safe equality checks against ASCII.
|
|
201
|
+
*
|
|
202
|
+
* Two-pass:
|
|
203
|
+
* 1. Normalize to NFC (composed form) so combining-character
|
|
204
|
+
* sequences don't compare differently from their pre-composed
|
|
205
|
+
* counterparts.
|
|
206
|
+
* 2. Replace known homoglyphs (Cyrillic `А`, Greek `Ε`, …) with their
|
|
207
|
+
* ASCII equivalents (`A`, `E`, …).
|
|
208
|
+
*
|
|
209
|
+
* Use before storing user-controlled identifiers (usernames, domain
|
|
210
|
+
* names) and before comparing them to a denylist or to each other.
|
|
211
|
+
*
|
|
212
|
+
* Returns `""` for non-string or empty input.
|
|
213
|
+
*
|
|
214
|
+
* @param input - Raw string from an untrusted source.
|
|
215
|
+
* @returns ASCII-normalized, NFC-composed string.
|
|
216
|
+
*/
|
|
217
|
+
declare function normalizeUnicode(input: string): string;
|
|
218
|
+
/**
|
|
219
|
+
* Generic user-facing fallback message. Render this verbatim when a
|
|
220
|
+
* detector fires but you don't want to expose which one. Prefer
|
|
221
|
+
* {@link getThreatErrorMessage} for category-specific messages.
|
|
222
|
+
*/
|
|
223
|
+
declare const THREAT_DETECTED_MESSAGE = "Input contains potentially unsafe content";
|
|
224
|
+
/**
|
|
225
|
+
* Boolean refinement helper for use with `zod.string().refine(...)`,
|
|
226
|
+
* `effect/Schema.filter(...)`, or any predicate-based validator.
|
|
227
|
+
*
|
|
228
|
+
* Equivalent to `isSafeInput(input)` with default config.
|
|
229
|
+
*/
|
|
230
|
+
declare function validateSafeText(input: string): boolean;
|
|
231
|
+
/**
|
|
232
|
+
* Refinement for human name fields. More permissive than
|
|
233
|
+
* {@link validateSafeText} — allows international letters,
|
|
234
|
+
* combining marks, hyphens, apostrophes, and spaces — but still
|
|
235
|
+
* rejects HTML/SQL/NoSQL injection patterns and homoglyph forgeries.
|
|
236
|
+
*
|
|
237
|
+
* Suitable for first/last/full-name inputs in registration forms.
|
|
238
|
+
*
|
|
239
|
+
* @returns `true` when the name passes both the threat detectors and
|
|
240
|
+
* the name-shape regex.
|
|
241
|
+
*/
|
|
242
|
+
declare function validateSafeName(input: string): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* Refinement for email fields. Combines:
|
|
245
|
+
*
|
|
246
|
+
* 1. RFC-style format check (length-bounded to ≤ 254 chars to
|
|
247
|
+
* prevent ReDoS).
|
|
248
|
+
* 2. XSS / SQL / NoSQL / homoglyph detectors — emails are extremely
|
|
249
|
+
* constrained and should never legitimately contain HTML or query
|
|
250
|
+
* operators.
|
|
251
|
+
*
|
|
252
|
+
* @returns `true` when both checks pass.
|
|
253
|
+
*/
|
|
254
|
+
declare function validateSafeEmail(input: string): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Map a {@link ThreatDetectionResult} into a user-facing error
|
|
257
|
+
* message string suitable for an HTTP 400 response or form
|
|
258
|
+
* validation error. Returns `""` when the result is safe (so
|
|
259
|
+
* `error || undefined` works).
|
|
260
|
+
*
|
|
261
|
+
* Uses only the **first** finding for the message — exposing every
|
|
262
|
+
* threat type to the user can leak information about the detection
|
|
263
|
+
* rules. For full diagnostics, log `result.threats` server-side
|
|
264
|
+
* rather than returning them.
|
|
265
|
+
*/
|
|
266
|
+
declare function getThreatErrorMessage(result: ThreatDetectionResult): string;
|
|
267
|
+
//#endregion
|
|
268
|
+
export { THREAT_DETECTED_MESSAGE, ThreatDetectionConfig, ThreatDetectionResult, ThreatFinding, ThreatType, containsCommandInjection, containsHomoglyphs, containsNoSQLInjection, containsPathTraversal, containsSQLInjection, containsXSSPatterns, detectThreatPatterns, getThreatErrorMessage, isSafeInput, normalizeUnicode, sanitizeForDisplay, validateSafeEmail, validateSafeName, validateSafeText };
|
|
269
|
+
//# sourceMappingURL=validators.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.mts","names":[],"sources":["../src/validators.ts"],"mappings":";;AAyKA;;;;;;;;;AAWA;;;;;;;;;;AAaA;;;UAxBiB,qBAAA;EAwBK;EAtBrB,MAAA;EAkDkC;EAhDlC,OAAA,EAAS,aAAA;AAAA;;AAgFV;;;UAzEiB,aAAA;EAyEiD;EAvEjE,IAAA,EAAM,UAAA;EAmG+B;EAjGrC,WAAA;EAiGsC;EA/FtC,cAAA;AAAA;;;;;KAOW,UAAA;;;;;AA2LZ;;;;;AA+BA;;;;;;;;;;;iBA9LgB,mBAAA,CAAoB,KAAA,WAAgB,aAAA;AA4OpD;;;;;;;;;;AAgDA;;AAhDA,iBA5MgB,oBAAA,CAAqB,KAAA,WAAgB,aAAA;;;;;;;AA8QrD;;;iBAlPgB,sBAAA,CAAuB,KAAA,WAAgB,aAAA;;AAgRvD;;;;;AAyBA;;;;;AAQA;;iBAlRgB,wBAAA,CAAyB,KAAA,WAAgB,aAAA;;;AAiSzD;;;;;AA0BA;;iBAtRgB,qBAAA,CAAsB,KAAA,WAAgB,aAAA;;;AAwTtD;;;;;;;;;;;iBAzRgB,kBAAA,CAAmB,KAAA,WAAgB,aAAA;;;;;;;;;UA+BlC,qBAAA;;EAEhB,QAAA;;EAEA,iBAAA;;EAEA,mBAAA;;EAEA,qBAAA;;EAEA,kBAAA;;EAEA,eAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;iBAkCe,oBAAA,CACf,KAAA,UACA,MAAA,GAAQ,qBAAA,GACN,qBAAA;;;;;;;;;iBA6Ca,WAAA,CAAY,KAAA,UAAe,MAAA,GAAS,qBAAA;;;;;;;;;;;;;;;iBAkBpC,kBAAA,CAAmB,KAAA;;;;;;;;;;;;;;;;;;;iBA8BnB,gBAAA,CAAiB,KAAA;;;;;;cAyBpB,uBAAA;;;;;;;iBAQG,gBAAA,CAAiB,KAAA;;;;;;;;;;;;iBAejB,gBAAA,CAAiB,KAAA;;;;;;;;;;;;iBA0BjB,iBAAA,CAAkB,KAAA;;;;;;;;;;;;iBAkClB,qBAAA,CAAsB,MAAA,EAAQ,qBAAA"}
|