@kubilaycakmak/projectfreeze 0.0.0-stage → 0.1.0-dev.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -2
- package/bin/projectfreeze.js +4 -0
- package/docs/interfaces.md +98 -0
- package/docs/operations.md +42 -0
- package/examples/demo.js +72 -0
- package/examples/stream-test.mjs +71 -0
- package/examples/workload.js +29 -0
- package/helpers/.gitkeep +0 -0
- package/helpers/projectfreeze-darwin-arm64 +0 -0
- package/helpers/projectfreeze-darwin-arm64.sha256 +1 -0
- package/helpers/projectfreeze-darwin-x64 +0 -0
- package/helpers/projectfreeze-darwin-x64.sha256 +1 -0
- package/helpers/projectfreeze-linux-arm64 +0 -0
- package/helpers/projectfreeze-linux-arm64.sha256 +1 -0
- package/helpers/projectfreeze-linux-x64 +0 -0
- package/helpers/projectfreeze-linux-x64.sha256 +1 -0
- package/helpers/projectfreeze-win32-arm64.exe +0 -0
- package/helpers/projectfreeze-win32-arm64.exe.sha256 +1 -0
- package/helpers/projectfreeze-win32-x64.exe +0 -0
- package/helpers/projectfreeze-win32-x64.exe.sha256 +1 -0
- package/package.json +17 -4
- package/src/cli.js +135 -0
- package/src/contracts.d.ts +28 -0
- package/src/discovery.js +112 -0
- package/src/engine.js +820 -0
- package/src/errors.js +20 -0
- package/src/integrations.js +195 -0
- package/src/inventory.js +37 -0
- package/src/ipc.js +258 -0
- package/src/line-menu.js +147 -0
- package/src/mcp.js +113 -0
- package/src/platform.js +320 -0
- package/src/recipe.js +69 -0
- package/src/store.js +61 -0
- package/src/supervisors.js +163 -0
- package/src/systemd.js +104 -0
- package/src/tui.js +596 -0
- package/src/windows-supervisors.ps1 +83 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
param([string]$Kind, [string]$Name, [string]$Action, [string]$Expected = '-')
|
|
2
|
+
$ErrorActionPreference = 'Stop'
|
|
3
|
+
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
|
4
|
+
$user = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
5
|
+
function Get-Sid([string]$Account) {
|
|
6
|
+
if ($Account -match '^S-1-[0-9-]+$') { return $Account }
|
|
7
|
+
return ([Security.Principal.NTAccount]::new($Account)).Translate([Security.Principal.SecurityIdentifier]).Value
|
|
8
|
+
}
|
|
9
|
+
function Digest($Value) {
|
|
10
|
+
$sha = [Security.Cryptography.SHA256]::Create()
|
|
11
|
+
try { return [BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes(($Value | ConvertTo-Json -Compress -Depth 6)))).Replace('-','').ToLowerInvariant() }
|
|
12
|
+
finally { $sha.Dispose() }
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
if ($Kind -eq 'task') {
|
|
16
|
+
if ($Name -notmatch '^\\(?:[A-Za-z0-9][A-Za-z0-9_.-]{0,80}\\)*[A-Za-z0-9][A-Za-z0-9_.-]{0,80}$') { exit 2 }
|
|
17
|
+
$at = $Name.LastIndexOf('\')
|
|
18
|
+
$folder = if ($at -eq 0) { '\' } else { $Name.Substring(0,$at) }
|
|
19
|
+
$scheduler = New-Object -ComObject 'Schedule.Service'
|
|
20
|
+
$scheduler.Connect()
|
|
21
|
+
$task = $scheduler.GetFolder($folder).GetTask($Name.Substring($at + 1))
|
|
22
|
+
$definition = $task.Definition
|
|
23
|
+
$acl = [Security.AccessControl.RawSecurityDescriptor]::new($task.GetSecurityDescriptor(5))
|
|
24
|
+
if (!$acl.DiscretionaryAcl) { exit 4 }
|
|
25
|
+
if ($definition.Actions.Count -ne 1 -or $definition.Actions.Item(1).Type -ne 0 -or
|
|
26
|
+
(Get-Sid $definition.Principal.UserId) -ne $user -or $definition.Principal.RunLevel -ne 0 -or
|
|
27
|
+
($acl.Owner.Value -ne $user -and $acl.Owner.Value -ne 'S-1-5-32-544')) { exit 4 }
|
|
28
|
+
foreach ($ace in $acl.DiscretionaryAcl) {
|
|
29
|
+
if ($ace.AceType -eq [Security.AccessControl.AceType]::AccessAllowed -and ($ace.AccessMask -band 0x500D0156) -ne 0 -and
|
|
30
|
+
$ace.SecurityIdentifier.Value -notin @($user,'S-1-5-18','S-1-5-32-544')) { exit 4 }
|
|
31
|
+
}
|
|
32
|
+
$exec = $definition.Actions.Item(1)
|
|
33
|
+
$xml = [xml]$task.Xml
|
|
34
|
+
$mutable = $xml.DocumentElement.SelectSingleNode('*[local-name()="Settings"]/*[local-name()="Enabled"]')
|
|
35
|
+
if ($mutable) { [void]$mutable.ParentNode.RemoveChild($mutable) }
|
|
36
|
+
$fingerprint = Digest @($task.Path,$xml.OuterXml,$acl.Owner.Value,$exec.Path,$exec.WorkingDirectory)
|
|
37
|
+
if ($Action -ne 'probe') {
|
|
38
|
+
if ($fingerprint -ne $Expected) { exit 4 }
|
|
39
|
+
if ($Action -notin @('disable','enable')) { exit 5 }
|
|
40
|
+
if ($Action -eq 'disable' -and ($task.GetInstances(0).Count -ne 0 -or $task.State -eq 2)) { exit 6 }
|
|
41
|
+
$task.Enabled = $Action -eq 'enable'
|
|
42
|
+
}
|
|
43
|
+
@{ path=$task.Path; fingerprint=$fingerprint; executable=$exec.Path; cwd=$exec.WorkingDirectory;
|
|
44
|
+
enabled=[bool]$task.Enabled; active=($task.GetInstances(0).Count -ne 0 -or $task.State -eq 2) } | ConvertTo-Json -Compress
|
|
45
|
+
} elseif ($Kind -eq 'scm') {
|
|
46
|
+
Add-Type -AssemblyName System.ServiceProcess
|
|
47
|
+
if ($Name -notmatch '^[A-Za-z0-9][A-Za-z0-9_.-]{0,100}$') { exit 2 }
|
|
48
|
+
$service = Get-CimInstance Win32_Service -Filter "Name='$Name'"
|
|
49
|
+
if (!$service) { exit 3 }
|
|
50
|
+
if ($service.ServiceType -ne 'Own Process' -or $service.StartMode -ne 'Manual' -or
|
|
51
|
+
(Get-Sid $service.StartName) -notin @($user,'S-1-5-19') -or $service.PathName -notmatch '^"([^"]+\.exe)"(?:\s|$)|^([^\s"]+\.exe)(?:\s|$)') { exit 4 }
|
|
52
|
+
$binary = if ($Matches[1]) { $Matches[1] } else { $Matches[2] }
|
|
53
|
+
$registry = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Services\$Name")
|
|
54
|
+
if (!$registry) { exit 4 }
|
|
55
|
+
try {
|
|
56
|
+
if (($registry.GetSubKeyNames() -contains 'TriggerInfo') -or $null -ne $registry.GetValue('FailureActions')) { exit 5 }
|
|
57
|
+
} finally { $registry.Dispose() }
|
|
58
|
+
$controller = [System.ServiceProcess.ServiceController]::new($Name)
|
|
59
|
+
try {
|
|
60
|
+
if ($controller.DependentServices.Count -or $controller.ServicesDependedOn.Count) { exit 4 }
|
|
61
|
+
$fingerprint = Digest @($service.Name,$service.PathName,$service.StartName,$service.ServiceType,$service.StartMode)
|
|
62
|
+
if ($Action -ne 'probe') {
|
|
63
|
+
if ($fingerprint -ne $Expected) { exit 4 }
|
|
64
|
+
$method = @{ pause='PauseService'; resume='ResumeService'; stop='StopService'; start='StartService' }[$Action]
|
|
65
|
+
if (!$method) { exit 5 }
|
|
66
|
+
$result = Invoke-CimMethod -InputObject $service -MethodName $method
|
|
67
|
+
if ($result.ReturnValue -eq 2) { exit 4 }
|
|
68
|
+
if ($result.ReturnValue -in @(6,10)) { exit 6 }
|
|
69
|
+
if ($result.ReturnValue -in @(1,5)) { exit 5 }
|
|
70
|
+
if ($result.ReturnValue -ne 0) { exit 7 }
|
|
71
|
+
}
|
|
72
|
+
@{ path=$service.Name; fingerprint=$fingerprint; executable=$binary; state=$service.State; pid=$service.ProcessId;
|
|
73
|
+
canPause=$service.AcceptPause; canStop=$service.AcceptStop } | ConvertTo-Json -Compress
|
|
74
|
+
} finally { $controller.Dispose() }
|
|
75
|
+
} else { exit 2 }
|
|
76
|
+
} catch {
|
|
77
|
+
if ($env:PF_SUPERVISOR_TEST_DIAGNOSTICS -eq '1') {
|
|
78
|
+
[Console]::Error.WriteLine(('{0}: HRESULT {1}, line {2}' -f $_.FullyQualifiedErrorId,$_.Exception.HResult,$_.InvocationInfo.ScriptLineNumber))
|
|
79
|
+
}
|
|
80
|
+
if ($_.Exception.HResult -in @(-2147024891,-2147217405)) { exit 4 }
|
|
81
|
+
if ($_.Exception.HResult -in @(-2147024894,-2147217406)) { exit 3 }
|
|
82
|
+
exit 7
|
|
83
|
+
}
|