nestjs-ddd-cli 2.2.0 → 3.2.1
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 +247 -408
- package/ddd.schema.json +111 -0
- package/dist/commands/aggregate-validator.d.ts +9 -0
- package/dist/commands/aggregate-validator.js +953 -0
- package/dist/commands/aggregate-validator.js.map +1 -0
- package/dist/commands/ai-assist.d.ts +8 -0
- package/dist/commands/ai-assist.js +337 -0
- package/dist/commands/ai-assist.js.map +1 -0
- package/dist/commands/api-contracts.d.ts +9 -0
- package/dist/commands/api-contracts.js +1368 -0
- package/dist/commands/api-contracts.js.map +1 -0
- package/dist/commands/api-docs.d.ts +8 -0
- package/dist/commands/api-docs.js +408 -0
- package/dist/commands/api-docs.js.map +1 -0
- package/dist/commands/api-versioning.d.ts +11 -0
- package/dist/commands/api-versioning.js +643 -0
- package/dist/commands/api-versioning.js.map +1 -0
- package/dist/commands/audit-logging.d.ts +9 -0
- package/dist/commands/audit-logging.js +1129 -0
- package/dist/commands/audit-logging.js.map +1 -0
- package/dist/commands/batch-generate.d.ts +10 -0
- package/dist/commands/batch-generate.js +405 -0
- package/dist/commands/batch-generate.js.map +1 -0
- package/dist/commands/caching-strategies.d.ts +9 -0
- package/dist/commands/caching-strategies.js +874 -0
- package/dist/commands/caching-strategies.js.map +1 -0
- package/dist/commands/code-analyzer.d.ts +42 -0
- package/dist/commands/code-analyzer.js +474 -0
- package/dist/commands/code-analyzer.js.map +1 -0
- package/dist/commands/database-seeding.d.ts +6 -0
- package/dist/commands/database-seeding.js +621 -0
- package/dist/commands/database-seeding.js.map +1 -0
- package/dist/commands/db-optimization.d.ts +7 -0
- package/dist/commands/db-optimization.js +687 -0
- package/dist/commands/db-optimization.js.map +1 -0
- package/dist/commands/dependency-graph.d.ts +6 -0
- package/dist/commands/dependency-graph.js +329 -0
- package/dist/commands/dependency-graph.js.map +1 -0
- package/dist/commands/doctor-enhanced.d.ts +22 -0
- package/dist/commands/doctor-enhanced.js +543 -0
- package/dist/commands/doctor-enhanced.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +151 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/env-manager.d.ts +6 -0
- package/dist/commands/env-manager.js +419 -0
- package/dist/commands/env-manager.js.map +1 -0
- package/dist/commands/event-sourcing-full.d.ts +10 -0
- package/dist/commands/event-sourcing-full.js +1107 -0
- package/dist/commands/event-sourcing-full.js.map +1 -0
- package/dist/commands/feature-flags.d.ts +9 -0
- package/dist/commands/feature-flags.js +824 -0
- package/dist/commands/feature-flags.js.map +1 -0
- package/dist/commands/filter-dsl.d.ts +10 -0
- package/dist/commands/filter-dsl.js +1407 -0
- package/dist/commands/filter-dsl.js.map +1 -0
- package/dist/commands/generate-all.js +485 -32
- package/dist/commands/generate-all.js.map +1 -1
- package/dist/commands/generate-deployment.d.ts +8 -0
- package/dist/commands/generate-deployment.js +746 -0
- package/dist/commands/generate-deployment.js.map +1 -0
- package/dist/commands/generate-domain-service.d.ts +14 -0
- package/dist/commands/generate-domain-service.js +796 -0
- package/dist/commands/generate-domain-service.js.map +1 -0
- package/dist/commands/generate-entity.js +82 -24
- package/dist/commands/generate-entity.js.map +1 -1
- package/dist/commands/generate-from-schema.d.ts +56 -0
- package/dist/commands/generate-from-schema.js +222 -0
- package/dist/commands/generate-from-schema.js.map +1 -0
- package/dist/commands/generate-orchestrator.d.ts +14 -0
- package/dist/commands/generate-orchestrator.js +887 -0
- package/dist/commands/generate-orchestrator.js.map +1 -0
- package/dist/commands/generate-repository.d.ts +14 -0
- package/dist/commands/generate-repository.js +1019 -0
- package/dist/commands/generate-repository.js.map +1 -0
- package/dist/commands/generate-shared.d.ts +4 -0
- package/dist/commands/generate-shared.js +388 -0
- package/dist/commands/generate-shared.js.map +1 -0
- package/dist/commands/generate-value-object.d.ts +32 -0
- package/dist/commands/generate-value-object.js +700 -0
- package/dist/commands/generate-value-object.js.map +1 -0
- package/dist/commands/graphql-subscriptions.d.ts +6 -0
- package/dist/commands/graphql-subscriptions.js +607 -0
- package/dist/commands/graphql-subscriptions.js.map +1 -0
- package/dist/commands/graphql-types.d.ts +5 -0
- package/dist/commands/graphql-types.js +423 -0
- package/dist/commands/graphql-types.js.map +1 -0
- package/dist/commands/health-probes-advanced.d.ts +6 -0
- package/dist/commands/health-probes-advanced.js +655 -0
- package/dist/commands/health-probes-advanced.js.map +1 -0
- package/dist/commands/i18n-setup.d.ts +10 -0
- package/dist/commands/i18n-setup.js +677 -0
- package/dist/commands/i18n-setup.js.map +1 -0
- package/dist/commands/init-config.d.ts +6 -0
- package/dist/commands/init-config.js +370 -0
- package/dist/commands/init-config.js.map +1 -0
- package/dist/commands/init-project.js +56 -6
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/interactive-scaffold.d.ts +5 -0
- package/dist/commands/interactive-scaffold.js +271 -0
- package/dist/commands/interactive-scaffold.js.map +1 -0
- package/dist/commands/metrics-prometheus.d.ts +6 -0
- package/dist/commands/metrics-prometheus.js +681 -0
- package/dist/commands/metrics-prometheus.js.map +1 -0
- package/dist/commands/migration-engine.d.ts +6 -0
- package/dist/commands/migration-engine.js +446 -0
- package/dist/commands/migration-engine.js.map +1 -0
- package/dist/commands/migration.d.ts +12 -0
- package/dist/commands/migration.js +484 -0
- package/dist/commands/migration.js.map +1 -0
- package/dist/commands/monorepo.d.ts +8 -0
- package/dist/commands/monorepo.js +483 -0
- package/dist/commands/monorepo.js.map +1 -0
- package/dist/commands/multi-database.d.ts +5 -0
- package/dist/commands/multi-database.js +439 -0
- package/dist/commands/multi-database.js.map +1 -0
- package/dist/commands/observability-tracing.d.ts +10 -0
- package/dist/commands/observability-tracing.js +740 -0
- package/dist/commands/observability-tracing.js.map +1 -0
- package/dist/commands/openapi-export.d.ts +8 -0
- package/dist/commands/openapi-export.js +359 -0
- package/dist/commands/openapi-export.js.map +1 -0
- package/dist/commands/perf-analyzer.d.ts +8 -0
- package/dist/commands/perf-analyzer.js +423 -0
- package/dist/commands/perf-analyzer.js.map +1 -0
- package/dist/commands/rate-limiting.d.ts +10 -0
- package/dist/commands/rate-limiting.js +953 -0
- package/dist/commands/rate-limiting.js.map +1 -0
- package/dist/commands/recipe-plugin.d.ts +56 -0
- package/dist/commands/recipe-plugin.js +315 -0
- package/dist/commands/recipe-plugin.js.map +1 -0
- package/dist/commands/recipe.d.ts +6 -0
- package/dist/commands/recipe.js +3941 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
- package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
- package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
- package/dist/commands/recipes/index.d.ts +7 -0
- package/dist/commands/recipes/index.js +24 -0
- package/dist/commands/recipes/index.js.map +1 -0
- package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
- package/dist/commands/recipes/message-queue.recipe.js +706 -0
- package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
- package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
- package/dist/commands/recipes/middleware.recipe.js +383 -0
- package/dist/commands/recipes/middleware.recipe.js.map +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2.recipe.js +472 -0
- package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
- package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
- package/dist/commands/recipes/websocket.recipe.js +453 -0
- package/dist/commands/recipes/websocket.recipe.js.map +1 -0
- package/dist/commands/resilience-patterns.d.ts +13 -0
- package/dist/commands/resilience-patterns.js +1029 -0
- package/dist/commands/resilience-patterns.js.map +1 -0
- package/dist/commands/security-patterns.d.ts +11 -0
- package/dist/commands/security-patterns.js +2233 -0
- package/dist/commands/security-patterns.js.map +1 -0
- package/dist/commands/template-debug.d.ts +27 -0
- package/dist/commands/template-debug.js +388 -0
- package/dist/commands/template-debug.js.map +1 -0
- package/dist/commands/test-factory-full.d.ts +9 -0
- package/dist/commands/test-factory-full.js +1570 -0
- package/dist/commands/test-factory-full.js.map +1 -0
- package/dist/commands/test-scaffold.d.ts +7 -0
- package/dist/commands/test-scaffold.js +621 -0
- package/dist/commands/test-scaffold.js.map +1 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -1
- package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
- package/dist/templates/ai-context/conventions.md.hbs +154 -0
- package/dist/templates/command/create-command.hbs +6 -14
- package/dist/templates/command/delete-command.hbs +19 -0
- package/dist/templates/command/update-command.hbs +24 -0
- package/dist/templates/controller/controller.hbs +64 -17
- package/dist/templates/dto/create-dto.hbs +29 -5
- package/dist/templates/dto/filter-dto.hbs +52 -0
- package/dist/templates/dto/filter-query.dto.hbs +148 -0
- package/dist/templates/dto/paginated-response.dto.hbs +29 -0
- package/dist/templates/dto/pagination-query.dto.hbs +30 -0
- package/dist/templates/dto/response-dto.hbs +38 -0
- package/dist/templates/dto/update-dto.hbs +11 -0
- package/dist/templates/entity/entity.hbs +32 -1
- package/dist/templates/event/domain-event.hbs +33 -7
- package/dist/templates/event/event-handler.hbs +40 -0
- package/dist/templates/exception/base-exceptions.hbs +69 -0
- package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
- package/dist/templates/mapper/mapper.hbs +49 -24
- package/dist/templates/module/module.hbs +34 -10
- package/dist/templates/orm-entity/orm-entity.hbs +63 -12
- package/dist/templates/prisma/prisma-mapper.hbs +71 -0
- package/dist/templates/prisma/prisma-repository.hbs +114 -0
- package/dist/templates/prisma/prisma-schema.hbs +20 -0
- package/dist/templates/prisma/prisma-service.hbs +51 -0
- package/dist/templates/query/get-all.query.hbs +50 -0
- package/dist/templates/query/get-by-id.query.hbs +31 -0
- package/dist/templates/repository/repository.hbs +55 -13
- package/dist/templates/resolver/graphql-input.hbs +54 -0
- package/dist/templates/resolver/graphql-type.hbs +58 -0
- package/dist/templates/resolver/pagination-args.hbs +33 -0
- package/dist/templates/resolver/resolver.hbs +62 -0
- package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
- package/dist/templates/shared/query-builder.util.hbs +218 -0
- package/dist/templates/test/controller.spec.hbs +124 -0
- package/dist/templates/test/repository.spec.hbs +158 -0
- package/dist/templates/test/usecase.spec.hbs +116 -0
- package/dist/templates/usecase/create-usecase.hbs +19 -7
- package/dist/templates/usecase/delete-usecase.hbs +17 -0
- package/dist/templates/usecase/update-usecase.hbs +31 -0
- package/dist/utils/config.utils.d.ts +45 -0
- package/dist/utils/config.utils.js +211 -0
- package/dist/utils/config.utils.js.map +1 -0
- package/dist/utils/error.utils.d.ts +145 -0
- package/dist/utils/error.utils.js +422 -0
- package/dist/utils/error.utils.js.map +1 -0
- package/dist/utils/field.utils.d.ts +54 -0
- package/dist/utils/field.utils.js +389 -0
- package/dist/utils/field.utils.js.map +1 -0
- package/dist/utils/file.utils.d.ts +19 -8
- package/dist/utils/file.utils.js +135 -4
- package/dist/utils/file.utils.js.map +1 -1
- package/dist/utils/idempotency.utils.d.ts +123 -0
- package/dist/utils/idempotency.utils.js +444 -0
- package/dist/utils/idempotency.utils.js.map +1 -0
- package/dist/utils/naming.utils.js +26 -3
- package/dist/utils/naming.utils.js.map +1 -1
- package/dist/utils/performance.utils.d.ts +37 -0
- package/dist/utils/performance.utils.js +158 -0
- package/dist/utils/performance.utils.js.map +1 -0
- package/dist/utils/relation.utils.d.ts +92 -0
- package/dist/utils/relation.utils.js +388 -0
- package/dist/utils/relation.utils.js.map +1 -0
- package/dist/utils/rollback.utils.d.ts +49 -0
- package/dist/utils/rollback.utils.js +306 -0
- package/dist/utils/rollback.utils.js.map +1 -0
- package/dist/utils/schema.utils.d.ts +123 -0
- package/dist/utils/schema.utils.js +419 -0
- package/dist/utils/schema.utils.js.map +1 -0
- package/dist/utils/security.utils.d.ts +57 -0
- package/dist/utils/security.utils.js +315 -0
- package/dist/utils/security.utils.js.map +1 -0
- package/dist/utils/template-engine.utils.d.ts +80 -0
- package/dist/utils/template-engine.utils.js +463 -0
- package/dist/utils/template-engine.utils.js.map +1 -0
- package/dist/utils/validation-registry.utils.d.ts +160 -0
- package/dist/utils/validation-registry.utils.js +526 -0
- package/dist/utils/validation-registry.utils.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security utilities for the CLI
|
|
4
|
+
* Implements OWASP-recommended practices for input validation and path safety
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.validateSafePath = validateSafePath;
|
|
41
|
+
exports.sanitizeEntityName = sanitizeEntityName;
|
|
42
|
+
exports.validateFieldName = validateFieldName;
|
|
43
|
+
exports.sanitizeShellInput = sanitizeShellInput;
|
|
44
|
+
exports.validateNumericInput = validateNumericInput;
|
|
45
|
+
exports.sanitizeHtmlOutput = sanitizeHtmlOutput;
|
|
46
|
+
exports.maskSensitiveData = maskSensitiveData;
|
|
47
|
+
exports.validateUrl = validateUrl;
|
|
48
|
+
exports.createRateLimitKey = createRateLimitKey;
|
|
49
|
+
exports.safeJsonParse = safeJsonParse;
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
/**
|
|
52
|
+
* Validates that a path does not escape the base directory
|
|
53
|
+
* Prevents path traversal attacks (OWASP A01:2021)
|
|
54
|
+
*/
|
|
55
|
+
function validateSafePath(basePath, targetPath) {
|
|
56
|
+
const resolvedBase = path.resolve(basePath);
|
|
57
|
+
const resolvedTarget = path.resolve(basePath, targetPath);
|
|
58
|
+
if (!resolvedTarget.startsWith(resolvedBase + path.sep) && resolvedTarget !== resolvedBase) {
|
|
59
|
+
throw new Error(`Security: Path traversal detected in "${targetPath}"`);
|
|
60
|
+
}
|
|
61
|
+
return resolvedTarget;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Sanitizes entity/module names for safe file system operations
|
|
65
|
+
* Prevents directory traversal and special character injection
|
|
66
|
+
*/
|
|
67
|
+
function sanitizeEntityName(name) {
|
|
68
|
+
if (!name || typeof name !== 'string') {
|
|
69
|
+
throw new Error('Security: Entity name must be a non-empty string');
|
|
70
|
+
}
|
|
71
|
+
// Remove any path separators, dots (for traversal), and special characters
|
|
72
|
+
const sanitized = name
|
|
73
|
+
.replace(/\.{2,}/g, '') // Remove consecutive dots (path traversal)
|
|
74
|
+
.replace(/[\/\\]/g, '') // Remove path separators
|
|
75
|
+
.replace(/[<>:"|?*\x00-\x1f]/g, '') // Remove invalid filename chars
|
|
76
|
+
.replace(/^[\s.-]+|[\s.-]+$/g, '') // Trim leading/trailing dots, spaces, hyphens
|
|
77
|
+
.trim();
|
|
78
|
+
if (sanitized.length === 0) {
|
|
79
|
+
throw new Error(`Security: Invalid entity name after sanitization: "${name}"`);
|
|
80
|
+
}
|
|
81
|
+
if (sanitized.length > 100) {
|
|
82
|
+
throw new Error(`Security: Entity name exceeds maximum length (100): "${name}"`);
|
|
83
|
+
}
|
|
84
|
+
// Block common injection patterns
|
|
85
|
+
const dangerousPatterns = [
|
|
86
|
+
/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i, // Windows reserved names
|
|
87
|
+
/[\x00-\x1f]/g, // Control characters
|
|
88
|
+
/__proto__|constructor|prototype/i, // Prototype pollution
|
|
89
|
+
];
|
|
90
|
+
for (const pattern of dangerousPatterns) {
|
|
91
|
+
if (pattern.test(sanitized)) {
|
|
92
|
+
throw new Error(`Security: Entity name contains dangerous pattern: "${name}"`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return sanitized;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Validates that field names are safe for use in SQL/ORM queries
|
|
99
|
+
* Only allows alphanumeric characters and underscores, starting with letter
|
|
100
|
+
*/
|
|
101
|
+
function validateFieldName(fieldName, allowedFields) {
|
|
102
|
+
if (!fieldName || typeof fieldName !== 'string') {
|
|
103
|
+
throw new Error('Security: Field name must be a non-empty string');
|
|
104
|
+
}
|
|
105
|
+
// If allowed fields list provided, strict whitelist validation
|
|
106
|
+
if (allowedFields && allowedFields.length > 0) {
|
|
107
|
+
if (!allowedFields.includes(fieldName)) {
|
|
108
|
+
throw new Error(`Security: Field "${fieldName}" not in allowed list: ${allowedFields.join(', ')}`);
|
|
109
|
+
}
|
|
110
|
+
return fieldName;
|
|
111
|
+
}
|
|
112
|
+
// Pattern: must start with letter or underscore, followed by alphanumeric/underscore
|
|
113
|
+
const SAFE_FIELD_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
114
|
+
if (!SAFE_FIELD_REGEX.test(fieldName)) {
|
|
115
|
+
throw new Error(`Security: Invalid field name format: "${fieldName}"`);
|
|
116
|
+
}
|
|
117
|
+
if (fieldName.length > 64) {
|
|
118
|
+
throw new Error(`Security: Field name exceeds maximum length (64): "${fieldName}"`);
|
|
119
|
+
}
|
|
120
|
+
// Block SQL keywords as field names
|
|
121
|
+
const SQL_KEYWORDS = [
|
|
122
|
+
'SELECT',
|
|
123
|
+
'INSERT',
|
|
124
|
+
'UPDATE',
|
|
125
|
+
'DELETE',
|
|
126
|
+
'DROP',
|
|
127
|
+
'CREATE',
|
|
128
|
+
'ALTER',
|
|
129
|
+
'TRUNCATE',
|
|
130
|
+
'GRANT',
|
|
131
|
+
'REVOKE',
|
|
132
|
+
'UNION',
|
|
133
|
+
'WHERE',
|
|
134
|
+
'FROM',
|
|
135
|
+
'JOIN',
|
|
136
|
+
'OR',
|
|
137
|
+
'AND',
|
|
138
|
+
'NOT',
|
|
139
|
+
'NULL',
|
|
140
|
+
'TRUE',
|
|
141
|
+
'FALSE',
|
|
142
|
+
'EXEC',
|
|
143
|
+
'EXECUTE',
|
|
144
|
+
'XP_',
|
|
145
|
+
'SP_',
|
|
146
|
+
];
|
|
147
|
+
if (SQL_KEYWORDS.includes(fieldName.toUpperCase())) {
|
|
148
|
+
throw new Error(`Security: Field name "${fieldName}" is a reserved SQL keyword`);
|
|
149
|
+
}
|
|
150
|
+
return fieldName;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Sanitizes input for use in shell commands
|
|
154
|
+
* Implements whitelist approach - only allows safe characters
|
|
155
|
+
*/
|
|
156
|
+
function sanitizeShellInput(input) {
|
|
157
|
+
if (!input || typeof input !== 'string') {
|
|
158
|
+
throw new Error('Security: Shell input must be a non-empty string');
|
|
159
|
+
}
|
|
160
|
+
// Strict whitelist: only alphanumeric, hyphen, underscore, dot, forward slash
|
|
161
|
+
// (for paths within allowed directories)
|
|
162
|
+
const SAFE_SHELL_REGEX = /^[a-zA-Z0-9_\-./]+$/;
|
|
163
|
+
if (!SAFE_SHELL_REGEX.test(input)) {
|
|
164
|
+
// Instead of throwing, sanitize by removing unsafe characters
|
|
165
|
+
const sanitized = input.replace(/[^a-zA-Z0-9_\-./]/g, '');
|
|
166
|
+
if (sanitized.length === 0) {
|
|
167
|
+
throw new Error('Security: Shell input contains only disallowed characters');
|
|
168
|
+
}
|
|
169
|
+
return sanitized;
|
|
170
|
+
}
|
|
171
|
+
// Block command injection patterns
|
|
172
|
+
if (/\.\./g.test(input)) {
|
|
173
|
+
throw new Error('Security: Shell input contains path traversal');
|
|
174
|
+
}
|
|
175
|
+
return input;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Validates numeric input is within safe bounds
|
|
179
|
+
*/
|
|
180
|
+
function validateNumericInput(value, options = {}) {
|
|
181
|
+
const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER, allowNegative = true, allowZero = true } = options;
|
|
182
|
+
if (typeof value !== 'number' || isNaN(value) || !isFinite(value)) {
|
|
183
|
+
throw new Error(`Security: Invalid numeric value: ${value}`);
|
|
184
|
+
}
|
|
185
|
+
if (!allowNegative && value < 0) {
|
|
186
|
+
throw new Error(`Security: Negative values not allowed: ${value}`);
|
|
187
|
+
}
|
|
188
|
+
if (!allowZero && value === 0) {
|
|
189
|
+
throw new Error('Security: Zero value not allowed');
|
|
190
|
+
}
|
|
191
|
+
if (value < min || value > max) {
|
|
192
|
+
throw new Error(`Security: Value ${value} out of range [${min}, ${max}]`);
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Sanitizes string for safe HTML output
|
|
198
|
+
* Prevents XSS attacks (OWASP A03:2021)
|
|
199
|
+
*/
|
|
200
|
+
function sanitizeHtmlOutput(input) {
|
|
201
|
+
if (!input || typeof input !== 'string') {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
const htmlEntities = {
|
|
205
|
+
'&': '&',
|
|
206
|
+
'<': '<',
|
|
207
|
+
'>': '>',
|
|
208
|
+
'"': '"',
|
|
209
|
+
"'": ''',
|
|
210
|
+
'/': '/',
|
|
211
|
+
'`': '`',
|
|
212
|
+
'=': '=',
|
|
213
|
+
};
|
|
214
|
+
return input.replace(/[&<>"'/`=]/g, (char) => htmlEntities[char] || char);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Masks sensitive data in strings (API keys, passwords, etc.)
|
|
218
|
+
* For safe logging
|
|
219
|
+
*/
|
|
220
|
+
function maskSensitiveData(input) {
|
|
221
|
+
if (!input || typeof input !== 'string') {
|
|
222
|
+
return '[EMPTY]';
|
|
223
|
+
}
|
|
224
|
+
if (input.length <= 8) {
|
|
225
|
+
return '****';
|
|
226
|
+
}
|
|
227
|
+
// Show first 4 and last 4 characters
|
|
228
|
+
return `${input.substring(0, 4)}${'*'.repeat(Math.min(input.length - 8, 20))}${input.substring(input.length - 4)}`;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Validates URL is safe and uses allowed protocols
|
|
232
|
+
*/
|
|
233
|
+
function validateUrl(url, allowedProtocols = ['https:', 'http:']) {
|
|
234
|
+
if (!url || typeof url !== 'string') {
|
|
235
|
+
throw new Error('Security: URL must be a non-empty string');
|
|
236
|
+
}
|
|
237
|
+
let parsed;
|
|
238
|
+
try {
|
|
239
|
+
parsed = new URL(url);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
throw new Error(`Security: Invalid URL format: ${url}`);
|
|
243
|
+
}
|
|
244
|
+
if (!allowedProtocols.includes(parsed.protocol)) {
|
|
245
|
+
throw new Error(`Security: URL protocol "${parsed.protocol}" not allowed. Allowed: ${allowedProtocols.join(', ')}`);
|
|
246
|
+
}
|
|
247
|
+
// Block localhost and internal IPs in production
|
|
248
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
249
|
+
const blockedHostPatterns = [
|
|
250
|
+
/^localhost$/i,
|
|
251
|
+
/^127\./,
|
|
252
|
+
/^10\./,
|
|
253
|
+
/^172\.(1[6-9]|2[0-9]|3[01])\./,
|
|
254
|
+
/^192\.168\./,
|
|
255
|
+
/^0\.0\.0\.0$/,
|
|
256
|
+
/^\[::1\]$/,
|
|
257
|
+
/^metadata\.google\.internal$/,
|
|
258
|
+
/^169\.254\./,
|
|
259
|
+
];
|
|
260
|
+
if (process.env.NODE_ENV === 'production') {
|
|
261
|
+
for (const pattern of blockedHostPatterns) {
|
|
262
|
+
if (pattern.test(hostname)) {
|
|
263
|
+
throw new Error(`Security: URL hostname "${hostname}" blocked in production`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return url;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Creates a rate limit key from request data
|
|
271
|
+
* Sanitizes to prevent key injection
|
|
272
|
+
*/
|
|
273
|
+
function createRateLimitKey(prefix, identifier) {
|
|
274
|
+
const sanitizedPrefix = prefix.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
275
|
+
const sanitizedIdentifier = identifier.replace(/[^a-zA-Z0-9_\-@.]/g, '').substring(0, 128);
|
|
276
|
+
return `${sanitizedPrefix}:${sanitizedIdentifier}`;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Validates JSON input for safe parsing
|
|
280
|
+
* Prevents prototype pollution
|
|
281
|
+
*/
|
|
282
|
+
function safeJsonParse(input) {
|
|
283
|
+
if (!input || typeof input !== 'string') {
|
|
284
|
+
throw new Error('Security: JSON input must be a non-empty string');
|
|
285
|
+
}
|
|
286
|
+
// Check for potential prototype pollution patterns before parsing
|
|
287
|
+
if (/__proto__|constructor|prototype/.test(input)) {
|
|
288
|
+
throw new Error('Security: JSON contains prototype pollution attempt');
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const parsed = JSON.parse(input);
|
|
292
|
+
// Deep check for prototype pollution
|
|
293
|
+
function checkObject(obj, depth = 0) {
|
|
294
|
+
if (depth > 10)
|
|
295
|
+
return; // Limit recursion depth
|
|
296
|
+
if (obj && typeof obj === 'object') {
|
|
297
|
+
for (const key of Object.keys(obj)) {
|
|
298
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
299
|
+
throw new Error('Security: JSON object contains prototype pollution key');
|
|
300
|
+
}
|
|
301
|
+
checkObject(obj[key], depth + 1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
checkObject(parsed);
|
|
306
|
+
return parsed;
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
if (error instanceof Error && error.message.includes('Security:')) {
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
throw new Error(`Security: Invalid JSON: ${error.message}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=security.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.utils.js","sourceRoot":"","sources":["../../src/utils/security.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config-Driven Template System
|
|
3
|
+
* Provides template inheritance, hooks, and per-module overrides
|
|
4
|
+
*/
|
|
5
|
+
import Handlebars from 'handlebars';
|
|
6
|
+
export interface TemplateConfig {
|
|
7
|
+
extends?: string;
|
|
8
|
+
overrides?: Record<string, string>;
|
|
9
|
+
hooks?: {
|
|
10
|
+
preGenerate?: string;
|
|
11
|
+
postGenerate?: string;
|
|
12
|
+
preCompile?: string;
|
|
13
|
+
postCompile?: string;
|
|
14
|
+
};
|
|
15
|
+
helpers?: Record<string, string>;
|
|
16
|
+
partials?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export interface GenerationContext {
|
|
19
|
+
entityName: string;
|
|
20
|
+
moduleName: string;
|
|
21
|
+
fields: any[];
|
|
22
|
+
relations?: any[];
|
|
23
|
+
config: Record<string, any>;
|
|
24
|
+
options: Record<string, any>;
|
|
25
|
+
meta: {
|
|
26
|
+
timestamp: Date;
|
|
27
|
+
version: string;
|
|
28
|
+
generator: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface GenerationHook {
|
|
32
|
+
(context: GenerationContext): Promise<GenerationContext> | GenerationContext;
|
|
33
|
+
}
|
|
34
|
+
export interface TemplateRegistry {
|
|
35
|
+
templates: Map<string, CompiledTemplate>;
|
|
36
|
+
partials: Map<string, Handlebars.TemplateDelegate>;
|
|
37
|
+
helpers: Map<string, Handlebars.HelperDelegate>;
|
|
38
|
+
hooks: {
|
|
39
|
+
preGenerate: GenerationHook[];
|
|
40
|
+
postGenerate: GenerationHook[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export interface CompiledTemplate {
|
|
44
|
+
name: string;
|
|
45
|
+
source: string;
|
|
46
|
+
compiled: Handlebars.TemplateDelegate;
|
|
47
|
+
config?: TemplateConfig;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a new template registry
|
|
51
|
+
*/
|
|
52
|
+
export declare function createTemplateRegistry(): TemplateRegistry;
|
|
53
|
+
/**
|
|
54
|
+
* Load template from file with optional config
|
|
55
|
+
*/
|
|
56
|
+
export declare function loadTemplate(registry: TemplateRegistry, templatePath: string, name?: string): CompiledTemplate;
|
|
57
|
+
/**
|
|
58
|
+
* Load all templates from a directory
|
|
59
|
+
*/
|
|
60
|
+
export declare function loadTemplatesFromDirectory(registry: TemplateRegistry, dirPath: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Register a partial template
|
|
63
|
+
*/
|
|
64
|
+
export declare function registerPartial(registry: TemplateRegistry, name: string, source: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Register a custom helper
|
|
67
|
+
*/
|
|
68
|
+
export declare function registerHelper(registry: TemplateRegistry, name: string, helper: Handlebars.HelperDelegate): void;
|
|
69
|
+
/**
|
|
70
|
+
* Register a generation hook
|
|
71
|
+
*/
|
|
72
|
+
export declare function registerHook(registry: TemplateRegistry, phase: 'preGenerate' | 'postGenerate', hook: GenerationHook): void;
|
|
73
|
+
/**
|
|
74
|
+
* Render a template with context
|
|
75
|
+
*/
|
|
76
|
+
export declare function renderTemplate(registry: TemplateRegistry, templateName: string, context: GenerationContext): Promise<string>;
|
|
77
|
+
/**
|
|
78
|
+
* Create per-module template overrides
|
|
79
|
+
*/
|
|
80
|
+
export declare function createModuleTemplateConfig(moduleName: string, overrides: Record<string, string>): TemplateConfig;
|