Skip to content

Commit b5264f0

Browse files
chore(windows): address relevant comments
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
1 parent 64ef737 commit b5264f0

4 files changed

Lines changed: 73 additions & 31 deletions

File tree

internal/detector/aicli.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ func (d *AICLIDetector) getVersion(ctx context.Context, spec cliToolSpec, binary
164164
}
165165

166166
// cleanVersionString strips a leading tool name prefix from version output.
167-
// e.g. "codex-cli 0.118.0" -> "0.118.0", "aider 0.86.2" -> "0.86.2"
167+
// It finds the first token that looks like a version number (starts with a digit
168+
// or "v" followed by a digit) and returns it, preserving any "v" prefix.
169+
// e.g. "codex-cli 0.118.0" -> "0.118.0", "aider 0.86.2" -> "0.86.2", "v1.2.3" -> "v1.2.3"
168170
func cleanVersionString(v string) string {
169171
parts := strings.Fields(v)
170172
for _, p := range parts {

internal/schtasks/schtasks.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,14 @@ func Install(exec executor.Executor, log *progress.Logger) error {
4242
return fmt.Errorf("creating log directory: %w", err)
4343
}
4444

45-
// Build the task command with output redirection
46-
taskCmd := fmt.Sprintf(`cmd /c "\"%s\" send-telemetry >> \"%s\agent.log\" 2>> \"%s\agent.error.log\""`,
47-
binaryPath, logDir, logDir)
48-
49-
// Build schtasks arguments
50-
args := []string{"/create", "/tn", taskName, "/tr", taskCmd,
51-
"/sc", "HOURLY", "/mo", strconv.Itoa(hours), "/f"}
52-
if exec.IsRoot() {
53-
args = append(args, "/ru", "SYSTEM")
54-
}
45+
args := buildCreateArgs(binaryPath, logDir, hours, exec.IsRoot())
5546

5647
_, stderr, exitCode, err := exec.Run(ctx, "schtasks", args...)
57-
if err != nil || exitCode != 0 {
58-
return fmt.Errorf("failed to create scheduled task: %s", stderr)
48+
if err != nil {
49+
return fmt.Errorf("failed to create scheduled task: %w", err)
50+
}
51+
if exitCode != 0 {
52+
return fmt.Errorf("failed to create scheduled task (exit code %d): %s", exitCode, stderr)
5953
}
6054

6155
log.Progress("Windows Task Scheduler configuration completed successfully")
@@ -81,8 +75,11 @@ func Uninstall(exec executor.Executor, log *progress.Logger) error {
8175

8276
func doUninstall(ctx context.Context, exec executor.Executor, log *progress.Logger) error {
8377
_, stderr, exitCode, err := exec.Run(ctx, "schtasks", "/delete", "/tn", taskName, "/f")
84-
if err != nil || exitCode != 0 {
85-
return fmt.Errorf("failed to delete scheduled task: %s", stderr)
78+
if err != nil {
79+
return fmt.Errorf("failed to delete scheduled task: %w", err)
80+
}
81+
if exitCode != 0 {
82+
return fmt.Errorf("failed to delete scheduled task (exit code %d): %s", exitCode, stderr)
8683
}
8784

8885
log.Progress("Removed scheduled task: %s", taskName)
@@ -95,6 +92,17 @@ func isConfigured(ctx context.Context, exec executor.Executor) bool {
9592
return exitCode == 0
9693
}
9794

95+
func buildCreateArgs(binaryPath, logDir string, hours int, isAdmin bool) []string {
96+
taskCmd := fmt.Sprintf(`cmd /c "\"%s\" send-telemetry >> \"%s\agent.log\" 2>> \"%s\agent.error.log\""`,
97+
binaryPath, logDir, logDir)
98+
args := []string{"/create", "/tn", taskName, "/tr", taskCmd,
99+
"/sc", "HOURLY", "/mo", strconv.Itoa(hours), "/f"}
100+
if isAdmin {
101+
args = append(args, "/ru", "SYSTEM")
102+
}
103+
return args
104+
}
105+
98106
func resolveLogDir(exec executor.Executor) string {
99107
if exec.IsRoot() {
100108
return `C:\ProgramData\StepSecurity`

internal/schtasks/schtasks_test.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/step-security/dev-machine-guard/internal/config"
87
"github.com/step-security/dev-machine-guard/internal/executor"
98
"github.com/step-security/dev-machine-guard/internal/progress"
109
)
@@ -98,21 +97,47 @@ func TestResolveLogDir_Admin(t *testing.T) {
9897
}
9998
}
10099

101-
func TestInstall_CustomFrequency(t *testing.T) {
102-
origFreq := config.ScanFrequencyHours
103-
t.Cleanup(func() { config.ScanFrequencyHours = origFreq })
104-
config.ScanFrequencyHours = "6"
100+
func TestBuildCreateArgs_CustomFrequency(t *testing.T) {
101+
args := buildCreateArgs(`C:\agent.exe`, `C:\logs`, 6, false)
102+
103+
// Find the /mo argument and check its value
104+
foundMo := false
105+
for i, a := range args {
106+
if a == "/mo" && i+1 < len(args) {
107+
foundMo = true
108+
if args[i+1] != "6" {
109+
t.Errorf("expected /mo 6, got /mo %s", args[i+1])
110+
}
111+
}
112+
}
113+
if !foundMo {
114+
t.Error("expected /mo argument in schtasks create args")
115+
}
116+
}
105117

106-
mock := executor.NewMock()
107-
mock.SetGOOS("windows")
108-
mock.SetIsRoot(false)
109-
mock.SetHomeDir(`C:\Users\testuser`)
110-
// Task doesn't exist
111-
mock.SetCommand("", "ERROR: not found", 1, "schtasks", "/query", "/tn", taskName)
118+
func TestBuildCreateArgs_Admin(t *testing.T) {
119+
args := buildCreateArgs(`C:\agent.exe`, `C:\logs`, 4, true)
120+
121+
foundRU := false
122+
for i, a := range args {
123+
if a == "/ru" && i+1 < len(args) {
124+
foundRU = true
125+
if args[i+1] != "SYSTEM" {
126+
t.Errorf("expected /ru SYSTEM, got /ru %s", args[i+1])
127+
}
128+
}
129+
}
130+
if !foundRU {
131+
t.Error("expected /ru SYSTEM for admin install")
132+
}
133+
}
134+
135+
func TestBuildCreateArgs_NonAdmin(t *testing.T) {
136+
args := buildCreateArgs(`C:\agent.exe`, `C:\logs`, 4, false)
112137

113-
// Install will fail at os.Executable or schtasks create, but we're testing
114-
// that the frequency is parsed correctly via the resolveLogDir and config paths.
115-
// A full integration test requires the real binary on Windows.
116-
_ = Install(mock, newTestLogger())
117-
// If we got past the config parsing without panic, the frequency was handled correctly.
138+
for _, a := range args {
139+
if a == "/ru" {
140+
t.Error("expected no /ru argument for non-admin install")
141+
}
142+
}
118143
}

scripts/deploy-windows.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,14 @@ scp_cmd() {
113113
if [[ -n "$KEY" ]]; then
114114
scp $SSH_OPTS -i "$KEY" "$src" "${USER}@${HOST}:${dst}"
115115
elif [[ -n "$PASSWORD" ]]; then
116+
if ! command -v sshpass &>/dev/null; then
117+
echo "Error: sshpass required for password auth (brew install sshpass / apt install sshpass)" >&2
118+
exit 1
119+
fi
116120
SSHPASS="$PASSWORD" sshpass -e scp $SSH_OPTS "$src" "${USER}@${HOST}:${dst}"
121+
else
122+
echo "Error: provide --key or set DEPLOY_PASSWORD" >&2
123+
exit 1
117124
fi
118125
}
119126

0 commit comments

Comments
 (0)