@factiii/stack 0.1.6 → 0.1.8

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 (60) hide show
  1. package/dist/plugins/addons/server-mode/scanfix/mac.d.ts.map +1 -1
  2. package/dist/plugins/addons/server-mode/scanfix/mac.js +13 -1
  3. package/dist/plugins/addons/server-mode/scanfix/mac.js.map +1 -1
  4. package/dist/plugins/pipelines/aws/configs/free-tier.d.ts.map +1 -1
  5. package/dist/plugins/pipelines/aws/configs/free-tier.js +3 -38
  6. package/dist/plugins/pipelines/aws/configs/free-tier.js.map +1 -1
  7. package/dist/plugins/pipelines/aws/index.d.ts +4 -1
  8. package/dist/plugins/pipelines/aws/index.d.ts.map +1 -1
  9. package/dist/plugins/pipelines/aws/index.js +101 -29
  10. package/dist/plugins/pipelines/aws/index.js.map +1 -1
  11. package/dist/plugins/pipelines/aws/scanfix/credentials.d.ts +9 -0
  12. package/dist/plugins/pipelines/aws/scanfix/credentials.d.ts.map +1 -0
  13. package/dist/plugins/pipelines/aws/scanfix/credentials.js +196 -0
  14. package/dist/plugins/pipelines/aws/scanfix/credentials.js.map +1 -0
  15. package/dist/plugins/pipelines/aws/scanfix/db-replication.d.ts +13 -0
  16. package/dist/plugins/pipelines/aws/scanfix/db-replication.d.ts.map +1 -0
  17. package/dist/plugins/pipelines/aws/scanfix/db-replication.js +136 -0
  18. package/dist/plugins/pipelines/aws/scanfix/db-replication.js.map +1 -0
  19. package/dist/plugins/pipelines/aws/scanfix/ec2.d.ts +10 -0
  20. package/dist/plugins/pipelines/aws/scanfix/ec2.d.ts.map +1 -0
  21. package/dist/plugins/pipelines/aws/scanfix/ec2.js +279 -0
  22. package/dist/plugins/pipelines/aws/scanfix/ec2.js.map +1 -0
  23. package/dist/plugins/pipelines/aws/scanfix/ecr.d.ts +9 -0
  24. package/dist/plugins/pipelines/aws/scanfix/ecr.d.ts.map +1 -0
  25. package/dist/plugins/pipelines/aws/scanfix/ecr.js +100 -0
  26. package/dist/plugins/pipelines/aws/scanfix/ecr.js.map +1 -0
  27. package/dist/plugins/pipelines/aws/scanfix/iam.d.ts +10 -0
  28. package/dist/plugins/pipelines/aws/scanfix/iam.d.ts.map +1 -0
  29. package/dist/plugins/pipelines/aws/scanfix/iam.js +255 -0
  30. package/dist/plugins/pipelines/aws/scanfix/iam.js.map +1 -0
  31. package/dist/plugins/pipelines/aws/scanfix/rds.d.ts +10 -0
  32. package/dist/plugins/pipelines/aws/scanfix/rds.d.ts.map +1 -0
  33. package/dist/plugins/pipelines/aws/scanfix/rds.js +261 -0
  34. package/dist/plugins/pipelines/aws/scanfix/rds.js.map +1 -0
  35. package/dist/plugins/pipelines/aws/scanfix/s3.d.ts +9 -0
  36. package/dist/plugins/pipelines/aws/scanfix/s3.d.ts.map +1 -0
  37. package/dist/plugins/pipelines/aws/scanfix/s3.js +134 -0
  38. package/dist/plugins/pipelines/aws/scanfix/s3.js.map +1 -0
  39. package/dist/plugins/pipelines/aws/scanfix/security-groups.d.ts +10 -0
  40. package/dist/plugins/pipelines/aws/scanfix/security-groups.d.ts.map +1 -0
  41. package/dist/plugins/pipelines/aws/scanfix/security-groups.js +225 -0
  42. package/dist/plugins/pipelines/aws/scanfix/security-groups.js.map +1 -0
  43. package/dist/plugins/pipelines/aws/scanfix/ses.d.ts +9 -0
  44. package/dist/plugins/pipelines/aws/scanfix/ses.d.ts.map +1 -0
  45. package/dist/plugins/pipelines/aws/scanfix/ses.js +174 -0
  46. package/dist/plugins/pipelines/aws/scanfix/ses.js.map +1 -0
  47. package/dist/plugins/pipelines/aws/scanfix/vpc.d.ts +9 -0
  48. package/dist/plugins/pipelines/aws/scanfix/vpc.d.ts.map +1 -0
  49. package/dist/plugins/pipelines/aws/scanfix/vpc.js +237 -0
  50. package/dist/plugins/pipelines/aws/scanfix/vpc.js.map +1 -0
  51. package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts +50 -0
  52. package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts.map +1 -0
  53. package/dist/plugins/pipelines/aws/utils/aws-helpers.js +137 -0
  54. package/dist/plugins/pipelines/aws/utils/aws-helpers.js.map +1 -0
  55. package/dist/plugins/pipelines/factiii/index.d.ts.map +1 -1
  56. package/dist/plugins/pipelines/factiii/index.js +11 -0
  57. package/dist/plugins/pipelines/factiii/index.js.map +1 -1
  58. package/dist/types/config.d.ts +11 -0
  59. package/dist/types/config.d.ts.map +1 -1
  60. package/package.json +1 -1
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * AWS Credential Fixes
4
+ *
5
+ * Handles AWS account setup guidance, credential validation,
6
+ * and region configuration checks.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.credentialsFixes = void 0;
43
+ const aws_helpers_js_1 = require("../utils/aws-helpers.js");
44
+ exports.credentialsFixes = [
45
+ // ============================================================
46
+ // DEV STAGE - AWS CLI and account setup
47
+ // ============================================================
48
+ {
49
+ id: 'aws-account-not-setup',
50
+ stage: 'dev',
51
+ severity: 'critical',
52
+ description: 'AWS CLI not installed or not configured',
53
+ scan: async (config, _rootDir) => {
54
+ // Only check if AWS pipeline is configured
55
+ const awsConfig = (0, aws_helpers_js_1.getAwsConfig)(config);
56
+ if (!awsConfig.accessKeyId && !config.aws)
57
+ return false;
58
+ // Check if AWS CLI is installed
59
+ if (!(0, aws_helpers_js_1.isAwsCliInstalled)())
60
+ return true;
61
+ // Check if credentials are configured (can call STS)
62
+ const accountId = (0, aws_helpers_js_1.getAwsAccountId)(awsConfig.region);
63
+ return !accountId;
64
+ },
65
+ fix: null,
66
+ manualFix: [
67
+ 'Setup AWS CLI:',
68
+ '',
69
+ '1. Create an AWS account at https://aws.amazon.com (free tier available)',
70
+ '',
71
+ '2. Install AWS CLI:',
72
+ ' Windows: winget install Amazon.AWSCLI',
73
+ ' macOS: brew install awscli',
74
+ ' Linux: curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && sudo ./aws/install',
75
+ '',
76
+ '3. Create an IAM user in AWS Console:',
77
+ ' IAM → Users → Create user → Attach AdministratorAccess policy',
78
+ ' → Security credentials → Create access key',
79
+ '',
80
+ '4. Configure AWS CLI:',
81
+ ' aws configure',
82
+ ' (Enter Access Key ID, Secret Access Key, region)',
83
+ ].join('\n'),
84
+ },
85
+ {
86
+ id: 'aws-region-configured',
87
+ stage: 'dev',
88
+ severity: 'warning',
89
+ description: 'AWS region not configured in factiii.yml',
90
+ scan: async (config, _rootDir) => {
91
+ // Only check if AWS pipeline is configured
92
+ const { extractEnvironments } = await Promise.resolve().then(() => __importStar(require('../../../../utils/config-helpers.js')));
93
+ const environments = extractEnvironments(config);
94
+ const hasAwsEnv = Object.values(environments).some((e) => e.pipeline === 'aws');
95
+ if (!hasAwsEnv && !config.aws)
96
+ return false;
97
+ const awsConfig = (0, aws_helpers_js_1.getAwsConfig)(config);
98
+ // Check if region is explicitly set (not just default)
99
+ return !awsConfig.region || awsConfig.region === 'us-east-1' && !config.aws?.region;
100
+ },
101
+ fix: null,
102
+ manualFix: 'Set aws.region in factiii.yml under the prod environment or top-level aws block',
103
+ },
104
+ // ============================================================
105
+ // SECRETS STAGE - Credential validation
106
+ // ============================================================
107
+ {
108
+ id: 'aws-credentials-missing',
109
+ stage: 'secrets',
110
+ severity: 'critical',
111
+ description: 'AWS credentials not available (env vars or Ansible Vault)',
112
+ scan: async (config, _rootDir) => {
113
+ // Only check if AWS pipeline is configured
114
+ const { extractEnvironments } = await Promise.resolve().then(() => __importStar(require('../../../../utils/config-helpers.js')));
115
+ const environments = extractEnvironments(config);
116
+ const hasAwsEnv = Object.values(environments).some((e) => e.pipeline === 'aws');
117
+ if (!hasAwsEnv && !config.aws)
118
+ return false;
119
+ // Check env vars
120
+ if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
121
+ return false;
122
+ }
123
+ // Check if Ansible Vault has AWS credentials
124
+ if (config.ansible?.vault_path) {
125
+ try {
126
+ const { AnsibleVaultSecrets } = await Promise.resolve().then(() => __importStar(require('../../../../utils/ansible-vault-secrets.js')));
127
+ const vault = new AnsibleVaultSecrets({
128
+ vault_path: config.ansible.vault_path,
129
+ vault_password_file: config.ansible.vault_password_file,
130
+ });
131
+ const result = await vault.checkSecrets(['aws_access_key_id', 'aws_secret_access_key']);
132
+ if (result.status?.aws_access_key_id && result.status?.aws_secret_access_key) {
133
+ return false;
134
+ }
135
+ }
136
+ catch {
137
+ // Vault not accessible
138
+ }
139
+ }
140
+ return true;
141
+ },
142
+ fix: null,
143
+ manualFix: [
144
+ 'Configure AWS credentials via one of:',
145
+ '',
146
+ ' Option A: Environment variables',
147
+ ' export AWS_ACCESS_KEY_ID=AKIA...',
148
+ ' export AWS_SECRET_ACCESS_KEY=...',
149
+ '',
150
+ ' Option B: AWS CLI configuration',
151
+ ' aws configure',
152
+ '',
153
+ ' Option C: Ansible Vault (recommended)',
154
+ ' Add aws_access_key_id and aws_secret_access_key to your vault file',
155
+ ' npx factiii secrets edit',
156
+ ].join('\n'),
157
+ },
158
+ {
159
+ id: 'aws-credentials-invalid',
160
+ stage: 'secrets',
161
+ severity: 'warning',
162
+ description: 'AWS credentials are invalid or expired',
163
+ scan: async (config, _rootDir) => {
164
+ // Only check if AWS CLI is installed and credentials exist
165
+ if (!(0, aws_helpers_js_1.isAwsCliInstalled)())
166
+ return false;
167
+ if (!process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_SECRET_ACCESS_KEY) {
168
+ // No env vars - might be using aws configure or vault
169
+ // Try to validate anyway
170
+ }
171
+ const awsConfig = (0, aws_helpers_js_1.getAwsConfig)(config);
172
+ const accountId = (0, aws_helpers_js_1.getAwsAccountId)(awsConfig.region);
173
+ // If we can't get account ID, credentials are invalid
174
+ // But only flag if we actually have credentials configured
175
+ if (!accountId) {
176
+ // Check if aws configure has credentials
177
+ try {
178
+ const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
179
+ const result = execSync('aws configure get aws_access_key_id 2>/dev/null || echo ""', {
180
+ encoding: 'utf8',
181
+ stdio: ['pipe', 'pipe', 'pipe'],
182
+ }).trim();
183
+ // Only flag as invalid if credentials exist but don't work
184
+ return result.length > 0;
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ return false;
191
+ },
192
+ fix: null,
193
+ manualFix: 'Check AWS credentials: aws sts get-caller-identity\nIf expired, regenerate in AWS Console: IAM → Users → Security credentials',
194
+ },
195
+ ];
196
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * AWS DB Replication Fixes
3
+ *
4
+ * Prereq checks for DB replication between staging (Mac Mini) and prod (RDS).
5
+ * Ensures PostgreSQL client is available on EC2 and RDS is reachable.
6
+ *
7
+ * Actual sync commands are in the AWS pipeline index.ts as plugin commands:
8
+ * - `db sync-to-prod`: pg_dump Mac Mini → SCP to EC2 → pg_restore into RDS
9
+ * - `db sync-to-staging`: pg_dump RDS via EC2 → SCP to Mac Mini → pg_restore
10
+ */
11
+ import type { Fix } from '../../../../types/index.js';
12
+ export declare const dbReplicationFixes: Fix[];
13
+ //# sourceMappingURL=db-replication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-replication.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/db-replication.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,4BAA4B,CAAC;AAwCrE,eAAO,MAAM,kBAAkB,EAAE,GAAG,EAuFnC,CAAC"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * AWS DB Replication Fixes
4
+ *
5
+ * Prereq checks for DB replication between staging (Mac Mini) and prod (RDS).
6
+ * Ensures PostgreSQL client is available on EC2 and RDS is reachable.
7
+ *
8
+ * Actual sync commands are in the AWS pipeline index.ts as plugin commands:
9
+ * - `db sync-to-prod`: pg_dump Mac Mini → SCP to EC2 → pg_restore into RDS
10
+ * - `db sync-to-staging`: pg_dump RDS via EC2 → SCP to Mac Mini → pg_restore
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.dbReplicationFixes = void 0;
14
+ const aws_helpers_js_1 = require("../utils/aws-helpers.js");
15
+ /**
16
+ * Find RDS instance endpoint
17
+ */
18
+ function findRdsEndpoint(projectName, region) {
19
+ const dbId = 'factiii-' + projectName + '-db';
20
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws rds describe-db-instances --db-instance-identifier ' + dbId +
21
+ ' --query "DBInstances[0].Endpoint.Address" --output text', region);
22
+ if (!result || result === 'None' || result === 'null')
23
+ return null;
24
+ return result.replace(/"/g, '');
25
+ }
26
+ /**
27
+ * Check if AWS is configured for this project
28
+ */
29
+ function isAwsConfigured(config) {
30
+ if (config.aws)
31
+ return true;
32
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
33
+ const { extractEnvironments } = require('../../../../utils/config-helpers.js');
34
+ const environments = extractEnvironments(config);
35
+ return Object.values(environments).some((e) => e.pipeline === 'aws');
36
+ }
37
+ /**
38
+ * Get prod environment config
39
+ */
40
+ function getProdEnv(config) {
41
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
42
+ const { extractEnvironments } = require('../../../../utils/config-helpers.js');
43
+ const environments = extractEnvironments(config);
44
+ return environments.prod ?? environments.production ?? null;
45
+ }
46
+ exports.dbReplicationFixes = [
47
+ {
48
+ id: 'aws-rds-ec2-pg-client-missing',
49
+ stage: 'prod',
50
+ severity: 'warning',
51
+ description: 'PostgreSQL client not installed on EC2 (needed for DB sync)',
52
+ scan: async (config) => {
53
+ if (!isAwsConfigured(config))
54
+ return false;
55
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
56
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
57
+ // Only check if RDS exists
58
+ const endpoint = findRdsEndpoint(projectName, region);
59
+ if (!endpoint)
60
+ return false;
61
+ // Check if pg_dump is available on EC2 via SSH
62
+ const prodEnv = getProdEnv(config);
63
+ if (!prodEnv?.domain || prodEnv.domain.startsWith('EXAMPLE-'))
64
+ return false;
65
+ try {
66
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
67
+ const { sshExec } = require('../../../../utils/ssh-helper.js');
68
+ const result = await sshExec(prodEnv, 'which pg_dump 2>/dev/null || echo "not_found"');
69
+ return result.trim() === 'not_found';
70
+ }
71
+ catch {
72
+ return false; // Can't SSH — skip
73
+ }
74
+ },
75
+ fix: async (config) => {
76
+ const prodEnv = getProdEnv(config);
77
+ if (!prodEnv?.domain) {
78
+ console.log(' Production domain not configured');
79
+ return false;
80
+ }
81
+ try {
82
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
83
+ const { sshExec } = require('../../../../utils/ssh-helper.js');
84
+ console.log(' Installing PostgreSQL 15 client on EC2...');
85
+ await sshExec(prodEnv, 'sudo apt-get update -qq && sudo apt-get install -y postgresql-client-15');
86
+ console.log(' PostgreSQL client installed');
87
+ return true;
88
+ }
89
+ catch (e) {
90
+ console.log(' Failed to install pg client: ' + (e instanceof Error ? e.message : String(e)));
91
+ return false;
92
+ }
93
+ },
94
+ manualFix: 'SSH to EC2 and run: sudo apt-get install -y postgresql-client-15',
95
+ },
96
+ {
97
+ id: 'aws-rds-connectivity',
98
+ stage: 'prod',
99
+ severity: 'critical',
100
+ description: 'EC2 cannot connect to RDS (check security groups)',
101
+ scan: async (config) => {
102
+ if (!isAwsConfigured(config))
103
+ return false;
104
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
105
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
106
+ const endpoint = findRdsEndpoint(projectName, region);
107
+ if (!endpoint)
108
+ return false;
109
+ const prodEnv = getProdEnv(config);
110
+ if (!prodEnv?.domain || prodEnv.domain.startsWith('EXAMPLE-'))
111
+ return false;
112
+ try {
113
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
114
+ const { sshExec } = require('../../../../utils/ssh-helper.js');
115
+ // Check if pg_isready is available first
116
+ const hasPg = await sshExec(prodEnv, 'which pg_isready 2>/dev/null || echo "not_found"');
117
+ if (hasPg.trim() === 'not_found')
118
+ return false; // Can't test without pg client
119
+ const result = await sshExec(prodEnv, 'pg_isready -h ' + endpoint + ' -p 5432 2>&1');
120
+ return !result.includes('accepting connections');
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ },
126
+ fix: null,
127
+ manualFix: [
128
+ 'EC2 cannot reach RDS. Check:',
129
+ '1. RDS security group allows port 5432 from EC2 security group',
130
+ '2. RDS is in the same VPC as EC2',
131
+ '3. RDS instance status is "available"',
132
+ '4. Test: ssh to EC2, run: pg_isready -h <rds-endpoint> -p 5432',
133
+ ].join('\n'),
134
+ },
135
+ ];
136
+ //# sourceMappingURL=db-replication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-replication.js","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/db-replication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AWS EC2 Fixes
3
+ *
4
+ * Provisions EC2 key pair, instance, and Elastic IP.
5
+ * Uses Ubuntu 22.04 AMI, t2.micro (free tier), public subnet.
6
+ * Key pair private key is stored in Ansible Vault.
7
+ */
8
+ import type { Fix } from '../../../../types/index.js';
9
+ export declare const ec2Fixes: Fix[];
10
+ //# sourceMappingURL=ec2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ec2.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/ec2.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,4BAA4B,CAAC;AAmGrE,eAAO,MAAM,QAAQ,EAAE,GAAG,EA2LzB,CAAC"}
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ /**
3
+ * AWS EC2 Fixes
4
+ *
5
+ * Provisions EC2 key pair, instance, and Elastic IP.
6
+ * Uses Ubuntu 22.04 AMI, t2.micro (free tier), public subnet.
7
+ * Key pair private key is stored in Ansible Vault.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.ec2Fixes = void 0;
44
+ const aws_helpers_js_1 = require("../utils/aws-helpers.js");
45
+ /**
46
+ * Find VPC by factiii:project tag
47
+ */
48
+ function findVpc(projectName, region) {
49
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-vpcs --filters "Name=tag:factiii:project,Values=' + projectName + '" --query "Vpcs[0].VpcId" --output text', region);
50
+ if (!result || result === 'None' || result === 'null')
51
+ return null;
52
+ return result.replace(/"/g, '');
53
+ }
54
+ /**
55
+ * Find subnet by tag and type
56
+ */
57
+ function findSubnet(projectName, region, type) {
58
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-subnets --filters "Name=tag:factiii:project,Values=' + projectName + '" "Name=tag:factiii:subnet-type,Values=' + type + '" --query "Subnets[0].SubnetId" --output text', region);
59
+ if (!result || result === 'None' || result === 'null')
60
+ return null;
61
+ return result.replace(/"/g, '');
62
+ }
63
+ /**
64
+ * Find security group by name and VPC
65
+ */
66
+ function findSecurityGroup(groupName, vpcId, region) {
67
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-security-groups --filters "Name=group-name,Values=' + groupName + '" "Name=vpc-id,Values=' + vpcId + '" --query "SecurityGroups[0].GroupId" --output text', region);
68
+ if (!result || result === 'None' || result === 'null')
69
+ return null;
70
+ return result.replace(/"/g, '');
71
+ }
72
+ /**
73
+ * Find EC2 key pair by name
74
+ */
75
+ function findKeyPair(keyName, region) {
76
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-key-pairs --key-names ' + keyName + ' --query "KeyPairs[0].KeyPairId" --output text', region);
77
+ return !!result && result !== 'None' && result !== 'null';
78
+ }
79
+ /**
80
+ * Find running EC2 instance by tag
81
+ */
82
+ function findInstance(projectName, region) {
83
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-instances --filters "Name=tag:factiii:project,Values=' + projectName + '" "Name=instance-state-name,Values=running,stopped" --query "Reservations[0].Instances[0].InstanceId" --output text', region);
84
+ if (!result || result === 'None' || result === 'null')
85
+ return null;
86
+ return result.replace(/"/g, '');
87
+ }
88
+ /**
89
+ * Find Elastic IP associated with an instance
90
+ */
91
+ function findElasticIp(instanceId, region) {
92
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-addresses --filters "Name=instance-id,Values=' + instanceId + '" --query "Addresses[0].PublicIp" --output text', region);
93
+ if (!result || result === 'None' || result === 'null')
94
+ return null;
95
+ return result.replace(/"/g, '');
96
+ }
97
+ /**
98
+ * Get latest Ubuntu 22.04 AMI for the region
99
+ */
100
+ function getUbuntuAmi(region) {
101
+ const result = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-images --owners 099720109477 --filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" "Name=state,Values=available" --query "sort_by(Images, &CreationDate)[-1].ImageId" --output text', region);
102
+ if (!result || result === 'None' || result === 'null')
103
+ return null;
104
+ return result.replace(/"/g, '');
105
+ }
106
+ /**
107
+ * Check if AWS is configured for this project
108
+ */
109
+ function isAwsConfigured(config) {
110
+ if (config.aws)
111
+ return true;
112
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
113
+ const { extractEnvironments } = require('../../../../utils/config-helpers.js');
114
+ const environments = extractEnvironments(config);
115
+ return Object.values(environments).some((e) => e.pipeline === 'aws');
116
+ }
117
+ exports.ec2Fixes = [
118
+ {
119
+ id: 'aws-keypair-missing',
120
+ stage: 'prod',
121
+ severity: 'critical',
122
+ description: 'EC2 key pair not created for SSH access',
123
+ scan: async (config) => {
124
+ if (!isAwsConfigured(config))
125
+ return false;
126
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
127
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
128
+ return !findKeyPair('factiii-' + projectName, region);
129
+ },
130
+ fix: async (config) => {
131
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
132
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
133
+ const keyName = 'factiii-' + projectName;
134
+ try {
135
+ // Create key pair — AWS returns the private key material
136
+ const result = (0, aws_helpers_js_1.awsExec)('aws ec2 create-key-pair --key-name ' + keyName + ' --key-type ed25519 --query "KeyMaterial" --output text', region);
137
+ // Save private key to ~/.ssh/prod_deploy_key
138
+ const os = await Promise.resolve().then(() => __importStar(require('os')));
139
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
140
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
141
+ const sshDir = path.join(os.homedir(), '.ssh');
142
+ if (!fs.existsSync(sshDir)) {
143
+ fs.mkdirSync(sshDir, { mode: 0o700 });
144
+ }
145
+ const keyPath = path.join(sshDir, 'prod_deploy_key');
146
+ fs.writeFileSync(keyPath, result + '\n', { mode: 0o600 });
147
+ console.log(' Created key pair: ' + keyName);
148
+ console.log(' Private key saved to: ' + keyPath);
149
+ // Store in Ansible Vault if configured
150
+ if (config.ansible?.vault_path) {
151
+ console.log(' TIP: Add this key to Ansible Vault with: npx factiii secrets edit');
152
+ }
153
+ return true;
154
+ }
155
+ catch (e) {
156
+ console.log(' Failed to create key pair: ' + (e instanceof Error ? e.message : String(e)));
157
+ return false;
158
+ }
159
+ },
160
+ manualFix: 'Create key pair: aws ec2 create-key-pair --key-name factiii-{name} --key-type ed25519',
161
+ },
162
+ {
163
+ id: 'aws-ec2-instance-missing',
164
+ stage: 'prod',
165
+ severity: 'critical',
166
+ description: 'EC2 instance not created (Ubuntu 22.04, t2.micro)',
167
+ scan: async (config) => {
168
+ if (!isAwsConfigured(config))
169
+ return false;
170
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
171
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
172
+ return !findInstance(projectName, region);
173
+ },
174
+ fix: async (config) => {
175
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
176
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
177
+ const vpcId = findVpc(projectName, region);
178
+ if (!vpcId) {
179
+ console.log(' VPC must be created first');
180
+ return false;
181
+ }
182
+ const publicSubnet = findSubnet(projectName, region, 'public');
183
+ if (!publicSubnet) {
184
+ console.log(' Public subnet must be created first');
185
+ return false;
186
+ }
187
+ const ec2SgId = findSecurityGroup('factiii-' + projectName + '-ec2', vpcId, region);
188
+ if (!ec2SgId) {
189
+ console.log(' EC2 security group must be created first');
190
+ return false;
191
+ }
192
+ const keyName = 'factiii-' + projectName;
193
+ if (!findKeyPair(keyName, region)) {
194
+ console.log(' Key pair must be created first');
195
+ return false;
196
+ }
197
+ try {
198
+ // Get latest Ubuntu 22.04 AMI
199
+ const amiId = getUbuntuAmi(region);
200
+ if (!amiId) {
201
+ console.log(' Failed to find Ubuntu 22.04 AMI for region ' + region);
202
+ return false;
203
+ }
204
+ console.log(' Using AMI: ' + amiId);
205
+ // Launch instance
206
+ const instanceResult = (0, aws_helpers_js_1.awsExec)('aws ec2 run-instances' +
207
+ ' --image-id ' + amiId +
208
+ ' --instance-type t2.micro' +
209
+ ' --key-name ' + keyName +
210
+ ' --security-group-ids ' + ec2SgId +
211
+ ' --subnet-id ' + publicSubnet +
212
+ ' --count 1' +
213
+ ' ' + (0, aws_helpers_js_1.tagSpec)('instance', projectName), region);
214
+ const instanceId = JSON.parse(instanceResult).Instances[0].InstanceId;
215
+ console.log(' Launched EC2 instance: ' + instanceId);
216
+ console.log(' Instance type: t2.micro (free tier eligible)');
217
+ console.log(' Waiting for instance to be running...');
218
+ // Wait for instance to be running
219
+ (0, aws_helpers_js_1.awsExec)('aws ec2 wait instance-running --instance-ids ' + instanceId, region);
220
+ // Get public IP
221
+ const ipResult = (0, aws_helpers_js_1.awsExecSafe)('aws ec2 describe-instances --instance-ids ' + instanceId + ' --query "Reservations[0].Instances[0].PublicIpAddress" --output text', region);
222
+ if (ipResult && ipResult !== 'None') {
223
+ console.log(' Public IP: ' + ipResult.replace(/"/g, ''));
224
+ console.log(' NOTE: This IP will change on restart. Run fix again for Elastic IP.');
225
+ }
226
+ return true;
227
+ }
228
+ catch (e) {
229
+ console.log(' Failed to launch EC2 instance: ' + (e instanceof Error ? e.message : String(e)));
230
+ return false;
231
+ }
232
+ },
233
+ manualFix: 'Launch EC2: aws ec2 run-instances --image-id <ubuntu-ami> --instance-type t2.micro --key-name factiii-{name}',
234
+ },
235
+ {
236
+ id: 'aws-ec2-elastic-ip',
237
+ stage: 'prod',
238
+ severity: 'warning',
239
+ description: 'Elastic IP not assigned to EC2 instance (IP changes on restart)',
240
+ scan: async (config) => {
241
+ if (!isAwsConfigured(config))
242
+ return false;
243
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
244
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
245
+ const instanceId = findInstance(projectName, region);
246
+ if (!instanceId)
247
+ return false; // Instance must exist first
248
+ return !findElasticIp(instanceId, region);
249
+ },
250
+ fix: async (config) => {
251
+ const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
252
+ const projectName = (0, aws_helpers_js_1.getProjectName)(config);
253
+ const instanceId = findInstance(projectName, region);
254
+ if (!instanceId) {
255
+ console.log(' EC2 instance must be created first');
256
+ return false;
257
+ }
258
+ try {
259
+ // Allocate Elastic IP
260
+ const eipResult = (0, aws_helpers_js_1.awsExec)('aws ec2 allocate-address --domain vpc ' + (0, aws_helpers_js_1.tagSpec)('elastic-ip', projectName), region);
261
+ const parsed = JSON.parse(eipResult);
262
+ const allocationId = parsed.AllocationId;
263
+ const publicIp = parsed.PublicIp;
264
+ console.log(' Allocated Elastic IP: ' + publicIp);
265
+ // Associate with instance
266
+ (0, aws_helpers_js_1.awsExec)('aws ec2 associate-address --allocation-id ' + allocationId + ' --instance-id ' + instanceId, region);
267
+ console.log(' Associated with instance: ' + instanceId);
268
+ console.log(' Update factiii.yml prod.domain to: ' + publicIp);
269
+ return true;
270
+ }
271
+ catch (e) {
272
+ console.log(' Failed to allocate Elastic IP: ' + (e instanceof Error ? e.message : String(e)));
273
+ return false;
274
+ }
275
+ },
276
+ manualFix: 'Allocate Elastic IP and associate with EC2 instance',
277
+ },
278
+ ];
279
+ //# sourceMappingURL=ec2.js.map