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