guardrail-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.
Files changed (95) hide show
  1. package/dist/attack-surface/analyzer.d.ts +50 -0
  2. package/dist/attack-surface/analyzer.d.ts.map +1 -0
  3. package/dist/attack-surface/analyzer.js +83 -0
  4. package/dist/attack-surface/index.d.ts +5 -0
  5. package/dist/attack-surface/index.d.ts.map +1 -0
  6. package/dist/attack-surface/index.js +20 -0
  7. package/dist/index.d.ts +15 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +33 -0
  10. package/dist/languages/index.d.ts +21 -0
  11. package/dist/languages/index.d.ts.map +1 -0
  12. package/dist/languages/index.js +78 -0
  13. package/dist/languages/java-analyzer.d.ts +72 -0
  14. package/dist/languages/java-analyzer.d.ts.map +1 -0
  15. package/dist/languages/java-analyzer.js +417 -0
  16. package/dist/languages/python-analyzer.d.ts +70 -0
  17. package/dist/languages/python-analyzer.d.ts.map +1 -0
  18. package/dist/languages/python-analyzer.js +425 -0
  19. package/dist/license/compatibility-matrix.d.ts +28 -0
  20. package/dist/license/compatibility-matrix.d.ts.map +1 -0
  21. package/dist/license/compatibility-matrix.js +323 -0
  22. package/dist/license/engine.d.ts +77 -0
  23. package/dist/license/engine.d.ts.map +1 -0
  24. package/dist/license/engine.js +264 -0
  25. package/dist/license/index.d.ts +6 -0
  26. package/dist/license/index.d.ts.map +1 -0
  27. package/dist/license/index.js +21 -0
  28. package/dist/sbom/generator.d.ts +108 -0
  29. package/dist/sbom/generator.d.ts.map +1 -0
  30. package/dist/sbom/generator.js +271 -0
  31. package/dist/sbom/index.d.ts +5 -0
  32. package/dist/sbom/index.d.ts.map +1 -0
  33. package/dist/sbom/index.js +20 -0
  34. package/dist/secrets/guardian.d.ts +113 -0
  35. package/dist/secrets/guardian.d.ts.map +1 -0
  36. package/dist/secrets/guardian.js +334 -0
  37. package/dist/secrets/index.d.ts +10 -0
  38. package/dist/secrets/index.d.ts.map +1 -0
  39. package/dist/secrets/index.js +30 -0
  40. package/dist/secrets/patterns.d.ts +42 -0
  41. package/dist/secrets/patterns.d.ts.map +1 -0
  42. package/dist/secrets/patterns.js +165 -0
  43. package/dist/secrets/pre-commit.d.ts +39 -0
  44. package/dist/secrets/pre-commit.d.ts.map +1 -0
  45. package/dist/secrets/pre-commit.js +127 -0
  46. package/dist/secrets/vault-integration.d.ts +83 -0
  47. package/dist/secrets/vault-integration.d.ts.map +1 -0
  48. package/dist/secrets/vault-integration.js +295 -0
  49. package/dist/secrets/vault-providers.d.ts +110 -0
  50. package/dist/secrets/vault-providers.d.ts.map +1 -0
  51. package/dist/secrets/vault-providers.js +417 -0
  52. package/dist/supply-chain/detector.d.ts +80 -0
  53. package/dist/supply-chain/detector.d.ts.map +1 -0
  54. package/dist/supply-chain/detector.js +168 -0
  55. package/dist/supply-chain/index.d.ts +11 -0
  56. package/dist/supply-chain/index.d.ts.map +1 -0
  57. package/dist/supply-chain/index.js +26 -0
  58. package/dist/supply-chain/malicious-db.d.ts +41 -0
  59. package/dist/supply-chain/malicious-db.d.ts.map +1 -0
  60. package/dist/supply-chain/malicious-db.js +82 -0
  61. package/dist/supply-chain/script-analyzer.d.ts +54 -0
  62. package/dist/supply-chain/script-analyzer.d.ts.map +1 -0
  63. package/dist/supply-chain/script-analyzer.js +160 -0
  64. package/dist/supply-chain/typosquat.d.ts +58 -0
  65. package/dist/supply-chain/typosquat.d.ts.map +1 -0
  66. package/dist/supply-chain/typosquat.js +257 -0
  67. package/dist/supply-chain/vulnerability-db.d.ts +114 -0
  68. package/dist/supply-chain/vulnerability-db.d.ts.map +1 -0
  69. package/dist/supply-chain/vulnerability-db.js +310 -0
  70. package/package.json +34 -0
  71. package/src/__tests__/license/engine.test.ts +250 -0
  72. package/src/__tests__/supply-chain/typosquat.test.ts +191 -0
  73. package/src/attack-surface/analyzer.ts +152 -0
  74. package/src/attack-surface/index.ts +5 -0
  75. package/src/index.ts +21 -0
  76. package/src/languages/index.ts +91 -0
  77. package/src/languages/java-analyzer.ts +490 -0
  78. package/src/languages/python-analyzer.ts +498 -0
  79. package/src/license/compatibility-matrix.ts +366 -0
  80. package/src/license/engine.ts +345 -0
  81. package/src/license/index.ts +6 -0
  82. package/src/sbom/generator.ts +355 -0
  83. package/src/sbom/index.ts +5 -0
  84. package/src/secrets/guardian.ts +448 -0
  85. package/src/secrets/index.ts +10 -0
  86. package/src/secrets/patterns.ts +186 -0
  87. package/src/secrets/pre-commit.ts +158 -0
  88. package/src/secrets/vault-integration.ts +360 -0
  89. package/src/secrets/vault-providers.ts +446 -0
  90. package/src/supply-chain/detector.ts +252 -0
  91. package/src/supply-chain/index.ts +11 -0
  92. package/src/supply-chain/malicious-db.ts +103 -0
  93. package/src/supply-chain/script-analyzer.ts +194 -0
  94. package/src/supply-chain/typosquat.ts +302 -0
  95. package/src/supply-chain/vulnerability-db.ts +386 -0
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ /**
3
+ * Python Language Analyzer
4
+ *
5
+ * Security analysis for Python projects including:
6
+ * - requirements.txt / Pipfile / pyproject.toml parsing
7
+ * - Import analysis for detecting dangerous modules
8
+ * - Secret detection patterns specific to Python
9
+ * - Common vulnerability patterns (SQL injection, command injection, etc.)
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.pythonAnalyzer = exports.PythonAnalyzer = void 0;
13
+ const fs_1 = require("fs");
14
+ const path_1 = require("path");
15
+ // Dangerous Python imports that may indicate security issues
16
+ const DANGEROUS_IMPORTS = [
17
+ {
18
+ module: "pickle",
19
+ reason: "Arbitrary code execution via deserialization",
20
+ severity: "high",
21
+ },
22
+ {
23
+ module: "marshal",
24
+ reason: "Unsafe deserialization",
25
+ severity: "high",
26
+ },
27
+ {
28
+ module: "shelve",
29
+ reason: "Uses pickle internally",
30
+ severity: "medium",
31
+ },
32
+ {
33
+ module: "subprocess",
34
+ reason: "Command execution - ensure proper sanitization",
35
+ severity: "medium",
36
+ },
37
+ {
38
+ module: "os.system",
39
+ reason: "Command execution - prefer subprocess",
40
+ severity: "high",
41
+ },
42
+ {
43
+ module: "eval",
44
+ reason: "Arbitrary code execution",
45
+ severity: "critical",
46
+ },
47
+ {
48
+ module: "exec",
49
+ reason: "Arbitrary code execution",
50
+ severity: "critical",
51
+ },
52
+ {
53
+ module: "compile",
54
+ reason: "Dynamic code compilation",
55
+ severity: "medium",
56
+ },
57
+ {
58
+ module: "__import__",
59
+ reason: "Dynamic import - potential for injection",
60
+ severity: "medium",
61
+ },
62
+ ];
63
+ // Python-specific secret patterns
64
+ const PYTHON_SECRET_PATTERNS = [
65
+ { pattern: /(?:api_key|apikey)\s*=\s*['"][^'"]{10,}['"]/gi, type: "API Key" },
66
+ {
67
+ pattern: /(?:secret|password|passwd|pwd)\s*=\s*['"][^'"]{6,}['"]/gi,
68
+ type: "Password/Secret",
69
+ },
70
+ {
71
+ pattern: /(?:token|auth_token|access_token)\s*=\s*['"][^'"]{10,}['"]/gi,
72
+ type: "Token",
73
+ },
74
+ {
75
+ pattern: /AWS_(?:ACCESS_KEY_ID|SECRET_ACCESS_KEY)\s*=\s*['"][^'"]+['"]/gi,
76
+ type: "AWS Credential",
77
+ },
78
+ {
79
+ pattern: /(?:private_key|ssh_key)\s*=\s*['"]-----BEGIN/gi,
80
+ type: "Private Key",
81
+ },
82
+ {
83
+ pattern: /mongodb(?:\+srv)?:\/\/[^:]+:[^@]+@/gi,
84
+ type: "MongoDB Connection String",
85
+ },
86
+ {
87
+ pattern: /postgres(?:ql)?:\/\/[^:]+:[^@]+@/gi,
88
+ type: "PostgreSQL Connection String",
89
+ },
90
+ ];
91
+ // Vulnerability code patterns
92
+ const VULNERABILITY_PATTERNS = [
93
+ {
94
+ pattern: /cursor\.execute\s*\(\s*['"f].*\{.*\}/g,
95
+ type: "SQL Injection",
96
+ message: "String formatting in SQL query - use parameterized queries",
97
+ severity: "critical",
98
+ },
99
+ {
100
+ pattern: /cursor\.execute\s*\(\s*.*%\s*\(/g,
101
+ type: "SQL Injection",
102
+ message: "String interpolation in SQL query - use parameterized queries",
103
+ severity: "critical",
104
+ },
105
+ {
106
+ pattern: /subprocess\.(?:call|run|Popen)\s*\(\s*['"f].*\{/g,
107
+ type: "Command Injection",
108
+ message: "String formatting in shell command - use list arguments",
109
+ severity: "critical",
110
+ },
111
+ {
112
+ pattern: /os\.system\s*\(/g,
113
+ type: "Command Injection",
114
+ message: "os.system is vulnerable to shell injection - use subprocess",
115
+ severity: "high",
116
+ },
117
+ {
118
+ pattern: /yaml\.load\s*\([^)]*\)/g,
119
+ type: "Unsafe Deserialization",
120
+ message: "yaml.load is unsafe - use yaml.safe_load instead",
121
+ severity: "high",
122
+ },
123
+ {
124
+ pattern: /pickle\.loads?\s*\(/g,
125
+ type: "Unsafe Deserialization",
126
+ message: "Pickle is unsafe with untrusted data",
127
+ severity: "high",
128
+ },
129
+ {
130
+ pattern: /render_template_string\s*\(/g,
131
+ type: "Server-Side Template Injection",
132
+ message: "render_template_string with user input is dangerous",
133
+ severity: "critical",
134
+ },
135
+ {
136
+ pattern: /DEBUG\s*=\s*True/g,
137
+ type: "Debug Mode",
138
+ message: "Debug mode enabled - disable in production",
139
+ severity: "medium",
140
+ },
141
+ {
142
+ pattern: /verify\s*=\s*False/g,
143
+ type: "SSL Verification Disabled",
144
+ message: "SSL verification disabled - vulnerable to MITM attacks",
145
+ severity: "high",
146
+ },
147
+ ];
148
+ class PythonAnalyzer {
149
+ /**
150
+ * Analyze a Python project
151
+ */
152
+ async analyze(projectPath) {
153
+ const dependencies = await this.extractDependencies(projectPath);
154
+ const securityIssues = [];
155
+ // Scan Python files for security issues
156
+ const pythonFiles = await this.findPythonFiles(projectPath);
157
+ for (const file of pythonFiles) {
158
+ const issues = await this.scanFile(file);
159
+ securityIssues.push(...issues);
160
+ }
161
+ // Get Python version if available
162
+ const pythonVersion = this.detectPythonVersion(projectPath);
163
+ // Calculate summary
164
+ const issuesBySeverity = {
165
+ critical: 0,
166
+ high: 0,
167
+ medium: 0,
168
+ low: 0,
169
+ };
170
+ for (const issue of securityIssues) {
171
+ const severity = issue.severity;
172
+ if (issuesBySeverity[severity] !== undefined) {
173
+ issuesBySeverity[severity]++;
174
+ }
175
+ }
176
+ return {
177
+ projectPath,
178
+ pythonVersion,
179
+ dependencies,
180
+ securityIssues,
181
+ summary: {
182
+ totalDependencies: dependencies.length,
183
+ directDependencies: dependencies.length,
184
+ issuesBySeverity,
185
+ },
186
+ };
187
+ }
188
+ /**
189
+ * Extract dependencies from various Python dependency files
190
+ */
191
+ async extractDependencies(projectPath) {
192
+ const dependencies = [];
193
+ // Check requirements.txt
194
+ const requirementsPath = (0, path_1.join)(projectPath, "requirements.txt");
195
+ if ((0, fs_1.existsSync)(requirementsPath)) {
196
+ const content = (0, fs_1.readFileSync)(requirementsPath, "utf-8");
197
+ dependencies.push(...this.parseRequirementsTxt(content));
198
+ }
199
+ // Check pyproject.toml
200
+ const pyprojectPath = (0, path_1.join)(projectPath, "pyproject.toml");
201
+ if ((0, fs_1.existsSync)(pyprojectPath)) {
202
+ const content = (0, fs_1.readFileSync)(pyprojectPath, "utf-8");
203
+ dependencies.push(...this.parsePyprojectToml(content));
204
+ }
205
+ // Check Pipfile
206
+ const pipfilePath = (0, path_1.join)(projectPath, "Pipfile");
207
+ if ((0, fs_1.existsSync)(pipfilePath)) {
208
+ const content = (0, fs_1.readFileSync)(pipfilePath, "utf-8");
209
+ dependencies.push(...this.parsePipfile(content));
210
+ }
211
+ return dependencies;
212
+ }
213
+ /**
214
+ * Parse requirements.txt format
215
+ */
216
+ parseRequirementsTxt(content) {
217
+ const dependencies = [];
218
+ const lines = content.split("\n");
219
+ for (const line of lines) {
220
+ const trimmed = line.trim();
221
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) {
222
+ continue;
223
+ }
224
+ // Match package==version or package>=version etc.
225
+ const match = trimmed.match(/^([a-zA-Z0-9_-]+)\s*([=<>!~]+)?\s*([\d.]+)?/);
226
+ if (match && match[1]) {
227
+ dependencies.push({
228
+ name: match[1],
229
+ version: match[3] ?? "*",
230
+ source: "requirements",
231
+ });
232
+ }
233
+ }
234
+ return dependencies;
235
+ }
236
+ /**
237
+ * Parse pyproject.toml dependencies
238
+ */
239
+ parsePyprojectToml(content) {
240
+ const dependencies = [];
241
+ // Simple regex parsing for dependencies section
242
+ const depsMatch = content.match(/\[project\.dependencies\]([\s\S]*?)(?:\[|$)/);
243
+ if (depsMatch && depsMatch[1]) {
244
+ const lines = depsMatch[1].split("\n");
245
+ for (const line of lines) {
246
+ const match = line.match(/^\s*"([^"]+)"/);
247
+ if (match && match[1]) {
248
+ const depSpec = match[1];
249
+ const nameMatch = depSpec.match(/^([a-zA-Z0-9_-]+)/);
250
+ const versionMatch = depSpec.match(/[=<>!~]+([\d.]+)/);
251
+ if (nameMatch && nameMatch[1]) {
252
+ dependencies.push({
253
+ name: nameMatch[1],
254
+ version: versionMatch?.[1] ?? "*",
255
+ source: "pyproject",
256
+ });
257
+ }
258
+ }
259
+ }
260
+ }
261
+ return dependencies;
262
+ }
263
+ /**
264
+ * Parse Pipfile dependencies
265
+ */
266
+ parsePipfile(content) {
267
+ const dependencies = [];
268
+ // Match [packages] section
269
+ const packagesMatch = content.match(/\[packages\]([\s\S]*?)(?:\[|$)/);
270
+ if (packagesMatch && packagesMatch[1]) {
271
+ const lines = packagesMatch[1].split("\n");
272
+ for (const line of lines) {
273
+ const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"?([^"\n]+)"?/);
274
+ if (match && match[1] && match[2]) {
275
+ dependencies.push({
276
+ name: match[1],
277
+ version: match[2] === "*" ? "*" : match[2],
278
+ source: "pipfile",
279
+ });
280
+ }
281
+ }
282
+ }
283
+ return dependencies;
284
+ }
285
+ /**
286
+ * Find all Python files in project
287
+ */
288
+ async findPythonFiles(projectPath) {
289
+ const files = [];
290
+ // Simple recursive file finder
291
+ const walkDir = (dir) => {
292
+ try {
293
+ const { readdirSync, statSync } = require("fs");
294
+ const entries = readdirSync(dir);
295
+ for (const entry of entries) {
296
+ const fullPath = (0, path_1.join)(dir, entry);
297
+ // Skip common non-source directories
298
+ if ([
299
+ "node_modules",
300
+ ".git",
301
+ "__pycache__",
302
+ ".venv",
303
+ "venv",
304
+ "env",
305
+ ".tox",
306
+ ].includes(entry)) {
307
+ continue;
308
+ }
309
+ try {
310
+ const stat = statSync(fullPath);
311
+ if (stat.isDirectory()) {
312
+ walkDir(fullPath);
313
+ }
314
+ else if (entry.endsWith(".py")) {
315
+ files.push(fullPath);
316
+ }
317
+ }
318
+ catch {
319
+ // Skip files we can't access
320
+ }
321
+ }
322
+ }
323
+ catch {
324
+ // Skip directories we can't access
325
+ }
326
+ };
327
+ walkDir(projectPath);
328
+ return files;
329
+ }
330
+ /**
331
+ * Scan a Python file for security issues
332
+ */
333
+ async scanFile(filePath) {
334
+ const issues = [];
335
+ try {
336
+ const content = (0, fs_1.readFileSync)(filePath, "utf-8");
337
+ const lines = content.split("\n");
338
+ // Check for dangerous imports
339
+ for (let i = 0; i < lines.length; i++) {
340
+ const line = lines[i];
341
+ for (const dangerous of DANGEROUS_IMPORTS) {
342
+ if (line &&
343
+ (line.includes(`import ${dangerous.module}`) ||
344
+ line.includes(`from ${dangerous.module}`) ||
345
+ line.includes(dangerous.module))) {
346
+ issues.push({
347
+ type: "dangerous_import",
348
+ severity: dangerous.severity,
349
+ file: filePath,
350
+ line: i + 1,
351
+ message: `Dangerous import: ${dangerous.module} - ${dangerous.reason}`,
352
+ recommendation: `Review usage of ${dangerous.module} and ensure proper security controls`,
353
+ });
354
+ }
355
+ }
356
+ }
357
+ // Check for secrets
358
+ for (const secretPattern of PYTHON_SECRET_PATTERNS) {
359
+ const matches = content.matchAll(secretPattern.pattern);
360
+ for (const match of matches) {
361
+ const lineNum = content.substring(0, match.index).split("\n").length;
362
+ issues.push({
363
+ type: "secret",
364
+ severity: "critical",
365
+ file: filePath,
366
+ line: lineNum,
367
+ message: `Potential ${secretPattern.type} detected`,
368
+ recommendation: "Move secrets to environment variables or a secure vault",
369
+ });
370
+ }
371
+ }
372
+ // Check for vulnerability patterns
373
+ for (const vulnPattern of VULNERABILITY_PATTERNS) {
374
+ const matches = content.matchAll(vulnPattern.pattern);
375
+ for (const match of matches) {
376
+ const lineNum = content.substring(0, match.index).split("\n").length;
377
+ issues.push({
378
+ type: "code_pattern",
379
+ severity: vulnPattern.severity,
380
+ file: filePath,
381
+ line: lineNum,
382
+ message: `${vulnPattern.type}: ${vulnPattern.message}`,
383
+ recommendation: `Fix the ${vulnPattern.type.toLowerCase()} vulnerability`,
384
+ });
385
+ }
386
+ }
387
+ }
388
+ catch (error) {
389
+ // Skip files we can't read
390
+ }
391
+ return issues;
392
+ }
393
+ /**
394
+ * Detect Python version from project
395
+ */
396
+ detectPythonVersion(projectPath) {
397
+ // Check pyproject.toml for requires-python
398
+ const pyprojectPath = (0, path_1.join)(projectPath, "pyproject.toml");
399
+ if ((0, fs_1.existsSync)(pyprojectPath)) {
400
+ const content = (0, fs_1.readFileSync)(pyprojectPath, "utf-8");
401
+ const match = content.match(/requires-python\s*=\s*"([^"]+)"/);
402
+ if (match) {
403
+ return match[1];
404
+ }
405
+ }
406
+ // Check .python-version file
407
+ const pythonVersionPath = (0, path_1.join)(projectPath, ".python-version");
408
+ if ((0, fs_1.existsSync)(pythonVersionPath)) {
409
+ return (0, fs_1.readFileSync)(pythonVersionPath, "utf-8").trim();
410
+ }
411
+ // Check runtime.txt (Heroku)
412
+ const runtimePath = (0, path_1.join)(projectPath, "runtime.txt");
413
+ if ((0, fs_1.existsSync)(runtimePath)) {
414
+ const content = (0, fs_1.readFileSync)(runtimePath, "utf-8").trim();
415
+ const match = content.match(/python-([\d.]+)/);
416
+ if (match) {
417
+ return match[1];
418
+ }
419
+ }
420
+ return undefined;
421
+ }
422
+ }
423
+ exports.PythonAnalyzer = PythonAnalyzer;
424
+ // Export singleton
425
+ exports.pythonAnalyzer = new PythonAnalyzer();
@@ -0,0 +1,28 @@
1
+ /**
2
+ * License Compatibility Matrix
3
+ *
4
+ * Defines which licenses are compatible with each other
5
+ */
6
+ export type LicenseType = "MIT" | "Apache-2.0" | "BSD-2-Clause" | "BSD-3-Clause" | "ISC" | "GPL-2.0" | "GPL-3.0" | "LGPL-2.1" | "LGPL-3.0" | "AGPL-3.0" | "MPL-2.0" | "CDDL-1.0" | "EPL-2.0" | "Unlicense" | "CC0-1.0" | "Proprietary" | "Unknown";
7
+ export type LicenseCategory = "permissive" | "weak_copyleft" | "strong_copyleft" | "public_domain" | "proprietary";
8
+ export interface LicenseInfo {
9
+ name: string;
10
+ category: LicenseCategory;
11
+ requiresAttribution: boolean;
12
+ requiresSourceDisclosure: boolean;
13
+ requiresSameLicense: boolean;
14
+ allowsCommercialUse: boolean;
15
+ allowsModification: boolean;
16
+ allowsDistribution: boolean;
17
+ patentGrant: boolean;
18
+ }
19
+ /**
20
+ * License metadata
21
+ */
22
+ export declare const LICENSE_INFO: Record<LicenseType, LicenseInfo>;
23
+ /**
24
+ * Compatibility matrix
25
+ * true = compatible, false = incompatible
26
+ */
27
+ export declare const COMPATIBILITY_MATRIX: Record<LicenseType, Record<LicenseType, boolean>>;
28
+ //# sourceMappingURL=compatibility-matrix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatibility-matrix.d.ts","sourceRoot":"","sources":["../../src/license/compatibility-matrix.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,WAAW,GACnB,KAAK,GACL,YAAY,GACZ,cAAc,GACd,cAAc,GACd,KAAK,GACL,SAAS,GACT,SAAS,GACT,UAAU,GACV,UAAU,GACV,UAAU,GACV,SAAS,GACT,UAAU,GACV,SAAS,GACT,WAAW,GACX,SAAS,GACT,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CA4LzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,WAAW,EACX,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAmGtB,CAAC"}