@factiii/stack 0.1.2 → 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.
- package/bin/factiii +13 -0
- package/dist/cli/pr-check.d.ts +24 -0
- package/dist/cli/pr-check.d.ts.map +1 -0
- package/dist/cli/pr-check.js +153 -0
- package/dist/cli/pr-check.js.map +1 -0
- package/dist/plugins/addons/server-mode/index.d.ts.map +1 -1
- package/dist/plugins/addons/server-mode/index.js +3 -0
- package/dist/plugins/addons/server-mode/index.js.map +1 -1
- package/dist/plugins/addons/server-mode/scanfix/mac.d.ts +20 -3
- package/dist/plugins/addons/server-mode/scanfix/mac.d.ts.map +1 -1
- package/dist/plugins/addons/server-mode/scanfix/mac.js +304 -177
- package/dist/plugins/addons/server-mode/scanfix/mac.js.map +1 -1
- package/dist/plugins/addons/server-mode/scanfix/tart.d.ts +19 -0
- package/dist/plugins/addons/server-mode/scanfix/tart.d.ts.map +1 -0
- package/dist/plugins/addons/server-mode/scanfix/tart.js +350 -0
- package/dist/plugins/addons/server-mode/scanfix/tart.js.map +1 -0
- package/dist/plugins/pipelines/aws/configs/free-tier.d.ts.map +1 -1
- package/dist/plugins/pipelines/aws/configs/free-tier.js +3 -38
- package/dist/plugins/pipelines/aws/configs/free-tier.js.map +1 -1
- package/dist/plugins/pipelines/aws/index.d.ts +4 -1
- package/dist/plugins/pipelines/aws/index.d.ts.map +1 -1
- package/dist/plugins/pipelines/aws/index.js +101 -29
- package/dist/plugins/pipelines/aws/index.js.map +1 -1
- package/dist/plugins/pipelines/aws/scanfix/credentials.d.ts +9 -0
- package/dist/plugins/pipelines/aws/scanfix/credentials.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/credentials.js +196 -0
- package/dist/plugins/pipelines/aws/scanfix/credentials.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/db-replication.d.ts +13 -0
- package/dist/plugins/pipelines/aws/scanfix/db-replication.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/db-replication.js +136 -0
- package/dist/plugins/pipelines/aws/scanfix/db-replication.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ec2.d.ts +10 -0
- package/dist/plugins/pipelines/aws/scanfix/ec2.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ec2.js +279 -0
- package/dist/plugins/pipelines/aws/scanfix/ec2.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ecr.d.ts +9 -0
- package/dist/plugins/pipelines/aws/scanfix/ecr.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ecr.js +100 -0
- package/dist/plugins/pipelines/aws/scanfix/ecr.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/iam.d.ts +10 -0
- package/dist/plugins/pipelines/aws/scanfix/iam.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/iam.js +255 -0
- package/dist/plugins/pipelines/aws/scanfix/iam.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/rds.d.ts +10 -0
- package/dist/plugins/pipelines/aws/scanfix/rds.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/rds.js +261 -0
- package/dist/plugins/pipelines/aws/scanfix/rds.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/s3.d.ts +9 -0
- package/dist/plugins/pipelines/aws/scanfix/s3.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/s3.js +134 -0
- package/dist/plugins/pipelines/aws/scanfix/s3.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/security-groups.d.ts +10 -0
- package/dist/plugins/pipelines/aws/scanfix/security-groups.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/security-groups.js +225 -0
- package/dist/plugins/pipelines/aws/scanfix/security-groups.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ses.d.ts +9 -0
- package/dist/plugins/pipelines/aws/scanfix/ses.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/ses.js +174 -0
- package/dist/plugins/pipelines/aws/scanfix/ses.js.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/vpc.d.ts +9 -0
- package/dist/plugins/pipelines/aws/scanfix/vpc.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/scanfix/vpc.js +237 -0
- package/dist/plugins/pipelines/aws/scanfix/vpc.js.map +1 -0
- package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts +50 -0
- package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts.map +1 -0
- package/dist/plugins/pipelines/aws/utils/aws-helpers.js +137 -0
- package/dist/plugins/pipelines/aws/utils/aws-helpers.js.map +1 -0
- package/dist/plugins/pipelines/factiii/index.d.ts.map +1 -1
- package/dist/plugins/pipelines/factiii/index.js +11 -0
- package/dist/plugins/pipelines/factiii/index.js.map +1 -1
- package/dist/plugins/pipelines/factiii/pr-check.d.ts +35 -0
- package/dist/plugins/pipelines/factiii/pr-check.d.ts.map +1 -0
- package/dist/plugins/pipelines/factiii/pr-check.js +202 -0
- package/dist/plugins/pipelines/factiii/pr-check.js.map +1 -0
- package/dist/plugins/pipelines/factiii/utils/workflows.d.ts.map +1 -1
- package/dist/plugins/pipelines/factiii/utils/workflows.js +1 -0
- package/dist/plugins/pipelines/factiii/utils/workflows.js.map +1 -1
- package/dist/plugins/pipelines/factiii/workflows/factiii-cicd-staging.yml +8 -3
- package/dist/plugins/pipelines/factiii/workflows/factiii-pr-check.yml +103 -0
- package/dist/plugins/servers/mac/staging.d.ts.map +1 -1
- package/dist/plugins/servers/mac/staging.js +304 -52
- package/dist/plugins/servers/mac/staging.js.map +1 -1
- package/dist/types/config.d.ts +11 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/github-status.d.ts +39 -0
- package/dist/utils/github-status.d.ts.map +1 -0
- package/dist/utils/github-status.js +172 -0
- package/dist/utils/github-status.js.map +1 -0
- package/package.json +3 -3
|
@@ -5,13 +5,30 @@
|
|
|
5
5
|
* Fixes for configuring Mac as a deployment server HOST (no Docker/dev tools).
|
|
6
6
|
* Focus: what makes a Mac reliable as a server.
|
|
7
7
|
*
|
|
8
|
+
* Power Management (pmset):
|
|
8
9
|
* - Disable sleep (system, disk, display)
|
|
9
10
|
* - Auto-restart on power loss
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
11
|
+
* - Fully disable sleep (disablesleep)
|
|
12
|
+
* - Wake on LAN (womp)
|
|
13
|
+
* - Disable hibernate
|
|
14
|
+
* - Disable standby
|
|
15
|
+
* - Disable Power Nap
|
|
16
|
+
* - Disable proximity wake
|
|
17
|
+
* - Keep awake during SSH (ttyskeepawake)
|
|
18
|
+
*
|
|
19
|
+
* System Services:
|
|
20
|
+
* - Disable screensaver (-currentHost)
|
|
12
21
|
* - Enable SSH (Remote Login)
|
|
13
22
|
* - Disable App Nap
|
|
14
|
-
* -
|
|
23
|
+
* - Disable Spotlight indexing
|
|
24
|
+
* - Disable Time Machine
|
|
25
|
+
* - Disable auto-update restart
|
|
26
|
+
* - Disable Bluetooth (manual)
|
|
27
|
+
* - Auto-login on boot (manual)
|
|
28
|
+
*
|
|
29
|
+
* Network/Security:
|
|
30
|
+
* - Enable NTP (network time)
|
|
31
|
+
* - Disable file sharing (manual)
|
|
15
32
|
*/
|
|
16
33
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
34
|
if (k2 === undefined) k2 = k;
|
|
@@ -50,27 +67,57 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
50
67
|
exports.macFixes = void 0;
|
|
51
68
|
const child_process_1 = require("child_process");
|
|
52
69
|
const fs = __importStar(require("fs"));
|
|
70
|
+
function macFixPair(def) {
|
|
71
|
+
return ['staging', 'prod'].map(stage => ({
|
|
72
|
+
id: def.idBase + '-' + stage,
|
|
73
|
+
stage,
|
|
74
|
+
os: 'mac',
|
|
75
|
+
severity: def.severity,
|
|
76
|
+
description: def.description,
|
|
77
|
+
scan: def.scan,
|
|
78
|
+
fix: def.fix,
|
|
79
|
+
manualFix: def.manualFix,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
// ============================================================
|
|
83
|
+
// Helper: check a pmset value
|
|
84
|
+
// ============================================================
|
|
85
|
+
function pmsetValueIs(key, expected) {
|
|
86
|
+
try {
|
|
87
|
+
const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i ' + key + ' || true', {
|
|
88
|
+
encoding: 'utf8',
|
|
89
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
90
|
+
});
|
|
91
|
+
// If key not found in pmset output, the setting doesn't exist on this hardware (e.g. desktop Macs
|
|
92
|
+
// don't have hibernatemode, disablesleep, proximitywake). Treat as not applicable = OK.
|
|
93
|
+
if (!result.trim())
|
|
94
|
+
return true;
|
|
95
|
+
return result.trim().includes(expected);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
53
101
|
exports.macFixes = [
|
|
54
102
|
// ============================================================
|
|
55
|
-
//
|
|
103
|
+
// POWER MANAGEMENT (pmset)
|
|
56
104
|
// ============================================================
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
stage: 'staging',
|
|
60
|
-
os: 'mac',
|
|
105
|
+
...macFixPair({
|
|
106
|
+
idBase: 'macos-sleep-enabled',
|
|
61
107
|
severity: 'warning',
|
|
62
108
|
description: 'macOS sleep is enabled (server may go offline)',
|
|
63
109
|
scan: async (_config, _rootDir) => {
|
|
64
110
|
try {
|
|
65
|
-
const result = (0, child_process_1.execSync)('pmset -g | grep -E "^\\s*sleep\\s+"', {
|
|
111
|
+
const result = (0, child_process_1.execSync)('pmset -g | grep -E "^\\s*sleep\\s+" || true', {
|
|
66
112
|
encoding: 'utf8',
|
|
67
113
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
68
114
|
});
|
|
69
|
-
|
|
115
|
+
if (!result.trim())
|
|
116
|
+
return false; // Key not found = not applicable
|
|
70
117
|
return !result.includes('sleep 0');
|
|
71
118
|
}
|
|
72
119
|
catch {
|
|
73
|
-
return false;
|
|
120
|
+
return false;
|
|
74
121
|
}
|
|
75
122
|
},
|
|
76
123
|
fix: async (_config, _rootDir) => {
|
|
@@ -84,139 +131,119 @@ exports.macFixes = [
|
|
|
84
131
|
}
|
|
85
132
|
},
|
|
86
133
|
manualFix: 'Run: sudo pmset -a sleep 0 disksleep 0 displaysleep 0',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
severity: 'info',
|
|
93
|
-
description: 'macOS screensaver is enabled',
|
|
134
|
+
}),
|
|
135
|
+
...macFixPair({
|
|
136
|
+
idBase: 'macos-autorestart-disabled',
|
|
137
|
+
severity: 'critical',
|
|
138
|
+
description: 'Auto-restart on power loss is disabled (server may stay off after outage)',
|
|
94
139
|
scan: async (_config, _rootDir) => {
|
|
95
140
|
try {
|
|
96
|
-
const result = (0, child_process_1.execSync)('
|
|
141
|
+
const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i autorestart || true', {
|
|
97
142
|
encoding: 'utf8',
|
|
143
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
98
144
|
});
|
|
99
|
-
|
|
100
|
-
|
|
145
|
+
if (!result.trim())
|
|
146
|
+
return false; // Key not found = not applicable
|
|
147
|
+
return !result.trim().includes('1');
|
|
101
148
|
}
|
|
102
149
|
catch {
|
|
103
|
-
return
|
|
150
|
+
return true;
|
|
104
151
|
}
|
|
105
152
|
},
|
|
106
153
|
fix: async (_config, _rootDir) => {
|
|
107
154
|
try {
|
|
108
|
-
console.log('
|
|
109
|
-
(0, child_process_1.execSync)('
|
|
155
|
+
console.log(' Enabling auto-restart on power loss...');
|
|
156
|
+
(0, child_process_1.execSync)('sudo pmset -a autorestart 1', { stdio: 'inherit' });
|
|
110
157
|
return true;
|
|
111
158
|
}
|
|
112
159
|
catch {
|
|
113
160
|
return false;
|
|
114
161
|
}
|
|
115
162
|
},
|
|
116
|
-
manualFix: 'Run:
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
severity: 'critical',
|
|
123
|
-
description: 'macOS Remote Login (SSH) is disabled',
|
|
163
|
+
manualFix: 'Run: sudo pmset -a autorestart 1',
|
|
164
|
+
}),
|
|
165
|
+
...macFixPair({
|
|
166
|
+
idBase: 'macos-disablesleep-disabled',
|
|
167
|
+
severity: 'warning',
|
|
168
|
+
description: 'System sleep is not fully disabled (disablesleep=0 allows Sleep menu)',
|
|
124
169
|
scan: async (_config, _rootDir) => {
|
|
125
170
|
try {
|
|
126
|
-
const result = (0, child_process_1.execSync)('
|
|
171
|
+
const result = (0, child_process_1.execSync)('pmset -g custom 2>/dev/null | grep -i disablesleep || true', {
|
|
127
172
|
encoding: 'utf8',
|
|
173
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
128
174
|
});
|
|
129
|
-
|
|
175
|
+
if (!result.trim())
|
|
176
|
+
return false; // Key not found on desktop Macs = not applicable
|
|
177
|
+
return !result.trim().includes('1');
|
|
130
178
|
}
|
|
131
179
|
catch {
|
|
132
|
-
return
|
|
180
|
+
return true;
|
|
133
181
|
}
|
|
134
182
|
},
|
|
135
183
|
fix: async (_config, _rootDir) => {
|
|
136
184
|
try {
|
|
137
|
-
console.log('
|
|
138
|
-
(0, child_process_1.execSync)('sudo
|
|
185
|
+
console.log(' Disabling system sleep (server mode)...');
|
|
186
|
+
(0, child_process_1.execSync)('sudo pmset -a disablesleep 1', { stdio: 'inherit' });
|
|
139
187
|
return true;
|
|
140
188
|
}
|
|
141
189
|
catch {
|
|
142
190
|
return false;
|
|
143
191
|
}
|
|
144
192
|
},
|
|
145
|
-
manualFix: 'Run: sudo
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
severity: 'info',
|
|
152
|
-
description: 'macOS App Nap may pause background processes',
|
|
193
|
+
manualFix: 'Run: sudo pmset -a disablesleep 1',
|
|
194
|
+
}),
|
|
195
|
+
...macFixPair({
|
|
196
|
+
idBase: 'macos-wake-on-lan',
|
|
197
|
+
severity: 'warning',
|
|
198
|
+
description: 'Wake on LAN is disabled (cannot remotely wake server via magic packet)',
|
|
153
199
|
scan: async (_config, _rootDir) => {
|
|
154
|
-
|
|
155
|
-
const result = (0, child_process_1.execSync)('defaults read NSGlobalDomain NSAppSleepDisabled 2>/dev/null || echo "0"', {
|
|
156
|
-
encoding: 'utf8',
|
|
157
|
-
});
|
|
158
|
-
return result.trim() !== '1';
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
return true; // If can't read, assume App Nap is enabled
|
|
162
|
-
}
|
|
200
|
+
return !pmsetValueIs('womp', '1');
|
|
163
201
|
},
|
|
164
202
|
fix: async (_config, _rootDir) => {
|
|
165
203
|
try {
|
|
166
|
-
console.log('
|
|
167
|
-
(0, child_process_1.execSync)('
|
|
204
|
+
console.log(' Enabling Wake on LAN...');
|
|
205
|
+
(0, child_process_1.execSync)('sudo pmset -a womp 1', { stdio: 'inherit' });
|
|
168
206
|
return true;
|
|
169
207
|
}
|
|
170
208
|
catch {
|
|
171
209
|
return false;
|
|
172
210
|
}
|
|
173
211
|
},
|
|
174
|
-
manualFix: 'Run:
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
stage: 'staging',
|
|
179
|
-
os: 'mac',
|
|
212
|
+
manualFix: 'Run: sudo pmset -a womp 1 (requires Ethernet and compatible hardware)',
|
|
213
|
+
}),
|
|
214
|
+
...macFixPair({
|
|
215
|
+
idBase: 'macos-hibernate-enabled',
|
|
180
216
|
severity: 'warning',
|
|
181
|
-
description: '
|
|
217
|
+
description: 'Hibernate mode is enabled (server writes RAM to disk and powers off)',
|
|
182
218
|
scan: async (_config, _rootDir) => {
|
|
183
|
-
|
|
184
|
-
const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i autorestart || true', {
|
|
185
|
-
encoding: 'utf8',
|
|
186
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
187
|
-
});
|
|
188
|
-
// Problem if autorestart is 0 or line doesn't contain 1
|
|
189
|
-
return !result.trim().includes('1');
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
return true; // Assume needs fix if we can't read
|
|
193
|
-
}
|
|
219
|
+
return !pmsetValueIs('hibernatemode', '0');
|
|
194
220
|
},
|
|
195
221
|
fix: async (_config, _rootDir) => {
|
|
196
222
|
try {
|
|
197
|
-
console.log('
|
|
198
|
-
(0, child_process_1.execSync)('sudo pmset -a
|
|
223
|
+
console.log(' Disabling hibernate mode...');
|
|
224
|
+
(0, child_process_1.execSync)('sudo pmset -a hibernatemode 0', { stdio: 'inherit' });
|
|
199
225
|
return true;
|
|
200
226
|
}
|
|
201
227
|
catch {
|
|
202
228
|
return false;
|
|
203
229
|
}
|
|
204
230
|
},
|
|
205
|
-
manualFix: 'Run: sudo pmset -a
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
severity: 'info',
|
|
212
|
-
description: 'System sleep is not fully disabled (disablesleep=0 allows Sleep menu)',
|
|
231
|
+
manualFix: 'Run: sudo pmset -a hibernatemode 0',
|
|
232
|
+
}),
|
|
233
|
+
...macFixPair({
|
|
234
|
+
idBase: 'macos-standby-enabled',
|
|
235
|
+
severity: 'warning',
|
|
236
|
+
description: 'Standby mode is enabled (server enters deep sleep after extended idle)',
|
|
213
237
|
scan: async (_config, _rootDir) => {
|
|
214
238
|
try {
|
|
215
|
-
|
|
239
|
+
// Use 'standby ' with trailing space to avoid matching standbydelayhigh/standbydelaylow
|
|
240
|
+
const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -E "^\\s*standby\\s+" || true', {
|
|
216
241
|
encoding: 'utf8',
|
|
217
242
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
218
243
|
});
|
|
219
|
-
|
|
244
|
+
if (!result.trim())
|
|
245
|
+
return false; // Key not found on desktop Macs = not applicable
|
|
246
|
+
return !result.trim().includes('0');
|
|
220
247
|
}
|
|
221
248
|
catch {
|
|
222
249
|
return true;
|
|
@@ -224,77 +251,78 @@ exports.macFixes = [
|
|
|
224
251
|
},
|
|
225
252
|
fix: async (_config, _rootDir) => {
|
|
226
253
|
try {
|
|
227
|
-
console.log(' Disabling
|
|
228
|
-
(0, child_process_1.execSync)('sudo pmset -a
|
|
254
|
+
console.log(' Disabling standby mode...');
|
|
255
|
+
(0, child_process_1.execSync)('sudo pmset -a standby 0', { stdio: 'inherit' });
|
|
229
256
|
return true;
|
|
230
257
|
}
|
|
231
258
|
catch {
|
|
232
259
|
return false;
|
|
233
260
|
}
|
|
234
261
|
},
|
|
235
|
-
manualFix: 'Run: sudo pmset -a
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
|
|
239
|
-
stage: 'staging',
|
|
240
|
-
os: 'mac',
|
|
262
|
+
manualFix: 'Run: sudo pmset -a standby 0',
|
|
263
|
+
}),
|
|
264
|
+
...macFixPair({
|
|
265
|
+
idBase: 'macos-powernap-enabled',
|
|
241
266
|
severity: 'info',
|
|
242
|
-
description: '
|
|
267
|
+
description: 'Power Nap is enabled (wastes CPU for iCloud/Mail checks on server)',
|
|
243
268
|
scan: async (_config, _rootDir) => {
|
|
269
|
+
return !pmsetValueIs('powernap', '0');
|
|
270
|
+
},
|
|
271
|
+
fix: async (_config, _rootDir) => {
|
|
244
272
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const result = (0, child_process_1.execSync)(`defaults read ${plist} autoLoginUser 2>/dev/null || echo ""`, {
|
|
249
|
-
encoding: 'utf8',
|
|
250
|
-
});
|
|
251
|
-
return !result.trim();
|
|
273
|
+
console.log(' Disabling Power Nap...');
|
|
274
|
+
(0, child_process_1.execSync)('sudo pmset -a powernap 0', { stdio: 'inherit' });
|
|
275
|
+
return true;
|
|
252
276
|
}
|
|
253
277
|
catch {
|
|
254
|
-
return
|
|
278
|
+
return false;
|
|
255
279
|
}
|
|
256
280
|
},
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// ============================================================
|
|
264
|
-
{
|
|
265
|
-
id: 'macos-sleep-enabled-prod',
|
|
266
|
-
stage: 'prod',
|
|
267
|
-
os: 'mac',
|
|
268
|
-
severity: 'warning',
|
|
269
|
-
description: 'macOS sleep is enabled (server may go offline)',
|
|
281
|
+
manualFix: 'Run: sudo pmset -a powernap 0',
|
|
282
|
+
}),
|
|
283
|
+
...macFixPair({
|
|
284
|
+
idBase: 'macos-proximitywake-enabled',
|
|
285
|
+
severity: 'info',
|
|
286
|
+
description: 'Proximity wake is enabled (nearby Apple devices can wake the Mac)',
|
|
270
287
|
scan: async (_config, _rootDir) => {
|
|
288
|
+
return !pmsetValueIs('proximitywake', '0');
|
|
289
|
+
},
|
|
290
|
+
fix: async (_config, _rootDir) => {
|
|
271
291
|
try {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
276
|
-
return !result.includes('sleep 0');
|
|
292
|
+
console.log(' Disabling proximity wake...');
|
|
293
|
+
(0, child_process_1.execSync)('sudo pmset -a proximitywake 0', { stdio: 'inherit' });
|
|
294
|
+
return true;
|
|
277
295
|
}
|
|
278
296
|
catch {
|
|
279
297
|
return false;
|
|
280
298
|
}
|
|
281
299
|
},
|
|
300
|
+
manualFix: 'Run: sudo pmset -a proximitywake 0',
|
|
301
|
+
}),
|
|
302
|
+
...macFixPair({
|
|
303
|
+
idBase: 'macos-ttyskeepawake-disabled',
|
|
304
|
+
severity: 'warning',
|
|
305
|
+
description: 'TTY keep-awake is disabled (Mac may sleep during active SSH sessions)',
|
|
306
|
+
scan: async (_config, _rootDir) => {
|
|
307
|
+
return !pmsetValueIs('ttyskeepawake', '1');
|
|
308
|
+
},
|
|
282
309
|
fix: async (_config, _rootDir) => {
|
|
283
310
|
try {
|
|
284
|
-
console.log('
|
|
285
|
-
(0, child_process_1.execSync)('sudo pmset -a
|
|
311
|
+
console.log(' Enabling TTY keep-awake...');
|
|
312
|
+
(0, child_process_1.execSync)('sudo pmset -a ttyskeepawake 1', { stdio: 'inherit' });
|
|
286
313
|
return true;
|
|
287
314
|
}
|
|
288
315
|
catch {
|
|
289
316
|
return false;
|
|
290
317
|
}
|
|
291
318
|
},
|
|
292
|
-
manualFix: 'Run: sudo pmset -a
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
319
|
+
manualFix: 'Run: sudo pmset -a ttyskeepawake 1',
|
|
320
|
+
}),
|
|
321
|
+
// ============================================================
|
|
322
|
+
// SYSTEM SERVICES
|
|
323
|
+
// ============================================================
|
|
324
|
+
...macFixPair({
|
|
325
|
+
idBase: 'macos-ssh-disabled',
|
|
298
326
|
severity: 'critical',
|
|
299
327
|
description: 'macOS Remote Login (SSH) is disabled',
|
|
300
328
|
scan: async (_config, _rootDir) => {
|
|
@@ -319,19 +347,18 @@ exports.macFixes = [
|
|
|
319
347
|
}
|
|
320
348
|
},
|
|
321
349
|
manualFix: 'Run: sudo systemsetup -setremotelogin on',
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
|
|
325
|
-
stage: 'prod',
|
|
326
|
-
os: 'mac',
|
|
350
|
+
}),
|
|
351
|
+
...macFixPair({
|
|
352
|
+
idBase: 'macos-screensaver-enabled',
|
|
327
353
|
severity: 'info',
|
|
328
|
-
description: 'macOS screensaver is enabled',
|
|
354
|
+
description: 'macOS screensaver is enabled (wasted GPU cycles on headless host)',
|
|
329
355
|
scan: async (_config, _rootDir) => {
|
|
330
356
|
try {
|
|
331
|
-
const result = (0, child_process_1.execSync)('defaults read com.apple.screensaver idleTime 2>/dev/null || echo "300"', {
|
|
357
|
+
const result = (0, child_process_1.execSync)('defaults -currentHost read com.apple.screensaver idleTime 2>/dev/null || echo "300"', {
|
|
332
358
|
encoding: 'utf8',
|
|
333
359
|
});
|
|
334
|
-
|
|
360
|
+
const idleTime = parseInt(result.trim(), 10);
|
|
361
|
+
return idleTime > 0;
|
|
335
362
|
}
|
|
336
363
|
catch {
|
|
337
364
|
return false;
|
|
@@ -339,19 +366,18 @@ exports.macFixes = [
|
|
|
339
366
|
},
|
|
340
367
|
fix: async (_config, _rootDir) => {
|
|
341
368
|
try {
|
|
342
|
-
|
|
369
|
+
console.log(' Disabling macOS screensaver...');
|
|
370
|
+
(0, child_process_1.execSync)('defaults -currentHost write com.apple.screensaver idleTime 0', { stdio: 'inherit' });
|
|
343
371
|
return true;
|
|
344
372
|
}
|
|
345
373
|
catch {
|
|
346
374
|
return false;
|
|
347
375
|
}
|
|
348
376
|
},
|
|
349
|
-
manualFix: 'Run: defaults write com.apple.screensaver idleTime 0',
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
|
|
353
|
-
stage: 'prod',
|
|
354
|
-
os: 'mac',
|
|
377
|
+
manualFix: 'Run: defaults -currentHost write com.apple.screensaver idleTime 0',
|
|
378
|
+
}),
|
|
379
|
+
...macFixPair({
|
|
380
|
+
idBase: 'macos-app-nap-enabled',
|
|
355
381
|
severity: 'info',
|
|
356
382
|
description: 'macOS App Nap may pause background processes',
|
|
357
383
|
scan: async (_config, _rootDir) => {
|
|
@@ -367,6 +393,7 @@ exports.macFixes = [
|
|
|
367
393
|
},
|
|
368
394
|
fix: async (_config, _rootDir) => {
|
|
369
395
|
try {
|
|
396
|
+
console.log(' Disabling macOS App Nap...');
|
|
370
397
|
(0, child_process_1.execSync)('defaults write NSGlobalDomain NSAppSleepDisabled -bool YES', { stdio: 'inherit' });
|
|
371
398
|
return true;
|
|
372
399
|
}
|
|
@@ -375,77 +402,117 @@ exports.macFixes = [
|
|
|
375
402
|
}
|
|
376
403
|
},
|
|
377
404
|
manualFix: 'Run: defaults write NSGlobalDomain NSAppSleepDisabled -bool YES',
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
severity: 'warning',
|
|
384
|
-
description: 'Auto-restart on power loss is disabled',
|
|
405
|
+
}),
|
|
406
|
+
...macFixPair({
|
|
407
|
+
idBase: 'macos-spotlight-enabled',
|
|
408
|
+
severity: 'info',
|
|
409
|
+
description: 'Spotlight indexing is enabled (burns CPU/disk on headless server)',
|
|
385
410
|
scan: async (_config, _rootDir) => {
|
|
386
411
|
try {
|
|
387
|
-
const result = (0, child_process_1.execSync)('
|
|
412
|
+
const result = (0, child_process_1.execSync)('mdutil -s / 2>/dev/null', {
|
|
388
413
|
encoding: 'utf8',
|
|
389
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
390
414
|
});
|
|
391
|
-
return
|
|
415
|
+
return result.toLowerCase().includes('indexing enabled');
|
|
392
416
|
}
|
|
393
417
|
catch {
|
|
394
|
-
return
|
|
418
|
+
return false;
|
|
395
419
|
}
|
|
396
420
|
},
|
|
397
421
|
fix: async (_config, _rootDir) => {
|
|
398
422
|
try {
|
|
399
|
-
|
|
423
|
+
console.log(' Disabling Spotlight indexing...');
|
|
424
|
+
(0, child_process_1.execSync)('sudo mdutil -a -i off', { stdio: 'inherit' });
|
|
400
425
|
return true;
|
|
401
426
|
}
|
|
402
427
|
catch {
|
|
403
428
|
return false;
|
|
404
429
|
}
|
|
405
430
|
},
|
|
406
|
-
manualFix: 'Run: sudo
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
|
|
410
|
-
stage: 'prod',
|
|
411
|
-
os: 'mac',
|
|
431
|
+
manualFix: 'Run: sudo mdutil -a -i off',
|
|
432
|
+
}),
|
|
433
|
+
...macFixPair({
|
|
434
|
+
idBase: 'macos-timemachine-enabled',
|
|
412
435
|
severity: 'info',
|
|
413
|
-
description: '
|
|
436
|
+
description: 'Time Machine is enabled (causes periodic heavy disk I/O)',
|
|
414
437
|
scan: async (_config, _rootDir) => {
|
|
415
438
|
try {
|
|
416
|
-
const result = (0, child_process_1.execSync)('
|
|
439
|
+
const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.TimeMachine AutoBackup 2>/dev/null || echo "0"', {
|
|
417
440
|
encoding: 'utf8',
|
|
418
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
419
441
|
});
|
|
420
|
-
return
|
|
442
|
+
return result.trim() === '1';
|
|
421
443
|
}
|
|
422
444
|
catch {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
fix: async (_config, _rootDir) => {
|
|
449
|
+
try {
|
|
450
|
+
console.log(' Disabling Time Machine...');
|
|
451
|
+
(0, child_process_1.execSync)('sudo tmutil disable', { stdio: 'inherit' });
|
|
423
452
|
return true;
|
|
424
453
|
}
|
|
454
|
+
catch {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
manualFix: 'Run: sudo tmutil disable',
|
|
459
|
+
}),
|
|
460
|
+
...macFixPair({
|
|
461
|
+
idBase: 'macos-autoupdate-restart',
|
|
462
|
+
severity: 'critical',
|
|
463
|
+
description: 'macOS auto-update may reboot the server without warning',
|
|
464
|
+
scan: async (_config, _rootDir) => {
|
|
465
|
+
try {
|
|
466
|
+
const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates 2>/dev/null || echo "0"', {
|
|
467
|
+
encoding: 'utf8',
|
|
468
|
+
});
|
|
469
|
+
return result.trim() === '1';
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
425
474
|
},
|
|
426
475
|
fix: async (_config, _rootDir) => {
|
|
427
476
|
try {
|
|
428
|
-
|
|
477
|
+
console.log(' Disabling automatic macOS update installs...');
|
|
478
|
+
(0, child_process_1.execSync)('sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool false', { stdio: 'inherit' });
|
|
429
479
|
return true;
|
|
430
480
|
}
|
|
431
481
|
catch {
|
|
432
482
|
return false;
|
|
433
483
|
}
|
|
434
484
|
},
|
|
435
|
-
manualFix: 'Run: sudo
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
|
|
439
|
-
stage: 'prod',
|
|
440
|
-
os: 'mac',
|
|
485
|
+
manualFix: 'Run: sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool false',
|
|
486
|
+
}),
|
|
487
|
+
...macFixPair({
|
|
488
|
+
idBase: 'macos-bluetooth-enabled',
|
|
441
489
|
severity: 'info',
|
|
442
|
-
description: '
|
|
490
|
+
description: 'Bluetooth is enabled (unnecessary attack surface on headless server)',
|
|
491
|
+
scan: async (_config, _rootDir) => {
|
|
492
|
+
try {
|
|
493
|
+
const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null || echo "1"', {
|
|
494
|
+
encoding: 'utf8',
|
|
495
|
+
});
|
|
496
|
+
return result.trim() !== '0';
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
fix: null,
|
|
503
|
+
manualFix: 'Disable Bluetooth (only if no BT keyboard/mouse): System Settings > Bluetooth > Turn Off. ' +
|
|
504
|
+
'Or: sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0 && sudo killall -HUP bluetoothd',
|
|
505
|
+
}),
|
|
506
|
+
...macFixPair({
|
|
507
|
+
idBase: 'macos-autologin-disabled',
|
|
508
|
+
severity: 'info',
|
|
509
|
+
description: 'Auto-login on boot is not configured (may require manual login after power loss)',
|
|
443
510
|
scan: async (_config, _rootDir) => {
|
|
444
511
|
try {
|
|
445
512
|
const plist = '/Library/Preferences/com.apple.loginwindow.plist';
|
|
446
513
|
if (!fs.existsSync(plist))
|
|
447
514
|
return true;
|
|
448
|
-
const result = (0, child_process_1.execSync)(
|
|
515
|
+
const result = (0, child_process_1.execSync)('defaults read ' + plist + ' autoLoginUser 2>/dev/null || echo ""', {
|
|
449
516
|
encoding: 'utf8',
|
|
450
517
|
});
|
|
451
518
|
return !result.trim();
|
|
@@ -455,7 +522,67 @@ exports.macFixes = [
|
|
|
455
522
|
}
|
|
456
523
|
},
|
|
457
524
|
fix: null,
|
|
458
|
-
manualFix: 'System Settings > Users & Groups > Login Options > Automatic login
|
|
459
|
-
|
|
525
|
+
manualFix: 'Set auto-login: System Settings > Users & Groups > Login Options > Automatic login > select user. ' +
|
|
526
|
+
'Or: sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string "admin" (then reboot)',
|
|
527
|
+
}),
|
|
528
|
+
// ============================================================
|
|
529
|
+
// NETWORK / SECURITY
|
|
530
|
+
// ============================================================
|
|
531
|
+
...macFixPair({
|
|
532
|
+
idBase: 'macos-ntp-disabled',
|
|
533
|
+
severity: 'warning',
|
|
534
|
+
description: 'Network time (NTP) is disabled (accurate time needed for TLS, logs, cron)',
|
|
535
|
+
scan: async (_config, _rootDir) => {
|
|
536
|
+
try {
|
|
537
|
+
const result = (0, child_process_1.execSync)('sudo systemsetup -getusingnetworktime 2>/dev/null', {
|
|
538
|
+
encoding: 'utf8',
|
|
539
|
+
});
|
|
540
|
+
return result.toLowerCase().includes('off');
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
fix: async (_config, _rootDir) => {
|
|
547
|
+
try {
|
|
548
|
+
console.log(' Enabling network time (NTP)...');
|
|
549
|
+
(0, child_process_1.execSync)('sudo systemsetup -setusingnetworktime on', { stdio: 'inherit' });
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
manualFix: 'Run: sudo systemsetup -setusingnetworktime on',
|
|
557
|
+
}),
|
|
558
|
+
...macFixPair({
|
|
559
|
+
idBase: 'macos-file-sharing-enabled',
|
|
560
|
+
severity: 'info',
|
|
561
|
+
description: 'File sharing (AFP/SMB) is enabled (unnecessary attack surface on server)',
|
|
562
|
+
scan: async (_config, _rootDir) => {
|
|
563
|
+
try {
|
|
564
|
+
// Check if smbd is running (SMB file sharing)
|
|
565
|
+
(0, child_process_1.execSync)('launchctl list 2>/dev/null | grep com.apple.smbd', {
|
|
566
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
567
|
+
});
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
// smbd not running, check AFP
|
|
572
|
+
try {
|
|
573
|
+
const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.AppleFileServer guestAccess 2>/dev/null || echo "0"', {
|
|
574
|
+
encoding: 'utf8',
|
|
575
|
+
});
|
|
576
|
+
return result.trim() === '1';
|
|
577
|
+
}
|
|
578
|
+
catch {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
fix: null,
|
|
584
|
+
manualFix: 'Disable file sharing: System Settings > General > Sharing > File Sharing > Off. ' +
|
|
585
|
+
'Only disable if file sharing is not intentionally used.',
|
|
586
|
+
}),
|
|
460
587
|
];
|
|
461
588
|
//# sourceMappingURL=mac.js.map
|