@stacksjs/ts-cloud-core 0.1.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/LICENSE.md +21 -0
- package/README.md +321 -0
- package/package.json +31 -0
- package/src/advanced-features.test.ts +465 -0
- package/src/aws/cloudformation.ts +421 -0
- package/src/aws/cloudfront.ts +158 -0
- package/src/aws/credentials.test.ts +132 -0
- package/src/aws/credentials.ts +545 -0
- package/src/aws/index.ts +87 -0
- package/src/aws/s3.test.ts +188 -0
- package/src/aws/s3.ts +1088 -0
- package/src/aws/signature.test.ts +670 -0
- package/src/aws/signature.ts +1155 -0
- package/src/backup/disaster-recovery.test.ts +726 -0
- package/src/backup/disaster-recovery.ts +500 -0
- package/src/backup/index.ts +34 -0
- package/src/backup/manager.test.ts +498 -0
- package/src/backup/manager.ts +432 -0
- package/src/cicd/circleci.ts +430 -0
- package/src/cicd/github-actions.ts +424 -0
- package/src/cicd/gitlab-ci.ts +255 -0
- package/src/cicd/index.ts +8 -0
- package/src/cli/history.ts +396 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/progress.ts +458 -0
- package/src/cli/repl.ts +454 -0
- package/src/cli/suggestions.ts +327 -0
- package/src/cli/table.test.ts +319 -0
- package/src/cli/table.ts +332 -0
- package/src/cloudformation/builder.test.ts +327 -0
- package/src/cloudformation/builder.ts +378 -0
- package/src/cloudformation/builders/api-gateway.ts +449 -0
- package/src/cloudformation/builders/cache.ts +334 -0
- package/src/cloudformation/builders/cdn.ts +278 -0
- package/src/cloudformation/builders/compute.ts +485 -0
- package/src/cloudformation/builders/database.ts +392 -0
- package/src/cloudformation/builders/functions.ts +343 -0
- package/src/cloudformation/builders/messaging.ts +140 -0
- package/src/cloudformation/builders/monitoring.ts +300 -0
- package/src/cloudformation/builders/network.ts +264 -0
- package/src/cloudformation/builders/queue.ts +147 -0
- package/src/cloudformation/builders/security.ts +399 -0
- package/src/cloudformation/builders/storage.ts +285 -0
- package/src/cloudformation/index.ts +30 -0
- package/src/cloudformation/types.ts +173 -0
- package/src/compliance/aws-config.ts +543 -0
- package/src/compliance/cloudtrail.ts +376 -0
- package/src/compliance/compliance.test.ts +423 -0
- package/src/compliance/guardduty.ts +446 -0
- package/src/compliance/index.ts +66 -0
- package/src/compliance/security-hub.ts +456 -0
- package/src/containers/build-optimization.ts +416 -0
- package/src/containers/containers.test.ts +508 -0
- package/src/containers/image-scanning.ts +360 -0
- package/src/containers/index.ts +9 -0
- package/src/containers/registry.ts +293 -0
- package/src/containers/service-mesh.ts +520 -0
- package/src/database/database.test.ts +762 -0
- package/src/database/index.ts +9 -0
- package/src/database/migrations.ts +444 -0
- package/src/database/performance.ts +528 -0
- package/src/database/replicas.ts +534 -0
- package/src/database/users.ts +494 -0
- package/src/dependency-graph.ts +143 -0
- package/src/deployment/ab-testing.ts +582 -0
- package/src/deployment/blue-green.ts +452 -0
- package/src/deployment/canary.ts +500 -0
- package/src/deployment/deployment.test.ts +526 -0
- package/src/deployment/index.ts +61 -0
- package/src/deployment/progressive.ts +62 -0
- package/src/dns/dns.test.ts +641 -0
- package/src/dns/dnssec.ts +315 -0
- package/src/dns/index.ts +8 -0
- package/src/dns/resolver.ts +496 -0
- package/src/dns/routing.ts +593 -0
- package/src/email/advanced/analytics.ts +445 -0
- package/src/email/advanced/index.ts +11 -0
- package/src/email/advanced/rules.ts +465 -0
- package/src/email/advanced/scheduling.ts +352 -0
- package/src/email/advanced/search.ts +412 -0
- package/src/email/advanced/shared-mailboxes.ts +404 -0
- package/src/email/advanced/templates.ts +455 -0
- package/src/email/advanced/threading.ts +281 -0
- package/src/email/analytics.ts +467 -0
- package/src/email/bounce-handling.ts +425 -0
- package/src/email/email.test.ts +431 -0
- package/src/email/handlers/__tests__/inbound.test.ts +38 -0
- package/src/email/handlers/__tests__/outbound.test.ts +37 -0
- package/src/email/handlers/converter.ts +227 -0
- package/src/email/handlers/feedback.ts +228 -0
- package/src/email/handlers/inbound.ts +169 -0
- package/src/email/handlers/outbound.ts +178 -0
- package/src/email/index.ts +15 -0
- package/src/email/reputation.ts +303 -0
- package/src/email/templates.ts +352 -0
- package/src/errors/index.test.ts +434 -0
- package/src/errors/index.ts +416 -0
- package/src/health-checks/index.ts +40 -0
- package/src/index.ts +360 -0
- package/src/intrinsic-functions.ts +118 -0
- package/src/lambda/concurrency.ts +330 -0
- package/src/lambda/destinations.ts +345 -0
- package/src/lambda/dlq.ts +425 -0
- package/src/lambda/index.ts +11 -0
- package/src/lambda/lambda.test.ts +840 -0
- package/src/lambda/layers.ts +263 -0
- package/src/lambda/versions.ts +376 -0
- package/src/lambda/vpc.ts +399 -0
- package/src/local/config.ts +114 -0
- package/src/local/index.ts +6 -0
- package/src/local/mock-aws.ts +351 -0
- package/src/modules/ai.ts +340 -0
- package/src/modules/api.ts +478 -0
- package/src/modules/auth.ts +805 -0
- package/src/modules/cache.ts +417 -0
- package/src/modules/cdn.ts +1062 -0
- package/src/modules/communication.ts +1094 -0
- package/src/modules/compute.ts +3348 -0
- package/src/modules/database.ts +554 -0
- package/src/modules/deployment.ts +1079 -0
- package/src/modules/dns.ts +337 -0
- package/src/modules/email.ts +1538 -0
- package/src/modules/filesystem.ts +515 -0
- package/src/modules/index.ts +32 -0
- package/src/modules/messaging.ts +486 -0
- package/src/modules/monitoring.ts +2086 -0
- package/src/modules/network.ts +664 -0
- package/src/modules/parameter-store.ts +325 -0
- package/src/modules/permissions.ts +1081 -0
- package/src/modules/phone.ts +494 -0
- package/src/modules/queue.ts +1260 -0
- package/src/modules/redirects.ts +464 -0
- package/src/modules/registry.ts +699 -0
- package/src/modules/search.ts +401 -0
- package/src/modules/secrets.ts +416 -0
- package/src/modules/security.ts +731 -0
- package/src/modules/sms.ts +389 -0
- package/src/modules/storage.ts +1120 -0
- package/src/modules/workflow.ts +680 -0
- package/src/multi-account/config.ts +521 -0
- package/src/multi-account/index.ts +7 -0
- package/src/multi-account/manager.ts +427 -0
- package/src/multi-region/cross-region.ts +410 -0
- package/src/multi-region/index.ts +8 -0
- package/src/multi-region/manager.ts +483 -0
- package/src/multi-region/regions.ts +435 -0
- package/src/network-security/index.ts +48 -0
- package/src/observability/index.ts +9 -0
- package/src/observability/logs.ts +522 -0
- package/src/observability/metrics.ts +460 -0
- package/src/observability/observability.test.ts +782 -0
- package/src/observability/synthetics.ts +568 -0
- package/src/observability/xray.ts +358 -0
- package/src/phone/advanced/analytics.ts +349 -0
- package/src/phone/advanced/callbacks.ts +428 -0
- package/src/phone/advanced/index.ts +8 -0
- package/src/phone/advanced/ivr-builder.ts +504 -0
- package/src/phone/advanced/recording.ts +310 -0
- package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
- package/src/phone/handlers/incoming-call.ts +117 -0
- package/src/phone/handlers/missed-call.ts +116 -0
- package/src/phone/handlers/voicemail.ts +179 -0
- package/src/phone/index.ts +9 -0
- package/src/presets/api-backend.ts +134 -0
- package/src/presets/data-pipeline.ts +204 -0
- package/src/presets/extend.test.ts +295 -0
- package/src/presets/extend.ts +297 -0
- package/src/presets/fullstack-app.ts +144 -0
- package/src/presets/index.ts +27 -0
- package/src/presets/jamstack.ts +135 -0
- package/src/presets/microservices.ts +167 -0
- package/src/presets/ml-api.ts +208 -0
- package/src/presets/nodejs-server.ts +104 -0
- package/src/presets/nodejs-serverless.ts +114 -0
- package/src/presets/realtime-app.ts +184 -0
- package/src/presets/static-site.ts +64 -0
- package/src/presets/traditional-web-app.ts +339 -0
- package/src/presets/wordpress.ts +138 -0
- package/src/preview/github.test.ts +249 -0
- package/src/preview/github.ts +297 -0
- package/src/preview/index.ts +37 -0
- package/src/preview/manager.test.ts +440 -0
- package/src/preview/manager.ts +326 -0
- package/src/preview/notifications.test.ts +582 -0
- package/src/preview/notifications.ts +341 -0
- package/src/queue/batch-processing.ts +402 -0
- package/src/queue/dlq-monitoring.ts +402 -0
- package/src/queue/fifo.ts +342 -0
- package/src/queue/index.ts +9 -0
- package/src/queue/management.ts +428 -0
- package/src/queue/queue.test.ts +429 -0
- package/src/resource-mgmt/index.ts +39 -0
- package/src/resource-naming.ts +62 -0
- package/src/s3/index.ts +523 -0
- package/src/schema/cloud-config.schema.json +554 -0
- package/src/schema/index.ts +68 -0
- package/src/security/certificate-manager.ts +492 -0
- package/src/security/index.ts +9 -0
- package/src/security/scanning.ts +545 -0
- package/src/security/secrets-manager.ts +476 -0
- package/src/security/secrets-rotation.ts +456 -0
- package/src/security/security.test.ts +738 -0
- package/src/sms/advanced/ab-testing.ts +389 -0
- package/src/sms/advanced/analytics.ts +336 -0
- package/src/sms/advanced/campaigns.ts +523 -0
- package/src/sms/advanced/chatbot.ts +224 -0
- package/src/sms/advanced/index.ts +10 -0
- package/src/sms/advanced/link-tracking.ts +248 -0
- package/src/sms/advanced/mms.ts +308 -0
- package/src/sms/handlers/__tests__/send.test.ts +40 -0
- package/src/sms/handlers/delivery-status.ts +133 -0
- package/src/sms/handlers/receive.ts +162 -0
- package/src/sms/handlers/send.ts +174 -0
- package/src/sms/index.ts +9 -0
- package/src/stack-diff.ts +389 -0
- package/src/static-site/index.ts +85 -0
- package/src/template-builder.ts +110 -0
- package/src/template-validator.ts +574 -0
- package/src/utils/cache.ts +291 -0
- package/src/utils/diff.ts +269 -0
- package/src/utils/hash.ts +227 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/parallel.ts +294 -0
- package/src/validators/credentials.test.ts +274 -0
- package/src/validators/credentials.ts +233 -0
- package/src/validators/quotas.test.ts +434 -0
- package/src/validators/quotas.ts +217 -0
- package/test/ai.test.ts +327 -0
- package/test/api.test.ts +511 -0
- package/test/auth.test.ts +632 -0
- package/test/cache.test.ts +406 -0
- package/test/cdn.test.ts +247 -0
- package/test/compute.test.ts +861 -0
- package/test/database.test.ts +523 -0
- package/test/deployment.test.ts +499 -0
- package/test/dns.test.ts +270 -0
- package/test/email.test.ts +439 -0
- package/test/filesystem.test.ts +382 -0
- package/test/integration.test.ts +350 -0
- package/test/messaging.test.ts +514 -0
- package/test/monitoring.test.ts +634 -0
- package/test/network.test.ts +425 -0
- package/test/permissions.test.ts +488 -0
- package/test/queue.test.ts +484 -0
- package/test/registry.test.ts +306 -0
- package/test/security.test.ts +462 -0
- package/test/storage.test.ts +463 -0
- package/test/template-validator.test.ts +559 -0
- package/test/workflow.test.ts +592 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Image Scanning
|
|
3
|
+
* Vulnerability scanning with Trivy, Snyk, and other tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ImageScanConfig {
|
|
7
|
+
id: string
|
|
8
|
+
repository: string
|
|
9
|
+
imageTag: string
|
|
10
|
+
scanner: ScannerType
|
|
11
|
+
scanOnPush: boolean
|
|
12
|
+
scanSchedule?: string
|
|
13
|
+
failOnSeverity?: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'
|
|
14
|
+
ignoreUnfixed?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ScannerType = 'trivy' | 'snyk' | 'clair' | 'anchore' | 'ecr'
|
|
18
|
+
|
|
19
|
+
export interface ImageScanResult {
|
|
20
|
+
id: string
|
|
21
|
+
imageUri: string
|
|
22
|
+
scannerType: ScannerType
|
|
23
|
+
scanDate: Date
|
|
24
|
+
vulnerabilities: ImageVulnerability[]
|
|
25
|
+
summary: VulnerabilitySummary
|
|
26
|
+
passed: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ImageVulnerability {
|
|
30
|
+
id: string
|
|
31
|
+
cve: string
|
|
32
|
+
severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'UNKNOWN'
|
|
33
|
+
packageName: string
|
|
34
|
+
installedVersion: string
|
|
35
|
+
fixedVersion?: string
|
|
36
|
+
title: string
|
|
37
|
+
description: string
|
|
38
|
+
references: string[]
|
|
39
|
+
cvss?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface VulnerabilitySummary {
|
|
43
|
+
total: number
|
|
44
|
+
critical: number
|
|
45
|
+
high: number
|
|
46
|
+
medium: number
|
|
47
|
+
low: number
|
|
48
|
+
unknown: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ScanPolicy {
|
|
52
|
+
id: string
|
|
53
|
+
name: string
|
|
54
|
+
allowedSeverities: string[]
|
|
55
|
+
maxCritical: number
|
|
56
|
+
maxHigh: number
|
|
57
|
+
blockOnFailure: boolean
|
|
58
|
+
exemptions: string[] // CVE IDs to ignore
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Image scanning manager
|
|
63
|
+
*/
|
|
64
|
+
export class ImageScanningManager {
|
|
65
|
+
private configs: Map<string, ImageScanConfig> = new Map()
|
|
66
|
+
private results: Map<string, ImageScanResult> = new Map()
|
|
67
|
+
private policies: Map<string, ScanPolicy> = new Map()
|
|
68
|
+
private configCounter = 0
|
|
69
|
+
private resultCounter = 0
|
|
70
|
+
private policyCounter = 0
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configure image scanning
|
|
74
|
+
*/
|
|
75
|
+
configureScan(config: Omit<ImageScanConfig, 'id'>): ImageScanConfig {
|
|
76
|
+
const id = `scan-config-${Date.now()}-${this.configCounter++}`
|
|
77
|
+
|
|
78
|
+
const scanConfig: ImageScanConfig = {
|
|
79
|
+
id,
|
|
80
|
+
...config,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.configs.set(id, scanConfig)
|
|
84
|
+
|
|
85
|
+
return scanConfig
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Configure Trivy scanning
|
|
90
|
+
*/
|
|
91
|
+
configureTrivyScan(options: {
|
|
92
|
+
repository: string
|
|
93
|
+
imageTag: string
|
|
94
|
+
scanOnPush?: boolean
|
|
95
|
+
ignoreUnfixed?: boolean
|
|
96
|
+
}): ImageScanConfig {
|
|
97
|
+
return this.configureScan({
|
|
98
|
+
repository: options.repository,
|
|
99
|
+
imageTag: options.imageTag,
|
|
100
|
+
scanner: 'trivy',
|
|
101
|
+
scanOnPush: options.scanOnPush ?? true,
|
|
102
|
+
ignoreUnfixed: options.ignoreUnfixed ?? false,
|
|
103
|
+
failOnSeverity: 'HIGH',
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Configure Snyk scanning
|
|
109
|
+
*/
|
|
110
|
+
configureSnykScan(options: {
|
|
111
|
+
repository: string
|
|
112
|
+
imageTag: string
|
|
113
|
+
scanOnPush?: boolean
|
|
114
|
+
}): ImageScanConfig {
|
|
115
|
+
return this.configureScan({
|
|
116
|
+
repository: options.repository,
|
|
117
|
+
imageTag: options.imageTag,
|
|
118
|
+
scanner: 'snyk',
|
|
119
|
+
scanOnPush: options.scanOnPush ?? true,
|
|
120
|
+
failOnSeverity: 'HIGH',
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Configure ECR scanning
|
|
126
|
+
*/
|
|
127
|
+
configureECRScan(options: {
|
|
128
|
+
repository: string
|
|
129
|
+
scanOnPush?: boolean
|
|
130
|
+
}): ImageScanConfig {
|
|
131
|
+
return this.configureScan({
|
|
132
|
+
repository: options.repository,
|
|
133
|
+
imageTag: 'latest',
|
|
134
|
+
scanner: 'ecr',
|
|
135
|
+
scanOnPush: options.scanOnPush ?? true,
|
|
136
|
+
failOnSeverity: 'CRITICAL',
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Scan image
|
|
142
|
+
*/
|
|
143
|
+
async scanImage(configId: string): Promise<ImageScanResult> {
|
|
144
|
+
const config = this.configs.get(configId)
|
|
145
|
+
|
|
146
|
+
if (!config) {
|
|
147
|
+
throw new Error(`Scan config not found: ${configId}`)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const imageUri = `${config.repository}:${config.imageTag}`
|
|
151
|
+
|
|
152
|
+
console.log(`\nScanning image: ${imageUri}`)
|
|
153
|
+
console.log(`Scanner: ${config.scanner}`)
|
|
154
|
+
|
|
155
|
+
const id = `scan-result-${Date.now()}-${this.resultCounter++}`
|
|
156
|
+
|
|
157
|
+
// Simulate scanning
|
|
158
|
+
const vulnerabilities = this.simulateVulnerabilities(config)
|
|
159
|
+
|
|
160
|
+
const summary: VulnerabilitySummary = {
|
|
161
|
+
total: vulnerabilities.length,
|
|
162
|
+
critical: vulnerabilities.filter(v => v.severity === 'CRITICAL').length,
|
|
163
|
+
high: vulnerabilities.filter(v => v.severity === 'HIGH').length,
|
|
164
|
+
medium: vulnerabilities.filter(v => v.severity === 'MEDIUM').length,
|
|
165
|
+
low: vulnerabilities.filter(v => v.severity === 'LOW').length,
|
|
166
|
+
unknown: vulnerabilities.filter(v => v.severity === 'UNKNOWN').length,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const passed = this.evaluateScanResult(config, summary)
|
|
170
|
+
|
|
171
|
+
const result: ImageScanResult = {
|
|
172
|
+
id,
|
|
173
|
+
imageUri,
|
|
174
|
+
scannerType: config.scanner,
|
|
175
|
+
scanDate: new Date(),
|
|
176
|
+
vulnerabilities,
|
|
177
|
+
summary,
|
|
178
|
+
passed,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.results.set(id, result)
|
|
182
|
+
|
|
183
|
+
console.log('\nScan Results:')
|
|
184
|
+
console.log(` Total vulnerabilities: ${summary.total}`)
|
|
185
|
+
console.log(` Critical: ${summary.critical}`)
|
|
186
|
+
console.log(` High: ${summary.high}`)
|
|
187
|
+
console.log(` Medium: ${summary.medium}`)
|
|
188
|
+
console.log(` Low: ${summary.low}`)
|
|
189
|
+
console.log(` Status: ${passed ? '✓ PASSED' : '✗ FAILED'}`)
|
|
190
|
+
|
|
191
|
+
return result
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Simulate vulnerabilities (in production, call actual scanner)
|
|
196
|
+
*/
|
|
197
|
+
private simulateVulnerabilities(config: ImageScanConfig): ImageVulnerability[] {
|
|
198
|
+
const vulnerabilities: ImageVulnerability[] = []
|
|
199
|
+
|
|
200
|
+
if (config.scanner === 'trivy' || config.scanner === 'ecr') {
|
|
201
|
+
vulnerabilities.push({
|
|
202
|
+
id: 'vuln-1',
|
|
203
|
+
cve: 'CVE-2024-1234',
|
|
204
|
+
severity: 'HIGH',
|
|
205
|
+
packageName: 'openssl',
|
|
206
|
+
installedVersion: '1.1.1k',
|
|
207
|
+
fixedVersion: '1.1.1l',
|
|
208
|
+
title: 'OpenSSL buffer overflow',
|
|
209
|
+
description: 'Buffer overflow vulnerability in OpenSSL',
|
|
210
|
+
references: ['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-1234'],
|
|
211
|
+
cvss: 7.5,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
vulnerabilities.push({
|
|
215
|
+
id: 'vuln-2',
|
|
216
|
+
cve: 'CVE-2024-5678',
|
|
217
|
+
severity: 'MEDIUM',
|
|
218
|
+
packageName: 'curl',
|
|
219
|
+
installedVersion: '7.68.0',
|
|
220
|
+
fixedVersion: '7.79.0',
|
|
221
|
+
title: 'curl remote code execution',
|
|
222
|
+
description: 'Remote code execution in curl',
|
|
223
|
+
references: ['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-5678'],
|
|
224
|
+
cvss: 5.3,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return vulnerabilities
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Evaluate scan result
|
|
233
|
+
*/
|
|
234
|
+
private evaluateScanResult(config: ImageScanConfig, summary: VulnerabilitySummary): boolean {
|
|
235
|
+
if (!config.failOnSeverity) {
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
switch (config.failOnSeverity) {
|
|
240
|
+
case 'CRITICAL':
|
|
241
|
+
return summary.critical === 0
|
|
242
|
+
case 'HIGH':
|
|
243
|
+
return summary.critical === 0 && summary.high === 0
|
|
244
|
+
case 'MEDIUM':
|
|
245
|
+
return summary.critical === 0 && summary.high === 0 && summary.medium === 0
|
|
246
|
+
case 'LOW':
|
|
247
|
+
return summary.total === 0
|
|
248
|
+
default:
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create scan policy
|
|
255
|
+
*/
|
|
256
|
+
createPolicy(policy: Omit<ScanPolicy, 'id'>): ScanPolicy {
|
|
257
|
+
const id = `policy-${Date.now()}-${this.policyCounter++}`
|
|
258
|
+
|
|
259
|
+
const scanPolicy: ScanPolicy = {
|
|
260
|
+
id,
|
|
261
|
+
...policy,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.policies.set(id, scanPolicy)
|
|
265
|
+
|
|
266
|
+
return scanPolicy
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create strict policy
|
|
271
|
+
*/
|
|
272
|
+
createStrictPolicy(name: string): ScanPolicy {
|
|
273
|
+
return this.createPolicy({
|
|
274
|
+
name,
|
|
275
|
+
allowedSeverities: [],
|
|
276
|
+
maxCritical: 0,
|
|
277
|
+
maxHigh: 0,
|
|
278
|
+
blockOnFailure: true,
|
|
279
|
+
exemptions: [],
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Create permissive policy
|
|
285
|
+
*/
|
|
286
|
+
createPermissivePolicy(name: string): ScanPolicy {
|
|
287
|
+
return this.createPolicy({
|
|
288
|
+
name,
|
|
289
|
+
allowedSeverities: ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'],
|
|
290
|
+
maxCritical: 5,
|
|
291
|
+
maxHigh: 10,
|
|
292
|
+
blockOnFailure: false,
|
|
293
|
+
exemptions: [],
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get config
|
|
299
|
+
*/
|
|
300
|
+
getConfig(id: string): ImageScanConfig | undefined {
|
|
301
|
+
return this.configs.get(id)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* List configs
|
|
306
|
+
*/
|
|
307
|
+
listConfigs(): ImageScanConfig[] {
|
|
308
|
+
return Array.from(this.configs.values())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get result
|
|
313
|
+
*/
|
|
314
|
+
getResult(id: string): ImageScanResult | undefined {
|
|
315
|
+
return this.results.get(id)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* List results
|
|
320
|
+
*/
|
|
321
|
+
listResults(): ImageScanResult[] {
|
|
322
|
+
return Array.from(this.results.values())
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Generate CloudFormation for ECR scanning
|
|
327
|
+
*/
|
|
328
|
+
generateECRScanCF(config: ImageScanConfig): any {
|
|
329
|
+
return {
|
|
330
|
+
Type: 'AWS::ECR::Repository',
|
|
331
|
+
Properties: {
|
|
332
|
+
RepositoryName: config.repository,
|
|
333
|
+
ImageScanningConfiguration: {
|
|
334
|
+
ScanOnPush: config.scanOnPush,
|
|
335
|
+
},
|
|
336
|
+
ImageTagMutability: 'IMMUTABLE',
|
|
337
|
+
EncryptionConfiguration: {
|
|
338
|
+
EncryptionType: 'AES256',
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Clear all data
|
|
346
|
+
*/
|
|
347
|
+
clear(): void {
|
|
348
|
+
this.configs.clear()
|
|
349
|
+
this.results.clear()
|
|
350
|
+
this.policies.clear()
|
|
351
|
+
this.configCounter = 0
|
|
352
|
+
this.resultCounter = 0
|
|
353
|
+
this.policyCounter = 0
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Global image scanning manager instance
|
|
359
|
+
*/
|
|
360
|
+
export const imageScanningManager: ImageScanningManager = new ImageScanningManager()
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private Container Registry
|
|
3
|
+
* ECR repository management and access control
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ContainerRegistry {
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
registryType: 'ecr' | 'dockerhub' | 'gcr' | 'acr'
|
|
10
|
+
repositoryUri: string
|
|
11
|
+
region?: string
|
|
12
|
+
encryption?: RegistryEncryption
|
|
13
|
+
scanning?: ScanningConfig
|
|
14
|
+
lifecycle?: LifecyclePolicy
|
|
15
|
+
replication?: ReplicationConfig
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RegistryEncryption {
|
|
19
|
+
encryptionType: 'AES256' | 'KMS'
|
|
20
|
+
kmsKeyId?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ScanningConfig {
|
|
24
|
+
scanOnPush: boolean
|
|
25
|
+
scanFilters?: ScanFilter[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ScanFilter {
|
|
29
|
+
tagPattern: string
|
|
30
|
+
scanFrequency: 'on_push' | 'daily' | 'weekly'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface LifecyclePolicy {
|
|
34
|
+
id: string
|
|
35
|
+
rules: LifecycleRule[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface LifecycleRule {
|
|
39
|
+
rulePriority: number
|
|
40
|
+
description: string
|
|
41
|
+
selection: {
|
|
42
|
+
tagStatus: 'tagged' | 'untagged' | 'any'
|
|
43
|
+
tagPrefixList?: string[]
|
|
44
|
+
countType: 'imageCountMoreThan' | 'sinceImagePushed'
|
|
45
|
+
countNumber: number
|
|
46
|
+
countUnit?: 'days'
|
|
47
|
+
}
|
|
48
|
+
action: {
|
|
49
|
+
type: 'expire'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ReplicationConfig {
|
|
54
|
+
enabled: boolean
|
|
55
|
+
destinations: ReplicationDestination[]
|
|
56
|
+
rules?: ReplicationRule[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ReplicationDestination {
|
|
60
|
+
region: string
|
|
61
|
+
registryId?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ReplicationRule {
|
|
65
|
+
repositoryFilter: string[]
|
|
66
|
+
destinations: ReplicationDestination[]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RegistryCredentials {
|
|
70
|
+
id: string
|
|
71
|
+
registryId: string
|
|
72
|
+
username: string
|
|
73
|
+
passwordSecretArn: string
|
|
74
|
+
expiresAt?: Date
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Container registry manager
|
|
79
|
+
*/
|
|
80
|
+
export class ContainerRegistryManager {
|
|
81
|
+
private registries: Map<string, ContainerRegistry> = new Map()
|
|
82
|
+
private credentials: Map<string, RegistryCredentials> = new Map()
|
|
83
|
+
private registryCounter = 0
|
|
84
|
+
private credentialsCounter = 0
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create registry
|
|
88
|
+
*/
|
|
89
|
+
createRegistry(registry: Omit<ContainerRegistry, 'id'>): ContainerRegistry {
|
|
90
|
+
const id = `registry-${Date.now()}-${this.registryCounter++}`
|
|
91
|
+
|
|
92
|
+
const containerRegistry: ContainerRegistry = {
|
|
93
|
+
id,
|
|
94
|
+
...registry,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.registries.set(id, containerRegistry)
|
|
98
|
+
|
|
99
|
+
return containerRegistry
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create ECR repository
|
|
104
|
+
*/
|
|
105
|
+
createECRRepository(options: {
|
|
106
|
+
name: string
|
|
107
|
+
region?: string
|
|
108
|
+
scanOnPush?: boolean
|
|
109
|
+
encryption?: 'AES256' | 'KMS'
|
|
110
|
+
kmsKeyId?: string
|
|
111
|
+
}): ContainerRegistry {
|
|
112
|
+
return this.createRegistry({
|
|
113
|
+
name: options.name,
|
|
114
|
+
registryType: 'ecr',
|
|
115
|
+
repositoryUri: `123456789012.dkr.ecr.${options.region || 'us-east-1'}.amazonaws.com/${options.name}`,
|
|
116
|
+
region: options.region || 'us-east-1',
|
|
117
|
+
encryption: {
|
|
118
|
+
encryptionType: options.encryption || 'AES256',
|
|
119
|
+
kmsKeyId: options.kmsKeyId,
|
|
120
|
+
},
|
|
121
|
+
scanning: {
|
|
122
|
+
scanOnPush: options.scanOnPush ?? true,
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create private registry with lifecycle policy
|
|
129
|
+
*/
|
|
130
|
+
createManagedRepository(options: {
|
|
131
|
+
name: string
|
|
132
|
+
region?: string
|
|
133
|
+
maxImageCount?: number
|
|
134
|
+
maxImageAgeDays?: number
|
|
135
|
+
}): ContainerRegistry {
|
|
136
|
+
const registry = this.createECRRepository({
|
|
137
|
+
name: options.name,
|
|
138
|
+
region: options.region,
|
|
139
|
+
scanOnPush: true,
|
|
140
|
+
encryption: 'KMS',
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Add lifecycle policy
|
|
144
|
+
registry.lifecycle = {
|
|
145
|
+
id: `lifecycle-${Date.now()}`,
|
|
146
|
+
rules: [
|
|
147
|
+
{
|
|
148
|
+
rulePriority: 1,
|
|
149
|
+
description: 'Keep only last N images',
|
|
150
|
+
selection: {
|
|
151
|
+
tagStatus: 'any',
|
|
152
|
+
countType: 'imageCountMoreThan',
|
|
153
|
+
countNumber: options.maxImageCount || 10,
|
|
154
|
+
},
|
|
155
|
+
action: {
|
|
156
|
+
type: 'expire',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
rulePriority: 2,
|
|
161
|
+
description: 'Remove images older than N days',
|
|
162
|
+
selection: {
|
|
163
|
+
tagStatus: 'untagged',
|
|
164
|
+
countType: 'sinceImagePushed',
|
|
165
|
+
countNumber: options.maxImageAgeDays || 30,
|
|
166
|
+
countUnit: 'days',
|
|
167
|
+
},
|
|
168
|
+
action: {
|
|
169
|
+
type: 'expire',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return registry
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Enable cross-region replication
|
|
180
|
+
*/
|
|
181
|
+
enableReplication(
|
|
182
|
+
registryId: string,
|
|
183
|
+
destinations: ReplicationDestination[]
|
|
184
|
+
): ContainerRegistry {
|
|
185
|
+
const registry = this.registries.get(registryId)
|
|
186
|
+
|
|
187
|
+
if (!registry) {
|
|
188
|
+
throw new Error(`Registry not found: ${registryId}`)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
registry.replication = {
|
|
192
|
+
enabled: true,
|
|
193
|
+
destinations,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return registry
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create registry credentials
|
|
201
|
+
*/
|
|
202
|
+
createCredentials(credentials: Omit<RegistryCredentials, 'id'>): RegistryCredentials {
|
|
203
|
+
const id = `creds-${Date.now()}-${this.credentialsCounter++}`
|
|
204
|
+
|
|
205
|
+
const registryCredentials: RegistryCredentials = {
|
|
206
|
+
id,
|
|
207
|
+
...credentials,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.credentials.set(id, registryCredentials)
|
|
211
|
+
|
|
212
|
+
return registryCredentials
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get registry
|
|
217
|
+
*/
|
|
218
|
+
getRegistry(id: string): ContainerRegistry | undefined {
|
|
219
|
+
return this.registries.get(id)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* List registries
|
|
224
|
+
*/
|
|
225
|
+
listRegistries(): ContainerRegistry[] {
|
|
226
|
+
return Array.from(this.registries.values())
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generate CloudFormation for ECR repository
|
|
231
|
+
*/
|
|
232
|
+
generateECRRepositoryCF(registry: ContainerRegistry): any {
|
|
233
|
+
return {
|
|
234
|
+
Type: 'AWS::ECR::Repository',
|
|
235
|
+
Properties: {
|
|
236
|
+
RepositoryName: registry.name,
|
|
237
|
+
ImageScanningConfiguration: {
|
|
238
|
+
ScanOnPush: registry.scanning?.scanOnPush ?? false,
|
|
239
|
+
},
|
|
240
|
+
EncryptionConfiguration: {
|
|
241
|
+
EncryptionType: registry.encryption?.encryptionType || 'AES256',
|
|
242
|
+
...(registry.encryption?.kmsKeyId && {
|
|
243
|
+
KmsKey: registry.encryption.kmsKeyId,
|
|
244
|
+
}),
|
|
245
|
+
},
|
|
246
|
+
ImageTagMutability: 'MUTABLE',
|
|
247
|
+
...(registry.lifecycle && {
|
|
248
|
+
LifecyclePolicy: {
|
|
249
|
+
LifecyclePolicyText: JSON.stringify({
|
|
250
|
+
rules: registry.lifecycle.rules,
|
|
251
|
+
}),
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Generate CloudFormation for replication configuration
|
|
260
|
+
*/
|
|
261
|
+
generateReplicationConfigCF(replication: ReplicationConfig): any {
|
|
262
|
+
return {
|
|
263
|
+
Type: 'AWS::ECR::ReplicationConfiguration',
|
|
264
|
+
Properties: {
|
|
265
|
+
ReplicationConfiguration: {
|
|
266
|
+
Rules: replication.destinations.map(dest => ({
|
|
267
|
+
Destinations: [
|
|
268
|
+
{
|
|
269
|
+
Region: dest.region,
|
|
270
|
+
...(dest.registryId && { RegistryId: dest.registryId }),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
})),
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Clear all data
|
|
281
|
+
*/
|
|
282
|
+
clear(): void {
|
|
283
|
+
this.registries.clear()
|
|
284
|
+
this.credentials.clear()
|
|
285
|
+
this.registryCounter = 0
|
|
286
|
+
this.credentialsCounter = 0
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Global container registry manager instance
|
|
292
|
+
*/
|
|
293
|
+
export const containerRegistryManager: ContainerRegistryManager = new ContainerRegistryManager()
|