verification-layer 0.20.0 → 0.22.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 +251 -615
- package/dist/cli.js +542 -0
- package/dist/cli.js.map +1 -1
- package/dist/marketplace/index.d.ts +8 -0
- package/dist/marketplace/index.d.ts.map +1 -0
- package/dist/marketplace/index.js +7 -0
- package/dist/marketplace/index.js.map +1 -0
- package/dist/marketplace/installer.d.ts +62 -0
- package/dist/marketplace/installer.d.ts.map +1 -0
- package/dist/marketplace/installer.js +254 -0
- package/dist/marketplace/installer.js.map +1 -0
- package/dist/marketplace/registry.d.ts +52 -0
- package/dist/marketplace/registry.d.ts.map +1 -0
- package/dist/marketplace/registry.js +759 -0
- package/dist/marketplace/registry.js.map +1 -0
- package/dist/marketplace/types.d.ts +123 -0
- package/dist/marketplace/types.d.ts.map +1 -0
- package/dist/marketplace/types.js +6 -0
- package/dist/marketplace/types.js.map +1 -0
- package/dist/reporters/audit-report.d.ts.map +1 -1
- package/dist/reporters/audit-report.js +180 -0
- package/dist/reporters/audit-report.js.map +1 -1
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js +2612 -5
- package/dist/reporters/index.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +15 -1
- package/dist/scan.js.map +1 -1
- package/dist/scanners/api-security/index.d.ts +7 -0
- package/dist/scanners/api-security/index.d.ts.map +1 -0
- package/dist/scanners/api-security/index.js +139 -0
- package/dist/scanners/api-security/index.js.map +1 -0
- package/dist/scanners/api-security/index.test.d.ts +5 -0
- package/dist/scanners/api-security/index.test.d.ts.map +1 -0
- package/dist/scanners/api-security/index.test.js +360 -0
- package/dist/scanners/api-security/index.test.js.map +1 -0
- package/dist/scanners/api-security/patterns.d.ts +32 -0
- package/dist/scanners/api-security/patterns.d.ts.map +1 -0
- package/dist/scanners/api-security/patterns.js +159 -0
- package/dist/scanners/api-security/patterns.js.map +1 -0
- package/dist/scanners/authentication/index.d.ts +7 -0
- package/dist/scanners/authentication/index.d.ts.map +1 -0
- package/dist/scanners/authentication/index.js +107 -0
- package/dist/scanners/authentication/index.js.map +1 -0
- package/dist/scanners/authentication/index.test.d.ts +5 -0
- package/dist/scanners/authentication/index.test.d.ts.map +1 -0
- package/dist/scanners/authentication/index.test.js +379 -0
- package/dist/scanners/authentication/index.test.js.map +1 -0
- package/dist/scanners/authentication/patterns.d.ts +32 -0
- package/dist/scanners/authentication/patterns.d.ts.map +1 -0
- package/dist/scanners/authentication/patterns.js +133 -0
- package/dist/scanners/authentication/patterns.js.map +1 -0
- package/dist/scanners/configuration/index.d.ts +8 -0
- package/dist/scanners/configuration/index.d.ts.map +1 -0
- package/dist/scanners/configuration/index.js +87 -0
- package/dist/scanners/configuration/index.js.map +1 -0
- package/dist/scanners/configuration/index.test.d.ts +5 -0
- package/dist/scanners/configuration/index.test.d.ts.map +1 -0
- package/dist/scanners/configuration/index.test.js +344 -0
- package/dist/scanners/configuration/index.test.js.map +1 -0
- package/dist/scanners/configuration/patterns.d.ts +32 -0
- package/dist/scanners/configuration/patterns.d.ts.map +1 -0
- package/dist/scanners/configuration/patterns.js +146 -0
- package/dist/scanners/configuration/patterns.js.map +1 -0
- package/dist/scanners/credentials/index.d.ts +7 -0
- package/dist/scanners/credentials/index.d.ts.map +1 -0
- package/dist/scanners/credentials/index.js +129 -0
- package/dist/scanners/credentials/index.js.map +1 -0
- package/dist/scanners/credentials/index.test.d.ts +5 -0
- package/dist/scanners/credentials/index.test.d.ts.map +1 -0
- package/dist/scanners/credentials/index.test.js +395 -0
- package/dist/scanners/credentials/index.test.js.map +1 -0
- package/dist/scanners/credentials/patterns.d.ts +32 -0
- package/dist/scanners/credentials/patterns.d.ts.map +1 -0
- package/dist/scanners/credentials/patterns.js +140 -0
- package/dist/scanners/credentials/patterns.js.map +1 -0
- package/dist/scanners/errors/index.d.ts +8 -0
- package/dist/scanners/errors/index.d.ts.map +1 -0
- package/dist/scanners/errors/index.js +78 -0
- package/dist/scanners/errors/index.js.map +1 -0
- package/dist/scanners/errors/index.test.d.ts +5 -0
- package/dist/scanners/errors/index.test.d.ts.map +1 -0
- package/dist/scanners/errors/index.test.js +330 -0
- package/dist/scanners/errors/index.test.js.map +1 -0
- package/dist/scanners/errors/patterns.d.ts +27 -0
- package/dist/scanners/errors/patterns.d.ts.map +1 -0
- package/dist/scanners/errors/patterns.js +97 -0
- package/dist/scanners/errors/patterns.js.map +1 -0
- package/dist/scanners/hipaa2026/index.d.ts +8 -0
- package/dist/scanners/hipaa2026/index.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/index.js +345 -0
- package/dist/scanners/hipaa2026/index.js.map +1 -0
- package/dist/scanners/hipaa2026/index.test.d.ts +5 -0
- package/dist/scanners/hipaa2026/index.test.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/index.test.js +332 -0
- package/dist/scanners/hipaa2026/index.test.js.map +1 -0
- package/dist/scanners/hipaa2026/patterns.d.ts +57 -0
- package/dist/scanners/hipaa2026/patterns.d.ts.map +1 -0
- package/dist/scanners/hipaa2026/patterns.js +268 -0
- package/dist/scanners/hipaa2026/patterns.js.map +1 -0
- package/dist/scanners/operational/index.d.ts +7 -0
- package/dist/scanners/operational/index.d.ts.map +1 -0
- package/dist/scanners/operational/index.js +171 -0
- package/dist/scanners/operational/index.js.map +1 -0
- package/dist/scanners/operational/index.test.d.ts +5 -0
- package/dist/scanners/operational/index.test.d.ts.map +1 -0
- package/dist/scanners/operational/index.test.js +406 -0
- package/dist/scanners/operational/index.test.js.map +1 -0
- package/dist/scanners/operational/patterns.d.ts +33 -0
- package/dist/scanners/operational/patterns.d.ts.map +1 -0
- package/dist/scanners/operational/patterns.js +151 -0
- package/dist/scanners/operational/patterns.js.map +1 -0
- package/dist/scanners/rbac/index.d.ts +7 -0
- package/dist/scanners/rbac/index.d.ts.map +1 -0
- package/dist/scanners/rbac/index.js +145 -0
- package/dist/scanners/rbac/index.js.map +1 -0
- package/dist/scanners/rbac/index.test.d.ts +5 -0
- package/dist/scanners/rbac/index.test.d.ts.map +1 -0
- package/dist/scanners/rbac/index.test.js +422 -0
- package/dist/scanners/rbac/index.test.js.map +1 -0
- package/dist/scanners/rbac/patterns.d.ts +32 -0
- package/dist/scanners/rbac/patterns.d.ts.map +1 -0
- package/dist/scanners/rbac/patterns.js +124 -0
- package/dist/scanners/rbac/patterns.js.map +1 -0
- package/dist/scanners/revocation/index.d.ts +8 -0
- package/dist/scanners/revocation/index.d.ts.map +1 -0
- package/dist/scanners/revocation/index.js +83 -0
- package/dist/scanners/revocation/index.js.map +1 -0
- package/dist/scanners/revocation/index.test.d.ts +5 -0
- package/dist/scanners/revocation/index.test.d.ts.map +1 -0
- package/dist/scanners/revocation/index.test.js +332 -0
- package/dist/scanners/revocation/index.test.js.map +1 -0
- package/dist/scanners/revocation/patterns.d.ts +27 -0
- package/dist/scanners/revocation/patterns.d.ts.map +1 -0
- package/dist/scanners/revocation/patterns.js +109 -0
- package/dist/scanners/revocation/patterns.js.map +1 -0
- package/dist/scanners/sanitization/index.d.ts +8 -0
- package/dist/scanners/sanitization/index.d.ts.map +1 -0
- package/dist/scanners/sanitization/index.js +98 -0
- package/dist/scanners/sanitization/index.js.map +1 -0
- package/dist/scanners/sanitization/index.test.d.ts +5 -0
- package/dist/scanners/sanitization/index.test.d.ts.map +1 -0
- package/dist/scanners/sanitization/index.test.js +370 -0
- package/dist/scanners/sanitization/index.test.js.map +1 -0
- package/dist/scanners/sanitization/patterns.d.ts +27 -0
- package/dist/scanners/sanitization/patterns.d.ts.map +1 -0
- package/dist/scanners/sanitization/patterns.js +117 -0
- package/dist/scanners/sanitization/patterns.js.map +1 -0
- package/dist/training/certificate.d.ts +26 -0
- package/dist/training/certificate.d.ts.map +1 -0
- package/dist/training/certificate.js +92 -0
- package/dist/training/certificate.js.map +1 -0
- package/dist/training/index.d.ts +3 -0
- package/dist/training/index.d.ts.map +1 -0
- package/dist/training/index.js +243 -0
- package/dist/training/index.js.map +1 -0
- package/dist/training/modules.d.ts +13 -0
- package/dist/training/modules.d.ts.map +1 -0
- package/dist/training/modules.js +608 -0
- package/dist/training/modules.js.map +1 -0
- package/dist/training/questions.d.ts +9 -0
- package/dist/training/questions.d.ts.map +1 -0
- package/dist/training/questions.js +505 -0
- package/dist/training/questions.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/npm-audit.d.ts +6 -0
- package/dist/utils/npm-audit.d.ts.map +1 -0
- package/dist/utils/npm-audit.js +95 -0
- package/dist/utils/npm-audit.js.map +1 -0
- package/dist/utils/scan-history.d.ts +59 -0
- package/dist/utils/scan-history.d.ts.map +1 -0
- package/dist/utils/scan-history.js +170 -0
- package/dist/utils/scan-history.js.map +1 -0
- package/package.json +4 -1
- package/templates/baa-verification-letter.md +105 -0
- package/templates/irp.md +545 -0
- package/templates/notice-of-privacy-practices.md +491 -0
- package/templates/physical-safeguards-checklist.md +247 -0
- package/templates/security-officer-designation.md +237 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Sanitization Security Detection Patterns
|
|
3
|
+
* Detects unsafe user input handling and file upload configurations
|
|
4
|
+
*/
|
|
5
|
+
export interface SanitizationPattern {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
severity: 'critical' | 'high' | 'medium';
|
|
10
|
+
hipaaReference: string;
|
|
11
|
+
patterns: RegExp[];
|
|
12
|
+
negativePatterns?: RegExp[];
|
|
13
|
+
recommendation: string;
|
|
14
|
+
category: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* SANITIZE-001: Unsanitized User Input in Database Operations
|
|
18
|
+
* Detects req.body/req.params/req.query used directly in DB operations
|
|
19
|
+
*/
|
|
20
|
+
export declare const UNSANITIZED_DB_INPUT: SanitizationPattern;
|
|
21
|
+
/**
|
|
22
|
+
* SANITIZE-002: Insecure File Upload Configuration
|
|
23
|
+
* Detects file upload configuration without proper validation
|
|
24
|
+
*/
|
|
25
|
+
export declare const INSECURE_FILE_UPLOAD: SanitizationPattern;
|
|
26
|
+
export declare const ALL_SANITIZATION_PATTERNS: SanitizationPattern[];
|
|
27
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/scanners/sanitization/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,mBA0ElC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,mBAgDlC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,mBAAmB,EAG1D,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Sanitization Security Detection Patterns
|
|
3
|
+
* Detects unsafe user input handling and file upload configurations
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* SANITIZE-001: Unsanitized User Input in Database Operations
|
|
7
|
+
* Detects req.body/req.params/req.query used directly in DB operations
|
|
8
|
+
*/
|
|
9
|
+
export const UNSANITIZED_DB_INPUT = {
|
|
10
|
+
id: 'SANITIZE-001',
|
|
11
|
+
name: 'Unsanitized User Input in Database Operations',
|
|
12
|
+
description: 'User input from req.body, req.params, or req.query used directly in database operations (insert, update, query, sql, where) without validation (zod, yup, joi, validate, sanitize, parse)',
|
|
13
|
+
severity: 'critical',
|
|
14
|
+
hipaaReference: 'NPRM Anti-malware',
|
|
15
|
+
patterns: [
|
|
16
|
+
// Database operations with req.body - matches both .insert() and method calls
|
|
17
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\.body/i,
|
|
18
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\['body'\]/i,
|
|
19
|
+
// Database operations with req.params
|
|
20
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\.params/i,
|
|
21
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\['params'\]/i,
|
|
22
|
+
// Database operations with req.query
|
|
23
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\.query/i,
|
|
24
|
+
/(?:insert|update|query|sql|where|execute)\s*\([^)]*req\['query'\]/i,
|
|
25
|
+
// SQL template literals with user input
|
|
26
|
+
/(?:sql|query|execute)\s*`[^`]*\$\{req\.(?:body|params|query)/i,
|
|
27
|
+
// Raw SQL queries with concatenation
|
|
28
|
+
/(?:SELECT|INSERT|UPDATE|DELETE).*?\+\s*req\.(?:body|params|query)/i,
|
|
29
|
+
// Prisma operations with unsanitized input
|
|
30
|
+
/prisma\.\w+\.(?:create|update|upsert|delete)\s*\([^)]*req\.(?:body|params|query)/i,
|
|
31
|
+
// Mongoose operations with unsanitized input
|
|
32
|
+
/(?:create|findOneAndUpdate|updateOne|updateMany)\s*\([^)]*req\.(?:body|params|query)/i,
|
|
33
|
+
// TypeORM operations
|
|
34
|
+
/(?:save|insert|update)\s*\([^)]*req\.(?:body|params|query)/i,
|
|
35
|
+
// Knex operations
|
|
36
|
+
/knex\s*\([^)]*\)\.(?:insert|update|where)\s*\([^)]*req\.(?:body|params|query)/i,
|
|
37
|
+
// Generic database method calls with chaining like .values(), .set(), .data
|
|
38
|
+
/\.(?:values|set|data)\s*\([^)]*req\.(?:body|params|query)/i,
|
|
39
|
+
// Direct method calls like User.create(req.body)
|
|
40
|
+
/\w+\.create\s*\(\s*req\.(?:body|params|query)/i,
|
|
41
|
+
],
|
|
42
|
+
negativePatterns: [
|
|
43
|
+
// Validation libraries
|
|
44
|
+
/zod/i,
|
|
45
|
+
/yup/i,
|
|
46
|
+
/joi/i,
|
|
47
|
+
/validate/i,
|
|
48
|
+
/sanitize/i,
|
|
49
|
+
/parse(?:Int|Float|Body)?/i,
|
|
50
|
+
/safeParse/i,
|
|
51
|
+
// Schema validation
|
|
52
|
+
/schema\.validate/i,
|
|
53
|
+
/\.schema\(/i,
|
|
54
|
+
// Type guards and checks
|
|
55
|
+
/typeof\s+/i,
|
|
56
|
+
/instanceof\s+/i,
|
|
57
|
+
// Validation middleware
|
|
58
|
+
/validationResult/i,
|
|
59
|
+
/express-validator/i,
|
|
60
|
+
// Safe wrappers
|
|
61
|
+
/sanitized/i,
|
|
62
|
+
/validated/i,
|
|
63
|
+
/checked/i,
|
|
64
|
+
],
|
|
65
|
+
recommendation: 'Always validate and sanitize user input before database operations. Use validation libraries like Zod, Yup, or Joi. Example: const validated = schema.parse(req.body); await db.insert(validated). Never use raw req.body/params/query directly in database operations.',
|
|
66
|
+
category: 'access-control',
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* SANITIZE-002: Insecure File Upload Configuration
|
|
70
|
+
* Detects file upload configuration without proper validation
|
|
71
|
+
*/
|
|
72
|
+
export const INSECURE_FILE_UPLOAD = {
|
|
73
|
+
id: 'SANITIZE-002',
|
|
74
|
+
name: 'Insecure File Upload Configuration',
|
|
75
|
+
description: 'File upload configuration (multer, formidable, busboy) missing fileFilter, limits, or MIME type validation',
|
|
76
|
+
severity: 'high',
|
|
77
|
+
hipaaReference: 'NPRM Anti-malware',
|
|
78
|
+
patterns: [
|
|
79
|
+
// Multer configuration (will check context for validation)
|
|
80
|
+
/multer\s*\(\s*\{/i,
|
|
81
|
+
// Formidable without file validation
|
|
82
|
+
/(?:new\s+)?formidable\.IncomingForm\s*\(/i,
|
|
83
|
+
/formidable\(\s*\(/i,
|
|
84
|
+
// Busboy without limits
|
|
85
|
+
/(?:new\s+)?Busboy\s*\(\s*\{/i,
|
|
86
|
+
/busboy\s*\(\s*\{/i,
|
|
87
|
+
],
|
|
88
|
+
negativePatterns: [
|
|
89
|
+
// Has fileFilter
|
|
90
|
+
/fileFilter/i,
|
|
91
|
+
// Has limits
|
|
92
|
+
/limits\s*:/i,
|
|
93
|
+
/fileSize/i,
|
|
94
|
+
/maxFileSize/i,
|
|
95
|
+
// Has MIME type validation
|
|
96
|
+
/mimetype/i,
|
|
97
|
+
/mimeType/i,
|
|
98
|
+
/contentType/i,
|
|
99
|
+
/allowedTypes/i,
|
|
100
|
+
/allowedMimeTypes/i,
|
|
101
|
+
// File type checking
|
|
102
|
+
/\.endsWith\s*\(/i,
|
|
103
|
+
/\.includes\s*\(/i,
|
|
104
|
+
/test\s*\(/i, // regex test for file extensions
|
|
105
|
+
// Validation functions
|
|
106
|
+
/validateFile/i,
|
|
107
|
+
/checkFileType/i,
|
|
108
|
+
/isValidFile/i,
|
|
109
|
+
],
|
|
110
|
+
recommendation: 'Configure file upload middleware with proper validation. Example: multer({ fileFilter: (req, file, cb) => { if (allowedMimes.includes(file.mimetype)) cb(null, true); else cb(new Error("Invalid file type")); }, limits: { fileSize: 5 * 1024 * 1024 } }). Always validate file type, size, and extension.',
|
|
111
|
+
category: 'access-control',
|
|
112
|
+
};
|
|
113
|
+
export const ALL_SANITIZATION_PATTERNS = [
|
|
114
|
+
UNSANITIZED_DB_INPUT,
|
|
115
|
+
INSECURE_FILE_UPLOAD,
|
|
116
|
+
];
|
|
117
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/scanners/sanitization/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB;IACvD,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,+CAA+C;IACrD,WAAW,EACT,2LAA2L;IAC7L,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,mBAAmB;IACnC,QAAQ,EAAE;QACR,8EAA8E;QAC9E,+DAA+D;QAC/D,mEAAmE;QAEnE,sCAAsC;QACtC,iEAAiE;QACjE,qEAAqE;QAErE,qCAAqC;QACrC,gEAAgE;QAChE,oEAAoE;QAEpE,wCAAwC;QACxC,+DAA+D;QAE/D,qCAAqC;QACrC,oEAAoE;QAEpE,2CAA2C;QAC3C,mFAAmF;QAEnF,6CAA6C;QAC7C,uFAAuF;QAEvF,qBAAqB;QACrB,6DAA6D;QAE7D,kBAAkB;QAClB,gFAAgF;QAEhF,4EAA4E;QAC5E,4DAA4D;QAE5D,iDAAiD;QACjD,gDAAgD;KACjD;IACD,gBAAgB,EAAE;QAChB,uBAAuB;QACvB,MAAM;QACN,MAAM;QACN,MAAM;QACN,WAAW;QACX,WAAW;QACX,2BAA2B;QAC3B,YAAY;QAEZ,oBAAoB;QACpB,mBAAmB;QACnB,aAAa;QAEb,yBAAyB;QACzB,YAAY;QACZ,gBAAgB;QAEhB,wBAAwB;QACxB,mBAAmB;QACnB,oBAAoB;QAEpB,gBAAgB;QAChB,YAAY;QACZ,YAAY;QACZ,UAAU;KACX;IACD,cAAc,EACZ,yQAAyQ;IAC3Q,QAAQ,EAAE,gBAAgB;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB;IACvD,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,oCAAoC;IAC1C,WAAW,EACT,4GAA4G;IAC9G,QAAQ,EAAE,MAAM;IAChB,cAAc,EAAE,mBAAmB;IACnC,QAAQ,EAAE;QACR,2DAA2D;QAC3D,mBAAmB;QAEnB,qCAAqC;QACrC,2CAA2C;QAC3C,oBAAoB;QAEpB,wBAAwB;QACxB,8BAA8B;QAC9B,mBAAmB;KACpB;IACD,gBAAgB,EAAE;QAChB,iBAAiB;QACjB,aAAa;QAEb,aAAa;QACb,aAAa;QACb,WAAW;QACX,cAAc;QAEd,2BAA2B;QAC3B,WAAW;QACX,WAAW;QACX,cAAc;QACd,eAAe;QACf,mBAAmB;QAEnB,qBAAqB;QACrB,kBAAkB;QAClB,kBAAkB;QAClB,YAAY,EAAE,iCAAiC;QAE/C,uBAAuB;QACvB,eAAe;QACf,gBAAgB;QAChB,cAAc;KACf;IACD,cAAc,EACZ,6SAA6S;IAC/S,QAAQ,EAAE,gBAAgB;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAA0B;IAC9D,oBAAoB;IACpB,oBAAoB;CACrB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface Certificate {
|
|
2
|
+
name: string;
|
|
3
|
+
email?: string;
|
|
4
|
+
date: string;
|
|
5
|
+
score: number;
|
|
6
|
+
totalQuestions: number;
|
|
7
|
+
percentage: number;
|
|
8
|
+
moduleScores: Record<number, {
|
|
9
|
+
correct: number;
|
|
10
|
+
total: number;
|
|
11
|
+
percentage: number;
|
|
12
|
+
}>;
|
|
13
|
+
duration: number;
|
|
14
|
+
hash: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function generateVerificationHash(certificate: Omit<Certificate, 'hash'>): string;
|
|
17
|
+
export declare function saveCertificate(name: string, email: string | undefined, score: number, totalQuestions: number, moduleScores: Record<number, {
|
|
18
|
+
correct: number;
|
|
19
|
+
total: number;
|
|
20
|
+
percentage: number;
|
|
21
|
+
}>, duration: number): Promise<{
|
|
22
|
+
certificate: Certificate;
|
|
23
|
+
path: string;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function printCertificate(certificate: Certificate): string;
|
|
26
|
+
//# sourceMappingURL=certificate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../../src/training/certificate.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAClB,MAAM,EACN;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CACvD,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,MAAM,CAUvF;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EACpF,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BrD;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAoDjE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { writeFile, mkdir } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
export function generateVerificationHash(certificate) {
|
|
6
|
+
const data = JSON.stringify({
|
|
7
|
+
name: certificate.name,
|
|
8
|
+
email: certificate.email,
|
|
9
|
+
date: certificate.date,
|
|
10
|
+
score: certificate.score,
|
|
11
|
+
totalQuestions: certificate.totalQuestions,
|
|
12
|
+
});
|
|
13
|
+
return createHash('sha256').update(data + 'vlayer-hipaa-training').digest('hex');
|
|
14
|
+
}
|
|
15
|
+
export async function saveCertificate(name, email, score, totalQuestions, moduleScores, duration) {
|
|
16
|
+
const date = new Date().toISOString();
|
|
17
|
+
const percentage = Math.round((score / totalQuestions) * 100);
|
|
18
|
+
const certificateData = {
|
|
19
|
+
name,
|
|
20
|
+
email,
|
|
21
|
+
date,
|
|
22
|
+
score,
|
|
23
|
+
totalQuestions,
|
|
24
|
+
percentage,
|
|
25
|
+
moduleScores,
|
|
26
|
+
duration,
|
|
27
|
+
};
|
|
28
|
+
const hash = generateVerificationHash(certificateData);
|
|
29
|
+
const certificate = { ...certificateData, hash };
|
|
30
|
+
// Save to .vlayer/training/ in home directory
|
|
31
|
+
const trainingDir = join(homedir(), '.vlayer', 'training');
|
|
32
|
+
await mkdir(trainingDir, { recursive: true });
|
|
33
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
34
|
+
const safeName = name.replace(/[^a-zA-Z0-9]/g, '_');
|
|
35
|
+
const filename = `${safeName}-${timestamp}.json`;
|
|
36
|
+
const filepath = join(trainingDir, filename);
|
|
37
|
+
await writeFile(filepath, JSON.stringify(certificate, null, 2), 'utf-8');
|
|
38
|
+
return { certificate, path: filepath };
|
|
39
|
+
}
|
|
40
|
+
export function printCertificate(certificate) {
|
|
41
|
+
const border = '═'.repeat(70);
|
|
42
|
+
const passed = certificate.percentage >= 70;
|
|
43
|
+
return `
|
|
44
|
+
╔${border}╗
|
|
45
|
+
║${' '.repeat(70)}║
|
|
46
|
+
║${'CERTIFICATE OF COMPLETION'.padStart(42).padEnd(70)}║
|
|
47
|
+
║${' '.repeat(70)}║
|
|
48
|
+
║${'HIPAA Security Training for Developers'.padStart(44).padEnd(70)}║
|
|
49
|
+
║${' '.repeat(70)}║
|
|
50
|
+
╠${border}╣
|
|
51
|
+
║${' '.repeat(70)}║
|
|
52
|
+
║${'This certifies that'.padStart(33).padEnd(70)}║
|
|
53
|
+
║${' '.repeat(70)}║
|
|
54
|
+
║${certificate.name.toUpperCase().padStart((70 + certificate.name.length) / 2).padEnd(70)}║
|
|
55
|
+
║${' '.repeat(70)}║
|
|
56
|
+
║${'has successfully completed the vlayer HIPAA Security Training'.padStart(51).padEnd(70)}║
|
|
57
|
+
║${' '.repeat(70)}║
|
|
58
|
+
╠${border}╣
|
|
59
|
+
║${' '.repeat(70)}║
|
|
60
|
+
║ Date: ${new Date(certificate.date).toLocaleDateString('en-US', {
|
|
61
|
+
year: 'numeric',
|
|
62
|
+
month: 'long',
|
|
63
|
+
day: 'numeric',
|
|
64
|
+
}).padEnd(61)}║
|
|
65
|
+
║ Score: ${certificate.score}/${certificate.totalQuestions} (${certificate.percentage}%)${' '.repeat(70 - 15 - certificate.score.toString().length - certificate.totalQuestions.toString().length - certificate.percentage.toString().length)}║
|
|
66
|
+
║ Status: ${passed ? 'PASSED ✓' : 'NEEDS IMPROVEMENT'}${' '.repeat(58 - (passed ? 9 : 17))}║
|
|
67
|
+
║ Duration: ${certificate.duration} minutes${' '.repeat(52 - certificate.duration.toString().length)}║
|
|
68
|
+
║${' '.repeat(70)}║
|
|
69
|
+
╠${border}╣
|
|
70
|
+
║${' '.repeat(70)}║
|
|
71
|
+
║ Module Breakdown:${' '.repeat(52)}║
|
|
72
|
+
║${' '.repeat(70)}║
|
|
73
|
+
${Object.entries(certificate.moduleScores)
|
|
74
|
+
.map(([moduleId, scores]) => {
|
|
75
|
+
const moduleNum = `Module ${moduleId}`.padEnd(12);
|
|
76
|
+
const scoreStr = `${scores.correct}/${scores.total} (${scores.percentage}%)`;
|
|
77
|
+
return `║ ${moduleNum}${scoreStr.padEnd(56)}║`;
|
|
78
|
+
})
|
|
79
|
+
.join('\n')}
|
|
80
|
+
║${' '.repeat(70)}║
|
|
81
|
+
╠${border}╣
|
|
82
|
+
║${' '.repeat(70)}║
|
|
83
|
+
║ Verification Hash:${' '.repeat(51)}║
|
|
84
|
+
║ ${certificate.hash.substring(0, 60)}${' '.repeat(8)}║
|
|
85
|
+
║ ${certificate.hash.substring(60)}${' '.repeat(70 - certificate.hash.substring(60).length - 2)}║
|
|
86
|
+
║${' '.repeat(70)}║
|
|
87
|
+
║${'Verify at: https://github.com/Francosimon53/verification-layer'.padEnd(70)}║
|
|
88
|
+
║${' '.repeat(70)}║
|
|
89
|
+
╚${border}╝
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=certificate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"certificate.js","sourceRoot":"","sources":["../../src/training/certificate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/training/index.ts"],"names":[],"mappings":"AAYA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAyHjD;AAuJD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA8CxD"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { trainingModules } from './modules.js';
|
|
4
|
+
import { questions } from './questions.js';
|
|
5
|
+
import { saveCertificate, printCertificate } from './certificate.js';
|
|
6
|
+
export async function runTraining() {
|
|
7
|
+
console.clear();
|
|
8
|
+
console.log(chalk.bold.cyan('\n╔═══════════════════════════════════════════════════════════════════════╗'));
|
|
9
|
+
console.log(chalk.bold.cyan('║ ║'));
|
|
10
|
+
console.log(chalk.bold.cyan('║ HIPAA SECURITY TRAINING FOR DEVELOPERS ║'));
|
|
11
|
+
console.log(chalk.bold.cyan('║ ║'));
|
|
12
|
+
console.log(chalk.bold.cyan('╚═══════════════════════════════════════════════════════════════════════╝\n'));
|
|
13
|
+
console.log(chalk.white('Welcome to the HIPAA Security Training for Developers!\n'));
|
|
14
|
+
console.log(chalk.gray('This interactive training covers 10 essential modules on HIPAA compliance,'));
|
|
15
|
+
console.log(chalk.gray('security best practices, and secure coding for healthcare applications.\n'));
|
|
16
|
+
console.log(chalk.yellow('📚 10 modules covering PHI, encryption, access control, APIs, and more'));
|
|
17
|
+
console.log(chalk.yellow('❓ 45+ multiple-choice questions with explanations'));
|
|
18
|
+
console.log(chalk.yellow('🏆 Certificate of completion with verification hash'));
|
|
19
|
+
console.log(chalk.yellow('⏱️ Estimated time: 30-45 minutes\n'));
|
|
20
|
+
const { ready } = await inquirer.prompt([
|
|
21
|
+
{
|
|
22
|
+
type: 'confirm',
|
|
23
|
+
name: 'ready',
|
|
24
|
+
message: 'Are you ready to begin?',
|
|
25
|
+
default: true,
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
28
|
+
if (!ready) {
|
|
29
|
+
console.log(chalk.gray('\nTraining cancelled. Run "vlayer training" when you\'re ready.\n'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const { name, email } = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: 'input',
|
|
35
|
+
name: 'name',
|
|
36
|
+
message: 'Enter your full name (for the certificate):',
|
|
37
|
+
validate: (input) => input.trim().length > 0 || 'Name is required',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: 'input',
|
|
41
|
+
name: 'email',
|
|
42
|
+
message: 'Enter your email (optional):',
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
let totalScore = 0;
|
|
47
|
+
const moduleScores = {};
|
|
48
|
+
// Run through each module
|
|
49
|
+
for (const module of trainingModules) {
|
|
50
|
+
console.clear();
|
|
51
|
+
await runModule(module);
|
|
52
|
+
// Get questions for this module
|
|
53
|
+
const moduleQuestions = questions.filter((q) => q.moduleId === module.id);
|
|
54
|
+
let moduleCorrect = 0;
|
|
55
|
+
for (const question of moduleQuestions) {
|
|
56
|
+
const isCorrect = await askQuestion(question);
|
|
57
|
+
if (isCorrect) {
|
|
58
|
+
moduleCorrect++;
|
|
59
|
+
totalScore++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
moduleScores[module.id] = {
|
|
63
|
+
correct: moduleCorrect,
|
|
64
|
+
total: moduleQuestions.length,
|
|
65
|
+
percentage: Math.round((moduleCorrect / moduleQuestions.length) * 100),
|
|
66
|
+
};
|
|
67
|
+
// Show module summary
|
|
68
|
+
const percentage = Math.round((moduleCorrect / moduleQuestions.length) * 100);
|
|
69
|
+
console.log(chalk.bold(`\n📊 Module ${module.id} Score: ${moduleCorrect}/${moduleQuestions.length} (${percentage}%)`));
|
|
70
|
+
if (percentage >= 80) {
|
|
71
|
+
console.log(chalk.green(' Excellent work! 🌟\n'));
|
|
72
|
+
}
|
|
73
|
+
else if (percentage >= 60) {
|
|
74
|
+
console.log(chalk.yellow(' Good effort! 👍\n'));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(chalk.red(' Review this module. 📚\n'));
|
|
78
|
+
}
|
|
79
|
+
if (module.id < trainingModules.length) {
|
|
80
|
+
const { continueTraining } = await inquirer.prompt([
|
|
81
|
+
{
|
|
82
|
+
type: 'confirm',
|
|
83
|
+
name: 'continueTraining',
|
|
84
|
+
message: 'Continue to next module?',
|
|
85
|
+
default: true,
|
|
86
|
+
},
|
|
87
|
+
]);
|
|
88
|
+
if (!continueTraining) {
|
|
89
|
+
console.log(chalk.gray('\nTraining paused. Your progress has not been saved. Run "vlayer training" to start over.\n'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const endTime = Date.now();
|
|
95
|
+
const duration = Math.round((endTime - startTime) / 60000); // minutes
|
|
96
|
+
// Show final results
|
|
97
|
+
console.clear();
|
|
98
|
+
await showFinalResults(name, email || undefined, totalScore, questions.length, moduleScores, duration);
|
|
99
|
+
}
|
|
100
|
+
async function runModule(module) {
|
|
101
|
+
console.log(chalk.bold.cyan(`\n╔═══ MODULE ${module.id}: ${module.title.toUpperCase()} ${'═'.repeat(Math.max(0, 60 - module.title.length))}╗\n`));
|
|
102
|
+
console.log(chalk.white.bold(module.description + '\n'));
|
|
103
|
+
// Display content
|
|
104
|
+
for (const line of module.content) {
|
|
105
|
+
if (line === '') {
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(chalk.gray(line));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Display code examples if available
|
|
113
|
+
if (module.codeExamples && module.codeExamples.length > 0) {
|
|
114
|
+
for (const example of module.codeExamples) {
|
|
115
|
+
console.log(chalk.bold.red('\n❌ WRONG:\n'));
|
|
116
|
+
console.log(chalk.red(example.wrong));
|
|
117
|
+
console.log(chalk.bold.green('\n✅ RIGHT:\n'));
|
|
118
|
+
console.log(chalk.green(example.right));
|
|
119
|
+
console.log(chalk.bold('\n💡 Explanation:\n'));
|
|
120
|
+
console.log(chalk.gray(example.explanation));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
console.log(chalk.bold.cyan(`\n╚${'═'.repeat(Math.max(0, 69))}╝\n`));
|
|
124
|
+
const { readyForQuestions } = await inquirer.prompt([
|
|
125
|
+
{
|
|
126
|
+
type: 'confirm',
|
|
127
|
+
name: 'readyForQuestions',
|
|
128
|
+
message: 'Ready for the questions?',
|
|
129
|
+
default: true,
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
132
|
+
if (!readyForQuestions) {
|
|
133
|
+
console.log(chalk.gray('Take your time. Press any key when ready...\n'));
|
|
134
|
+
await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'confirm',
|
|
137
|
+
name: 'continue',
|
|
138
|
+
message: 'Ready now?',
|
|
139
|
+
default: true,
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
console.log('');
|
|
144
|
+
}
|
|
145
|
+
async function askQuestion(question) {
|
|
146
|
+
const { answer } = await inquirer.prompt([
|
|
147
|
+
{
|
|
148
|
+
type: 'list',
|
|
149
|
+
name: 'answer',
|
|
150
|
+
message: question.question,
|
|
151
|
+
choices: question.options,
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
const selectedIndex = question.options.indexOf(answer);
|
|
155
|
+
const isCorrect = selectedIndex === question.correctAnswer;
|
|
156
|
+
if (isCorrect) {
|
|
157
|
+
console.log(chalk.green.bold(' ✓ Correct!\n'));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.log(chalk.red.bold(' ✗ Incorrect.\n'));
|
|
161
|
+
console.log(chalk.yellow(` The correct answer is: ${question.options[question.correctAnswer]}\n`));
|
|
162
|
+
}
|
|
163
|
+
console.log(chalk.gray(` 💡 ${question.explanation}\n`));
|
|
164
|
+
return isCorrect;
|
|
165
|
+
}
|
|
166
|
+
async function showFinalResults(name, email, score, total, moduleScores, duration) {
|
|
167
|
+
const percentage = Math.round((score / total) * 100);
|
|
168
|
+
const passed = percentage >= 70;
|
|
169
|
+
console.log(chalk.bold.cyan('\n╔═══════════════════════════════════════════════════════════════════════╗'));
|
|
170
|
+
console.log(chalk.bold.cyan('║ TRAINING COMPLETE! ║'));
|
|
171
|
+
console.log(chalk.bold.cyan('╚═══════════════════════════════════════════════════════════════════════╝\n'));
|
|
172
|
+
console.log(chalk.white(`Name: ${name}`));
|
|
173
|
+
if (email) {
|
|
174
|
+
console.log(chalk.white(`Email: ${email}`));
|
|
175
|
+
}
|
|
176
|
+
console.log(chalk.white(`Duration: ${duration} minutes\n`));
|
|
177
|
+
console.log(chalk.bold('📊 Final Score:\n'));
|
|
178
|
+
console.log(chalk.white(` ${score}/${total} questions correct (${percentage}%)\n`));
|
|
179
|
+
console.log(chalk.bold('📈 Module Breakdown:\n'));
|
|
180
|
+
for (const [moduleId, scores] of Object.entries(moduleScores)) {
|
|
181
|
+
const module = trainingModules.find((m) => m.id === parseInt(moduleId));
|
|
182
|
+
const bar = '█'.repeat(Math.round(scores.percentage / 5)) + '░'.repeat(20 - Math.round(scores.percentage / 5));
|
|
183
|
+
const color = scores.percentage >= 80 ? chalk.green : scores.percentage >= 60 ? chalk.yellow : chalk.red;
|
|
184
|
+
console.log(color(` Module ${moduleId}: ${module?.title.padEnd(45)} ${bar} ${scores.percentage}% (${scores.correct}/${scores.total})`));
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
if (passed) {
|
|
188
|
+
console.log(chalk.green.bold('🎉 CONGRATULATIONS! You passed the training!\n'));
|
|
189
|
+
console.log(chalk.green('You have successfully completed the HIPAA Security Training.'));
|
|
190
|
+
console.log(chalk.green('Your certificate has been generated below.\n'));
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
console.log(chalk.yellow.bold('⚠️ You did not pass the training (70% required).\n'));
|
|
194
|
+
console.log(chalk.yellow('Review the modules and try again. Run "vlayer training" to restart.'));
|
|
195
|
+
console.log(chalk.yellow('Your score has been recorded but no certificate was generated.\n'));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Save certificate
|
|
199
|
+
const { certificate, path } = await saveCertificate(name, email, score, total, moduleScores, duration);
|
|
200
|
+
// Print certificate
|
|
201
|
+
console.log(chalk.cyan(printCertificate(certificate)));
|
|
202
|
+
console.log(chalk.gray(`\n📄 Certificate saved to: ${path}\n`));
|
|
203
|
+
console.log(chalk.gray('Share your verification hash to prove completion!\n'));
|
|
204
|
+
}
|
|
205
|
+
export async function showTrainingStatus() {
|
|
206
|
+
const { readdir } = await import('fs/promises');
|
|
207
|
+
const { join } = await import('path');
|
|
208
|
+
const { homedir } = await import('os');
|
|
209
|
+
const trainingDir = join(homedir(), '.vlayer', 'training');
|
|
210
|
+
try {
|
|
211
|
+
const files = await readdir(trainingDir);
|
|
212
|
+
const certificates = files.filter((f) => f.endsWith('.json'));
|
|
213
|
+
if (certificates.length === 0) {
|
|
214
|
+
console.log(chalk.yellow('\n⚠️ No training certificates found.\n'));
|
|
215
|
+
console.log(chalk.gray('Run "vlayer training" to complete the training and earn a certificate.\n'));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
console.log(chalk.bold.cyan('\n📚 Training Completion Status:\n'));
|
|
219
|
+
for (const certFile of certificates) {
|
|
220
|
+
const certPath = join(trainingDir, certFile);
|
|
221
|
+
const certContent = await import('fs/promises').then((fs) => fs.readFile(certPath, 'utf-8'));
|
|
222
|
+
const cert = JSON.parse(certContent);
|
|
223
|
+
const status = cert.percentage >= 70 ? chalk.green('✓ PASSED') : chalk.red('✗ FAILED');
|
|
224
|
+
const date = new Date(cert.date).toLocaleDateString('en-US', {
|
|
225
|
+
year: 'numeric',
|
|
226
|
+
month: 'short',
|
|
227
|
+
day: 'numeric',
|
|
228
|
+
});
|
|
229
|
+
console.log(chalk.white(`${status} ${cert.name.padEnd(30)} ${cert.percentage}% ${chalk.gray(`(${date})`)}`));
|
|
230
|
+
if (cert.email) {
|
|
231
|
+
console.log(chalk.gray(` ${cert.email}`));
|
|
232
|
+
}
|
|
233
|
+
console.log(chalk.gray(` Hash: ${cert.hash.substring(0, 40)}...`));
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
console.log(chalk.gray(`Total certificates: ${certificates.length}\n`));
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
console.log(chalk.yellow('\n⚠️ No training records found.\n'));
|
|
240
|
+
console.log(chalk.gray('Run "vlayer training" to complete the training.\n'));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/training/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TrainingModule {
|
|
2
|
+
id: number;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
content: string[];
|
|
6
|
+
codeExamples?: {
|
|
7
|
+
wrong: string;
|
|
8
|
+
right: string;
|
|
9
|
+
explanation: string;
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
export declare const trainingModules: TrainingModule[];
|
|
13
|
+
//# sourceMappingURL=modules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../../src/training/modules.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,EAAE,CAAC;CACL;AAED,eAAO,MAAM,eAAe,EAAE,cAAc,EAqmB3C,CAAC"}
|