@virusis/api-client 0.1.16 → 0.1.19

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.
Files changed (73) hide show
  1. package/dist/base.d.ts +12 -1
  2. package/dist/base.js +174 -1
  3. package/dist/container.d.ts +15 -3
  4. package/dist/generated/clients/diagnostics-service.d.ts +2 -0
  5. package/dist/generated/clients/diagnostics-service.js +2 -0
  6. package/dist/generated/clients/feedbacks-service.d.ts +2 -0
  7. package/dist/generated/clients/feedbacks-service.js +2 -0
  8. package/dist/generated/clients/index.d.ts +5 -1
  9. package/dist/generated/clients/index.js +5 -1
  10. package/dist/generated/clients/portal-service.d.ts +2 -0
  11. package/dist/generated/clients/portal-service.js +2 -0
  12. package/dist/generated/clients/risk-service.d.ts +2 -0
  13. package/dist/generated/clients/risk-service.js +2 -0
  14. package/dist/generated/clients-rx/diagnostics-service-rx.d.ts +16 -0
  15. package/dist/generated/clients-rx/diagnostics-service-rx.js +12 -0
  16. package/dist/generated/clients-rx/feedbacks-service-rx.d.ts +16 -0
  17. package/dist/generated/clients-rx/feedbacks-service-rx.js +12 -0
  18. package/dist/generated/clients-rx/index.d.ts +5 -1
  19. package/dist/generated/clients-rx/index.js +5 -1
  20. package/dist/generated/clients-rx/portal-service-rx.d.ts +16 -0
  21. package/dist/generated/clients-rx/portal-service-rx.js +12 -0
  22. package/dist/generated/clients-rx/risk-service-rx.d.ts +16 -0
  23. package/dist/generated/clients-rx/risk-service-rx.js +12 -0
  24. package/dist/generated/index.d.ts +398 -67
  25. package/dist/generated/index.js +811 -138
  26. package/dist/generated/models/access-token-i-data-result.d.ts +4 -0
  27. package/dist/generated/models/access-token-i-data-result.js +1 -0
  28. package/dist/generated/models/access-token.d.ts +4 -0
  29. package/dist/generated/models/access-token.js +1 -0
  30. package/dist/generated/models/application-click-event-batch-dto.d.ts +4 -0
  31. package/dist/generated/models/application-click-event-batch-dto.js +1 -0
  32. package/dist/generated/models/application-click-event-create-dto.d.ts +4 -0
  33. package/dist/generated/models/application-click-event-create-dto.js +1 -0
  34. package/dist/generated/models/feedback-category-dto-list-i-data-result.d.ts +4 -0
  35. package/dist/generated/models/feedback-category-dto-list-i-data-result.js +1 -0
  36. package/dist/generated/models/feedback-category-dto.d.ts +4 -0
  37. package/dist/generated/models/feedback-category-dto.js +1 -0
  38. package/dist/generated/models/index.d.ts +17 -0
  39. package/dist/generated/models/index.js +17 -0
  40. package/dist/generated/models/otp-generate-result.d.ts +4 -0
  41. package/dist/generated/models/otp-generate-result.js +1 -0
  42. package/dist/generated/models/queue-monitor-workers-response.d.ts +4 -0
  43. package/dist/generated/models/queue-monitor-workers-response.js +1 -0
  44. package/dist/generated/models/risk-flag-request-dto.d.ts +4 -0
  45. package/dist/generated/models/risk-flag-request-dto.js +1 -0
  46. package/dist/generated/models/risk-signal-avg-dto.d.ts +4 -0
  47. package/dist/generated/models/risk-signal-avg-dto.js +1 -0
  48. package/dist/generated/models/risk-signal-client-dto.d.ts +4 -0
  49. package/dist/generated/models/risk-signal-client-dto.js +1 -0
  50. package/dist/generated/models/risk-signal-counts-dto.d.ts +4 -0
  51. package/dist/generated/models/risk-signal-counts-dto.js +1 -0
  52. package/dist/generated/models/risk-signals-dto.d.ts +4 -0
  53. package/dist/generated/models/risk-signals-dto.js +1 -0
  54. package/dist/generated/models/risk-state-dto.d.ts +4 -0
  55. package/dist/generated/models/risk-state-dto.js +1 -0
  56. package/dist/generated/models/risk-verify-dto.d.ts +4 -0
  57. package/dist/generated/models/risk-verify-dto.js +1 -0
  58. package/dist/generated/models/scan-status-dto-i-data-result.d.ts +4 -0
  59. package/dist/generated/models/scan-status-dto-i-data-result.js +1 -0
  60. package/dist/generated/models/scan-status-dto.d.ts +4 -0
  61. package/dist/generated/models/scan-status-dto.js +1 -0
  62. package/dist/index.d.ts +1 -0
  63. package/dist/index.js +1 -0
  64. package/dist/rx.d.ts +30 -6
  65. package/dist/security/index.d.ts +4 -0
  66. package/dist/security/index.js +2 -0
  67. package/dist/security/input-security-policy.d.ts +23 -0
  68. package/dist/security/input-security-policy.js +5 -0
  69. package/dist/security/input-security-service.d.ts +3 -0
  70. package/dist/security/input-security-service.js +153 -0
  71. package/dist/security/request-sanitizer.d.ts +18 -0
  72. package/dist/security/request-sanitizer.js +242 -0
  73. package/package.json +9 -4
@@ -0,0 +1,242 @@
1
+ import { sanitize } from "./input-security-service.js";
2
+ /**
3
+ * Request-level defensive sanitizer for API client fetch helpers.
4
+ * Validates/sanitizes path, body string fields, headers, and FormData filenames.
5
+ */
6
+ // ─── Path validation ────────────────────────────────────────────
7
+ const DANGEROUS_PATH_PATTERNS = [
8
+ /\.\.[/\\]/, // path traversal
9
+ /%2e%2e/i, // encoded traversal
10
+ /%2f/i, // encoded slash
11
+ /%5c/i, // encoded backslash
12
+ /[\r\n]/, // CRLF
13
+ /[\x00-\x1F]/, // control chars
14
+ /\/\//, // double slash (protocol-relative)
15
+ ];
16
+ export function validatePath(path) {
17
+ for (const p of DANGEROUS_PATH_PATTERNS) {
18
+ if (p.test(path)) {
19
+ return { safe: false, sanitized: "" };
20
+ }
21
+ }
22
+ return { safe: true, sanitized: path };
23
+ }
24
+ // ─── URL host validation ────────────────────────────────────────
25
+ // ─── Private/reserved IP patterns for SSRF ─────────────────────
26
+ const PRIVATE_IP_PATTERNS = [
27
+ /^127\./,
28
+ /^10\./,
29
+ /^172\.(1[6-9]|2\d|3[01])\./,
30
+ /^192\.168\./,
31
+ /^169\.254\./,
32
+ /^0\./,
33
+ /^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./,
34
+ /^198\.1[89]\./,
35
+ /^(2[4-5]\d)\./,
36
+ ];
37
+ export function validateAbsoluteUrl(url, trustedHosts) {
38
+ try {
39
+ const parsed = new URL(url);
40
+ // Block javascript/data/file schemes
41
+ if (["javascript:", "data:", "file:", "vbscript:", "ftp:", "gopher:", "dict:", "ldap:"].includes(parsed.protocol)) {
42
+ return { safe: false, reason: `Blocked scheme: ${parsed.protocol}` };
43
+ }
44
+ const host = parsed.hostname.toLowerCase();
45
+ // Block embedded credentials
46
+ if (parsed.username || parsed.password) {
47
+ return { safe: false, reason: "URL contains embedded credentials" };
48
+ }
49
+ // Block localhost/loopback
50
+ if (host === "localhost" || host === "127.0.0.1" || host === "[::1]") {
51
+ return { safe: false, reason: `Blocked localhost/loopback: ${host}` };
52
+ }
53
+ // Block metadata hosts
54
+ if (host === "169.254.169.254" || host === "metadata.google.internal" || host === "100.100.100.200") {
55
+ return { safe: false, reason: `Blocked metadata host: ${host}` };
56
+ }
57
+ // Block private/reserved IPs
58
+ for (const pattern of PRIVATE_IP_PATTERNS) {
59
+ if (pattern.test(host)) {
60
+ return { safe: false, reason: `Blocked private/reserved IP: ${host}` };
61
+ }
62
+ }
63
+ // Check trusted hosts if list is provided
64
+ if (trustedHosts.length > 0) {
65
+ const isTrusted = trustedHosts.some((h) => host === h.toLowerCase() || host.endsWith("." + h.toLowerCase()));
66
+ if (!isTrusted) {
67
+ return { safe: false, reason: `Untrusted host: ${host}` };
68
+ }
69
+ }
70
+ return { safe: true };
71
+ }
72
+ catch {
73
+ return { safe: false, reason: "Invalid URL" };
74
+ }
75
+ }
76
+ // ─── Body sanitization ─────────────────────────────────────────
77
+ // Property name → policy kind mapping for common API fields
78
+ const FIELD_POLICIES = {
79
+ sha256: "sha256",
80
+ sha1: "sha1",
81
+ md5: "md5",
82
+ url: "url",
83
+ ip: "ip",
84
+ email: "email",
85
+ nickName: "freeTextShort",
86
+ title: "freeTextShort",
87
+ subject: "freeTextShort",
88
+ scanName: "scanName",
89
+ name: "scanDisplayName",
90
+ submissionName: "scanDisplayName",
91
+ description: "freeTextLong",
92
+ message: "freeTextLong",
93
+ fileName: "fileName",
94
+ scanMode: "scanMode",
95
+ scanType: "enum",
96
+ otp: "otp",
97
+ password: "password",
98
+ captchaToken: "captchaToken",
99
+ scanGuid: "guid",
100
+ guid: "guid",
101
+ selectedEngineIdsJson: "engineIdsJson",
102
+ analysisIdsJson: "analysisIdsJson",
103
+ dto: "jsonPayload",
104
+ };
105
+ // ─── Prototype pollution key denylist ────────────────────────────
106
+ const DENIED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
107
+ const MAX_BODY_DEPTH = 8;
108
+ export function sanitizeBody(body, depth = 0, visited = new WeakSet()) {
109
+ const result = {
110
+ body: { ...body },
111
+ modified: false,
112
+ blocked: false,
113
+ threats: [],
114
+ };
115
+ if (depth >= MAX_BODY_DEPTH)
116
+ return result;
117
+ if (visited.has(body))
118
+ return result;
119
+ visited.add(body);
120
+ for (const [key, value] of Object.entries(body)) {
121
+ // Reject prototype pollution keys
122
+ if (DENIED_KEYS.has(key)) {
123
+ result.blocked = true;
124
+ result.threats.push({
125
+ sanitized: "",
126
+ modified: false,
127
+ blocked: true,
128
+ detectedThreats: [{
129
+ type: "prototype-pollution",
130
+ severity: "critical",
131
+ message: `Prototype pollution key rejected: ${key}`,
132
+ }],
133
+ });
134
+ continue;
135
+ }
136
+ if (Array.isArray(value)) {
137
+ // Recursively check array items
138
+ const arrayResult = sanitizeArray(value, depth + 1, visited);
139
+ if (arrayResult.blocked) {
140
+ result.blocked = true;
141
+ result.threats.push(...arrayResult.threats);
142
+ }
143
+ else if (arrayResult.modified) {
144
+ result.body[key] = arrayResult.items;
145
+ result.modified = true;
146
+ result.threats.push(...arrayResult.threats);
147
+ }
148
+ continue;
149
+ }
150
+ if (typeof value === "object" && value !== null) {
151
+ // Recursively check nested objects for prototype pollution keys
152
+ const nested = sanitizeBody(value, depth + 1, visited);
153
+ if (nested.blocked) {
154
+ result.blocked = true;
155
+ result.threats.push(...nested.threats);
156
+ }
157
+ else if (nested.modified) {
158
+ result.body[key] = nested.body;
159
+ result.modified = true;
160
+ result.threats.push(...nested.threats);
161
+ }
162
+ continue;
163
+ }
164
+ if (typeof value !== "string")
165
+ continue;
166
+ const kind = FIELD_POLICIES[key] ?? "freeTextShort";
167
+ const secResult = sanitize(kind, value, key);
168
+ if (secResult.blocked) {
169
+ result.blocked = true;
170
+ result.threats.push(secResult);
171
+ }
172
+ else if (secResult.modified) {
173
+ result.body[key] = secResult.sanitized;
174
+ result.modified = true;
175
+ if (secResult.detectedThreats.length > 0) {
176
+ result.threats.push(secResult);
177
+ }
178
+ }
179
+ }
180
+ return result;
181
+ }
182
+ function sanitizeArray(items, depth, visited) {
183
+ const result = {
184
+ items: [...items],
185
+ modified: false,
186
+ blocked: false,
187
+ threats: [],
188
+ };
189
+ if (depth >= MAX_BODY_DEPTH)
190
+ return result;
191
+ for (let i = 0; i < items.length; i++) {
192
+ const item = items[i];
193
+ if (item === null || item === undefined)
194
+ continue;
195
+ if (Array.isArray(item)) {
196
+ const nested = sanitizeArray(item, depth + 1, visited);
197
+ if (nested.blocked) {
198
+ result.blocked = true;
199
+ result.threats.push(...nested.threats);
200
+ }
201
+ else if (nested.modified) {
202
+ result.items[i] = nested.items;
203
+ result.modified = true;
204
+ result.threats.push(...nested.threats);
205
+ }
206
+ }
207
+ else if (typeof item === "object") {
208
+ const nested = sanitizeBody(item, depth + 1, visited);
209
+ if (nested.blocked) {
210
+ result.blocked = true;
211
+ result.threats.push(...nested.threats);
212
+ }
213
+ else if (nested.modified) {
214
+ result.items[i] = nested.body;
215
+ result.modified = true;
216
+ result.threats.push(...nested.threats);
217
+ }
218
+ }
219
+ else if (typeof item === "string") {
220
+ const secResult = sanitize("freeTextShort", item, `[${i}]`);
221
+ if (secResult.blocked) {
222
+ result.blocked = true;
223
+ result.threats.push(secResult);
224
+ }
225
+ else if (secResult.modified) {
226
+ result.items[i] = secResult.sanitized;
227
+ result.modified = true;
228
+ }
229
+ }
230
+ }
231
+ return result;
232
+ }
233
+ // ─── FormData sanitization ──────────────────────────────────────
234
+ export function sanitizeFormDataFileName(filename) {
235
+ const result = sanitize("fileName", filename, "formDataFile");
236
+ return result.blocked ? "sanitized_file" : result.sanitized;
237
+ }
238
+ // ─── Header sanitization ────────────────────────────────────────
239
+ export function sanitizeHeaderValue(value) {
240
+ const result = sanitize("headerValue", value, "header");
241
+ return result.blocked ? "" : result.sanitized;
242
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@virusis/api-client",
3
- "version": "0.1.16",
3
+ "version": "0.1.19",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,13 +23,18 @@
23
23
  },
24
24
  "./package.json": "./package.json"
25
25
  },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
26
30
  "scripts": {
27
31
  "generate": "node scripts/run-nswag.mjs",
28
32
  "postgenerate": "node scripts/split-clients.js",
29
- "build": "tsc -p tsconfig.json",
30
- "test": "vitest run",
33
+ "build": "node node_modules/typescript/bin/tsc -p tsconfig.json",
34
+ "test": "node node_modules/vitest/vitest.mjs run",
31
35
  "ci": "npm run generate && npm run build && npm test",
32
- "release": "npm run ci && npm publish --access=public",
36
+ "release": "npm run ci && npm publish --access=public --registry=https://registry.npmjs.org/",
37
+ "release:dry": "npm run ci && npm publish --dry-run --access=public --registry=https://registry.npmjs.org/",
33
38
  "pack:local": "node -e \"require('fs').mkdirSync('packages',{recursive:true});\" && npm pack --pack-destination ./packages",
34
39
  "fetch:spec": "node scripts/fetch-spec.mjs",
35
40
  "gen:python": "npm run fetch:spec && npx @openapitools/openapi-generator-cli generate -g python -i specs/openapi.json -o out/python --package-name virusis_api_client",