@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39

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 (149) hide show
  1. package/README.md +662 -658
  2. package/package.json +10 -3
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/index.js +26 -0
  100. package/src/services/lambda/server.js +12 -1
  101. package/src/services/lambda/simulator.js +4 -0
  102. package/src/services/opensearch/index.js +105 -0
  103. package/src/services/opensearch/models/document.js +115 -0
  104. package/src/services/opensearch/models/index.js +73 -0
  105. package/src/services/opensearch/server.js +310 -0
  106. package/src/services/opensearch/simulator.js +605 -0
  107. package/src/services/opensearch/store.js +162 -0
  108. package/src/services/organizations/control-tower-simulator.js +321 -0
  109. package/src/services/organizations/control-tower-store.js +182 -0
  110. package/src/services/organizations/iam-integration.js +413 -0
  111. package/src/services/organizations/index.js +172 -0
  112. package/src/services/organizations/models/index.js +13 -0
  113. package/src/services/organizations/models/invitation.js +48 -0
  114. package/src/services/organizations/models/organization.js +566 -0
  115. package/src/services/organizations/models/role.js +244 -0
  116. package/src/services/organizations/models/scp.js +131 -0
  117. package/src/services/organizations/server.js +736 -0
  118. package/src/services/organizations/simulator.js +593 -0
  119. package/src/services/organizations/store.js +235 -0
  120. package/src/services/organizations/vpc-integration.js +453 -0
  121. package/src/services/route53/index.js +67 -0
  122. package/src/services/route53/models/healthCheck.js +101 -0
  123. package/src/services/route53/models/hostedZone.js +37 -0
  124. package/src/services/route53/models/resourceRecord.js +106 -0
  125. package/src/services/route53/server.js +841 -0
  126. package/src/services/route53/simulator.js +350 -0
  127. package/src/services/s3/index.js +62 -0
  128. package/src/services/s3/presigned-url-generator.js +337 -0
  129. package/src/services/s3/server.js +231 -0
  130. package/src/services/s3/simulator.js +551 -4
  131. package/src/services/sns/index.js +31 -0
  132. package/src/services/sns/server.js +9 -0
  133. package/src/services/sqs/index.js +38 -0
  134. package/src/services/sqs/server.js +57 -4
  135. package/src/services/sqs/simulator.js +423 -33
  136. package/src/services/sts/simulator.js +209 -17
  137. package/src/services/vpc/README.md +459 -0
  138. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  139. package/src/services/vpc/index.js +90 -0
  140. package/src/services/vpc/models/internetGateway.js +71 -0
  141. package/src/services/vpc/models/routeTable.js +160 -0
  142. package/src/services/vpc/models/subnet.js +48 -0
  143. package/src/services/vpc/models/vpc.js +37 -0
  144. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  145. package/src/services/vpc/models/vpcGateway.js +134 -0
  146. package/src/services/vpc/server.js +836 -0
  147. package/src/services/vpc/simulator.js +613 -0
  148. package/src/utils/policy-cache.js +215 -0
  149. package/aws-config +0 -154
@@ -0,0 +1,425 @@
1
+ const { Distribution } = require('./models');
2
+ const CacheEngine = require('./cache-engine');
3
+ const SignedUrlGenerator = require('./signed-url-generator');
4
+ const OriginValidator = require('./origin-validator');
5
+ const GeoRestrictions = require('./geo-restrictions');
6
+ const OriginRouter = require('./origin-router');
7
+
8
+ /**
9
+ * CloudFront Simulator
10
+ * Simulates CloudFront distribution behavior including caching, routing, and content delivery
11
+ */
12
+ class CloudFrontSimulator {
13
+ constructor(options = {}) {
14
+ this.distributions = new Map();
15
+ this.cacheEngine = new CacheEngine(options.cacheOptions);
16
+ this.signedUrlGenerator = new SignedUrlGenerator();
17
+ this.originValidator = new OriginValidator();
18
+ this.geoRestrictions = new GeoRestrictions();
19
+ this.originRouter = new OriginRouter(options.servicesRegistry || {});
20
+ this.requestStats = {
21
+ totalRequests: 0,
22
+ cacheHits: 0,
23
+ cacheMisses: 0,
24
+ blockedRequests: 0
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Register services for origin routing
30
+ * @param {string} serviceName - Service name (s3, apigateway, ec2, lambda, etc)
31
+ * @param {object} serviceInstance - Service instance
32
+ */
33
+ registerService(serviceName, serviceInstance) {
34
+ this.originRouter.registerService(serviceName, serviceInstance);
35
+ }
36
+ createDistribution(distributionConfig) {
37
+ const distribution = new Distribution(distributionConfig);
38
+
39
+ // Validate all origins
40
+ for (const origin of distribution.origins) {
41
+ const validation = this.originValidator.validateOrigin(origin);
42
+ if (!validation.isValid) {
43
+ throw new Error(
44
+ `Invalid origin '${origin.id}': ${validation.error}`
45
+ );
46
+ }
47
+ }
48
+
49
+ // Validate geo-restrictions if present
50
+ if (distribution.restrictions?.geoRestriction) {
51
+ const geoValidation = this.geoRestrictions.validateGeoRestriction(
52
+ distribution.restrictions.geoRestriction
53
+ );
54
+ if (!geoValidation.isValid) {
55
+ throw new Error(
56
+ `Invalid geo-restriction: ${geoValidation.errors.join(', ')}`
57
+ );
58
+ }
59
+ }
60
+
61
+ this.distributions.set(distribution.id, distribution);
62
+ return distribution;
63
+ }
64
+
65
+ /**
66
+ * Get a distribution by ID
67
+ * @param {string} distributionId - Distribution ID
68
+ * @returns {object|null} Distribution or null
69
+ */
70
+ getDistribution(distributionId) {
71
+ return this.distributions.get(distributionId) || null;
72
+ }
73
+
74
+ /**
75
+ * List all distributions
76
+ * @returns {object[]} Array of distributions
77
+ */
78
+ listDistributions() {
79
+ return Array.from(this.distributions.values());
80
+ }
81
+
82
+ /**
83
+ * Delete a distribution
84
+ * @param {string} distributionId - Distribution ID
85
+ * @returns {boolean} True if deleted
86
+ */
87
+ deleteDistribution(distributionId) {
88
+ // Invalidate all cached content for this distribution
89
+ this.cacheEngine.invalidateDistribution(distributionId);
90
+ return this.distributions.delete(distributionId);
91
+ }
92
+
93
+ /**
94
+ * Process a request through CloudFront
95
+ * Handles routing, caching, geo-restrictions, signed URLs, etc.
96
+ * @param {object} request - Request object
97
+ * @param {string} request.distributionId - Distribution to route through
98
+ * @param {string} request.path - Request path
99
+ * @param {string} [request.method] - HTTP method (default: GET)
100
+ * @param {object} [request.headers] - Request headers
101
+ * @param {string} [request.clientIp] - Client IP for geo-restrictions
102
+ * @param {string} [request.protocol] - Request protocol (http/https)
103
+ * @returns {object} Response object with caching info
104
+ */
105
+ processRequest(request) {
106
+ this.requestStats.totalRequests++;
107
+
108
+ const distribution = this.getDistribution(request.distributionId);
109
+ if (!distribution) {
110
+ return {
111
+ status: 404,
112
+ error: 'Distribution not found',
113
+ cached: false
114
+ };
115
+ }
116
+
117
+ // Check if distribution is enabled
118
+ if (!distribution.enabled) {
119
+ return {
120
+ status: 403,
121
+ error: 'Distribution is disabled',
122
+ cached: false
123
+ };
124
+ }
125
+
126
+ // Check geo-restrictions
127
+ if (distribution.restrictions?.geoRestriction && request.clientIp) {
128
+ const clientCountry = this._getCountryFromIp(request.clientIp);
129
+ const geoCheck = this.geoRestrictions.checkGeoRestriction(
130
+ distribution.restrictions.geoRestriction,
131
+ clientCountry,
132
+ { ip: request.clientIp, distributionId: request.distributionId }
133
+ );
134
+
135
+ if (!geoCheck.isAllowed) {
136
+ this.requestStats.blockedRequests++;
137
+ return {
138
+ status: 403,
139
+ error: 'Access denied by geo-restriction',
140
+ reason: geoCheck.reason,
141
+ cached: false
142
+ };
143
+ }
144
+ }
145
+
146
+ // Check signed URL if required
147
+ if (distribution.defaultBehavior?.requireSignedUrls) {
148
+ const urlValidation = this._validateSignedUrl(request, distribution);
149
+ if (!urlValidation.isValid) {
150
+ this.requestStats.blockedRequests++;
151
+ return {
152
+ status: 403,
153
+ error: 'Invalid or missing signed URL',
154
+ reason: urlValidation.reason,
155
+ cached: false
156
+ };
157
+ }
158
+ }
159
+
160
+ // Find matching behavior
161
+ const behavior = distribution.matchBehavior(request.path);
162
+ if (!behavior) {
163
+ return {
164
+ status: 404,
165
+ error: 'No matching behavior found',
166
+ cached: false
167
+ };
168
+ }
169
+
170
+ // Check cache
171
+ const cacheKey = this._generateCacheKey(request.distributionId, request.path);
172
+ let cachedResponse = this.cacheEngine.get(cacheKey);
173
+
174
+ if (cachedResponse) {
175
+ this.requestStats.cacheHits++;
176
+ return {
177
+ ...cachedResponse,
178
+ cached: true,
179
+ cacheStatus: 'HIT',
180
+ xAmzCfId: this._generateCfId()
181
+ };
182
+ }
183
+
184
+ this.requestStats.cacheMisses++;
185
+
186
+ // Get origin
187
+ const origin = distribution.origins.find(o => o.id === behavior.targetOriginId);
188
+ if (!origin) {
189
+ return {
190
+ status: 502,
191
+ error: 'Origin not found',
192
+ cached: false
193
+ };
194
+ }
195
+
196
+ // Request to origin (may be async)
197
+ const originResponse = this._simulateOriginRequest(origin, request, behavior);
198
+
199
+ // Handle promise (async origin routing)
200
+ if (originResponse && typeof originResponse.then === 'function') {
201
+ return originResponse.then(response => {
202
+ // Cache response if cacheable
203
+ if (behavior.isCacheable(request.method || 'GET')) {
204
+ const ttl = behavior.getCachePolicy()?.defaultTtl || 3600;
205
+ this.cacheEngine.set(cacheKey, response, ttl, request.distributionId);
206
+ }
207
+
208
+ return {
209
+ ...response,
210
+ cached: false,
211
+ cacheStatus: 'MISS',
212
+ xAmzCfId: this._generateCfId()
213
+ };
214
+ }).catch(error => ({
215
+ status: 502,
216
+ error: 'Origin request failed',
217
+ message: error.message,
218
+ cached: false
219
+ }));
220
+ }
221
+
222
+ // Synchronous response
223
+ // Cache response if cacheable
224
+ if (behavior.isCacheable(request.method || 'GET')) {
225
+ const ttl = behavior.getCachePolicy()?.defaultTtl || 3600;
226
+ this.cacheEngine.set(cacheKey, originResponse, ttl, request.distributionId);
227
+ }
228
+
229
+ return {
230
+ ...originResponse,
231
+ cached: false,
232
+ cacheStatus: 'MISS',
233
+ xAmzCfId: this._generateCfId()
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Invalidate distribution content
239
+ * @param {string} distributionId - Distribution ID
240
+ * @param {string|string[]} paths - Paths to invalidate
241
+ * @returns {object} Invalidation result
242
+ */
243
+ invalidateDistribution(distributionId, paths) {
244
+ const distribution = this.getDistribution(distributionId);
245
+ if (!distribution) {
246
+ return { error: 'Distribution not found' };
247
+ }
248
+
249
+ const pathsArray = Array.isArray(paths) ? paths : [paths];
250
+ let invalidatedCount = 0;
251
+
252
+ for (const path of pathsArray) {
253
+ const cacheKey = this._generateCacheKey(distributionId, path);
254
+ this.cacheEngine.invalidate(cacheKey);
255
+ invalidatedCount++;
256
+ }
257
+
258
+ return {
259
+ invalidationId: this._generateInvalidationId(),
260
+ distributionId: distributionId,
261
+ paths: pathsArray,
262
+ invalidatedCount: invalidatedCount,
263
+ status: 'InProgress'
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Get distribution statistics
269
+ * @param {string} distributionId - Distribution ID
270
+ * @returns {object} Distribution stats
271
+ */
272
+ getDistributionStats(distributionId) {
273
+ const distribution = this.getDistribution(distributionId);
274
+ if (!distribution) {
275
+ return null;
276
+ }
277
+
278
+ const cacheStats = this.cacheEngine.getStats();
279
+
280
+ return {
281
+ distributionId: distributionId,
282
+ enabled: distribution.enabled,
283
+ domainName: distribution.domainName,
284
+ caching: {
285
+ entries: cacheStats.entries,
286
+ maxSize: cacheStats.maxSize,
287
+ usedSize: cacheStats.usedSize,
288
+ hitRate: this.requestStats.totalRequests > 0
289
+ ? (this.requestStats.cacheHits / this.requestStats.totalRequests * 100).toFixed(2) + '%'
290
+ : '0%'
291
+ },
292
+ global: this.requestStats
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Generate a signed URL
298
+ * @param {string} distributionId - Distribution ID
299
+ * @param {string} resourcePath - Resource path
300
+ * @param {string} privateKey - Private key for signing
301
+ * @param {string} keyPairId - Key pair ID
302
+ * @param {number} expirationTime - Expiration time (seconds from now)
303
+ * @returns {string} Signed URL
304
+ */
305
+ generateSignedUrl(
306
+ distributionId,
307
+ resourcePath,
308
+ privateKey,
309
+ keyPairId,
310
+ expirationTime
311
+ ) {
312
+ const distribution = this.getDistribution(distributionId);
313
+ if (!distribution) {
314
+ throw new Error('Distribution not found');
315
+ }
316
+
317
+ const expirationDate = new Date(Date.now() + expirationTime * 1000);
318
+ return this.signedUrlGenerator.generateCannedUrl(
319
+ distribution.domainName,
320
+ resourcePath,
321
+ privateKey,
322
+ keyPairId,
323
+ expirationDate
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Get cache engine stats
329
+ * @returns {object} Cache statistics
330
+ */
331
+ getCacheStats() {
332
+ return this.cacheEngine.getStats();
333
+ }
334
+
335
+ /**
336
+ * Clear all caches
337
+ */
338
+ clearAllCaches() {
339
+ this.cacheEngine.clear();
340
+ }
341
+
342
+ /**
343
+ * Get global request statistics
344
+ * @returns {object} Request statistics
345
+ */
346
+ getRequestStats() {
347
+ const hitRate = this.requestStats.totalRequests > 0
348
+ ? (this.requestStats.cacheHits / this.requestStats.totalRequests * 100).toFixed(2)
349
+ : 0;
350
+
351
+ return {
352
+ ...this.requestStats,
353
+ hitRate: hitRate + '%'
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Private Methods
359
+ */
360
+
361
+ _generateCacheKey(distributionId, path) {
362
+ return `${distributionId}:${path}`;
363
+ }
364
+
365
+ _generateCfId() {
366
+ return 'X-Amz-Cf-Id-' + Math.random().toString(36).substring(2, 15);
367
+ }
368
+
369
+ _generateInvalidationId() {
370
+ return 'I' + Math.random().toString(36).substring(2, 15).toUpperCase();
371
+ }
372
+
373
+ _getCountryFromIp(ip) {
374
+ // Simplified - in production this would use GeoIP database
375
+ // For demo: return country code based on IP ranges
376
+ const ipParts = ip.split('.');
377
+ const firstOctet = parseInt(ipParts[0]);
378
+
379
+ // Rough mapping for demo
380
+ if (firstOctet >= 1 && firstOctet <= 39) return 'US';
381
+ if (firstOctet >= 40 && firstOctet <= 79) return 'GB';
382
+ if (firstOctet >= 80 && firstOctet <= 119) return 'DE';
383
+ if (firstOctet >= 120 && firstOctet <= 159) return 'JP';
384
+ if (firstOctet >= 160 && firstOctet <= 199) return 'BR';
385
+ if (firstOctet >= 200 && firstOctet <= 255) return 'CN';
386
+
387
+ return 'UNKNOWN';
388
+ }
389
+
390
+ _validateSignedUrl(request, distribution) {
391
+ const url = request.url || request.path;
392
+
393
+ // Look for signature parameters in URL or headers
394
+ const hasSignature = url?.includes('Signature=') ||
395
+ url?.includes('Policy=') ||
396
+ request.headers?.['CloudFront-Policy'];
397
+
398
+ if (!hasSignature) {
399
+ return {
400
+ isValid: false,
401
+ reason: 'MISSING_SIGNATURE'
402
+ };
403
+ }
404
+
405
+ // Full verification would require the public key
406
+ // For now, just check if signature is present
407
+ return {
408
+ isValid: true
409
+ };
410
+ }
411
+
412
+ _simulateOriginRequest(origin, request, behavior) {
413
+ // Rotate to real origin (S3, API Gateway, ALB, Lambda, HTTP)
414
+ return this.originRouter.routeToOrigin(origin, request.path || '/', request)
415
+ .then(response => response)
416
+ .catch(error => ({
417
+ status: 502,
418
+ headers: { 'Content-Type': 'application/json' },
419
+ body: JSON.stringify({ error: 'Bad Gateway', message: error.message }),
420
+ timestamp: new Date().toISOString()
421
+ }));
422
+ }
423
+ }
424
+
425
+ module.exports = CloudFrontSimulator;