Skip to content

Commit d4fc09e

Browse files
committed
Improve UNIX screensaver detection, fix /git/git bug
1 parent deb20e2 commit d4fc09e

File tree

5 files changed

+190
-118
lines changed

5 files changed

+190
-118
lines changed

.yamllint

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ rules:
1111
document-start: disable
1212
line-length:
1313
level: warning
14-
max: 160
14+
max: 200
1515
allow-non-breakable-inline-mappings: true
1616
truthy: disable

cmd/agent/checks.yaml

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,87 @@ checks:
173173
- Set screen lock delay to 5 seconds or less
174174
- Go to System Settings > Lock Screen
175175
- Set "Require password after..." to 5 seconds or less
176-
linux:
177-
- output: gsettings get org.gnome.desktop.screensaver lock-enabled
176+
unix:
177+
# GNOME - Only check if GNOME Shell is running
178+
- output: pgrep gnome-shell && gsettings get org.gnome.desktop.screensaver lock-enabled
178179
includes: "false"
179180
remediation:
180181
- Enable screen lock with 'gsettings set org.gnome.desktop.screensaver lock-enabled true'
182+
# MATE - Only check if MATE session is running
183+
- output: pgrep mate-session && gsettings get org.mate.screensaver lock-enabled
184+
includes: "false"
185+
remediation:
186+
- Enable screen lock with 'gsettings set org.mate.screensaver lock-enabled true'
187+
# XFCE - Only check if XFCE session is running
188+
- output: pgrep xfce4-session && xfconf-query -c xfce4-screensaver -p /saver/enabled
189+
includes: "false"
190+
remediation:
191+
- Enable screensaver with 'xfconf-query -c xfce4-screensaver -p /saver/enabled -s true'
192+
- output: pgrep xfce4-session && xfconf-query -c xfce4-screensaver -p /lock/enabled
193+
includes: "false"
194+
remediation:
195+
- Enable screen lock with 'xfconf-query -c xfce4-screensaver -p /lock/enabled -s true'
196+
# KDE Plasma - Only check if KDE session is running
197+
- output: pgrep plasmashell && kreadconfig5 --file kscreenlockerrc --group Daemon --key Autolock
198+
includes: "false"
199+
remediation:
200+
- Enable automatic screen locking in KDE System Settings > Desktop Behavior > Screen Locking
201+
- Or run 'kwriteconfig5 --file kscreenlockerrc --group Daemon --key Autolock true'
202+
# Cinnamon - Only check if Cinnamon is running
203+
- output: pgrep cinnamon && gsettings get org.cinnamon.desktop.screensaver lock-enabled
204+
includes: "false"
205+
remediation:
206+
- Enable Cinnamon screen lock with 'gsettings set org.cinnamon.desktop.screensaver lock-enabled true'
207+
# Budgie - Only check if Budgie is running
208+
- output: pgrep budgie-panel && gsettings get org.gnome.desktop.screensaver lock-enabled
209+
includes: "false"
210+
remediation:
211+
- Enable Budgie screen lock with 'gsettings set org.gnome.desktop.screensaver lock-enabled true'
212+
# LXQt - Only check if LXQt session is running
213+
- output: pgrep lxqt-session && grep -r "lockScreenCommand" ~/.config/lxqt/
214+
excludes: "lockScreenCommand"
215+
remediation:
216+
- Configure LXQt screen locking
217+
- Set lockScreenCommand in ~/.config/lxqt/session.conf
218+
- Install a screen locker like light-locker, xscreensaver, or xlock
219+
# LXDE - Only check if LXDE session is running
220+
- output: pgrep lxsession && (command -v light-locker || command -v xscreensaver || command -v xlock)
221+
exitcode: 1
222+
remediation:
223+
- Install a screen locker for LXDE
224+
- "Linux/FreeBSD: Install light-locker or xscreensaver"
225+
- "OpenBSD/NetBSD: Install xlock from packages"
226+
# i3 Window Manager - Only check if i3 is running
227+
- output: pgrep i3 && grep -r "i3lock\|xautolock\|xss-lock" ~/.config/i3/ ~/.i3/config
228+
excludes: "i3lock|xautolock|xss-lock"
229+
remediation:
230+
- Install and configure screen locking for i3
231+
- "Install locker: i3lock, slock, or xlock (varies by OS)"
232+
- "Add to i3 config: 'exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock -n'"
233+
# Openbox Window Manager - Only check if openbox is running
234+
- output: pgrep openbox && grep -r "xautolock\|xss-lock" ~/.config/openbox/
235+
excludes: "xautolock|xss-lock"
236+
remediation:
237+
- Configure screen locking for Openbox
238+
- "Add to ~/.config/openbox/autostart: 'xautolock -time 15 -locker \"i3lock -c 000000\" &'"
239+
# Sway (Wayland) - Only check if sway is running
240+
- output: pgrep sway && grep -r "exec swayidle" ~/.config/sway/
241+
excludes: "swayidle"
242+
remediation:
243+
- Configure swayidle to lock screen automatically
244+
- "Add to Sway config: 'exec swayidle -w timeout 900 \"swaylock -f\" before-sleep \"swaylock -f\"'"
245+
# Generic X11 fallback - Only if X11 is running but no specific DE detected
246+
- output: >
247+
pgrep Xorg && ! (pgrep gnome-shell || pgrep mate-session || pgrep xfce4-session ||
248+
pgrep plasmashell || pgrep cinnamon || pgrep budgie-panel || pgrep lxqt-session ||
249+
pgrep lxsession || pgrep i3 || pgrep openbox || pgrep sway) && xset q
250+
includes: "timeout:.*0"
251+
remediation:
252+
- Configure X11 screen saver with 'xset s 900'
253+
- Install and configure a screen locker (xlock, i3lock, slock)
254+
- "Add to window manager startup: 'xautolock -time 15 -locker \"xlock\" &'"
255+
linux:
256+
# Linux-specific additional checks
181257
windows:
182258
- output: >
183259
powershell -Command "(Get-ItemProperty -Path 'HKCU:\Control Panel\Desktop'
@@ -199,13 +275,89 @@ checks:
199275
- Go to System Settings > Lock Screen
200276
- Set "Turn display off on battery when inactive" to 15 minutes or less
201277
- Set "Turn display off on power adapter when inactive" to 15 minutes or less
202-
linux:
203-
# Check screensaver idle delay (in seconds) - fail if 0 or > 900
204-
- output: gsettings get org.gnome.desktop.session idle-delay 2>&1 | grep -o '[0-9]*' || echo "0"
278+
unix:
279+
# GNOME - Only check timeout if GNOME Shell is running
280+
- output: pgrep gnome-shell && gsettings get org.gnome.desktop.session idle-delay
205281
includes: "^(0|9[1-9][0-9]|[1-9][0-9]{3,})$"
206282
remediation:
207-
- Set screensaver timeout to 15 minutes or less
283+
- Set GNOME screensaver timeout to 15 minutes or less
208284
- Run 'gsettings set org.gnome.desktop.session idle-delay 900'
285+
# MATE - Only check timeout if MATE session is running
286+
- output: pgrep mate-session && gsettings get org.mate.screensaver idle-delay
287+
includes: "^(0|1[6-9]|[2-9][0-9]|[1-9][0-9]{2,})$"
288+
remediation:
289+
- Set MATE screensaver timeout to 15 minutes or less
290+
- Run 'gsettings set org.mate.screensaver idle-delay 15'
291+
# XFCE - Only check timeout if XFCE session is running
292+
- output: pgrep xfce4-session && xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/dpms-on-ac-sleep
293+
includes: "^(0|1[6-9]|[2-9][0-9]|[1-9][0-9]{2,})$"
294+
remediation:
295+
- Set XFCE display timeout to 15 minutes or less
296+
- Run 'xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/dpms-on-ac-sleep -s 15'
297+
- output: pgrep xfce4-session && xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/dpms-on-battery-sleep
298+
includes: "^(0|1[6-9]|[2-9][0-9]|[1-9][0-9]{2,})$"
299+
remediation:
300+
- Set XFCE battery display timeout to 15 minutes or less
301+
- Run 'xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/dpms-on-battery-sleep -s 15'
302+
# KDE Plasma - Only check timeout if KDE session is running
303+
- output: pgrep plasmashell && kreadconfig5 --file kscreenlockerrc --group Daemon --key Timeout
304+
includes: "^(0|1[6-9]|[2-9][0-9]|[1-9][0-9]{2,})$"
305+
remediation:
306+
- Set KDE screen lock timeout to 15 minutes or less
307+
- Run 'kwriteconfig5 --file kscreenlockerrc --group Daemon --key Timeout 15'
308+
- Or configure in System Settings > Desktop Behavior > Screen Locking
309+
# Cinnamon - Only check timeout if Cinnamon is running
310+
- output: pgrep cinnamon && gsettings get org.cinnamon.desktop.screensaver lock-delay
311+
includes: "^(0|9[1-9][0-9]|[1-9][0-9]{3,})$"
312+
remediation:
313+
- Set Cinnamon screensaver timeout to 15 minutes or less
314+
- Run 'gsettings set org.cinnamon.desktop.screensaver lock-delay 900'
315+
# Budgie - Only check timeout if Budgie is running
316+
- output: pgrep budgie-panel && gsettings get org.gnome.desktop.session idle-delay
317+
includes: "^(0|9[1-9][0-9]|[1-9][0-9]{3,})$"
318+
remediation:
319+
- Set Budgie screensaver timeout to 15 minutes or less
320+
- Run 'gsettings set org.gnome.desktop.session idle-delay 900'
321+
# LXQt - Only check timeout if LXQt session is running
322+
- output: pgrep lxqt-session && grep -r "timeBeforeIdleMs" ~/.config/lxqt/
323+
includes: "timeBeforeIdleMs.*([9][1-9][0-9][0-9][0-9][0-9]|[1-9][0-9]{6,})"
324+
remediation:
325+
- Set LXQt idle timeout to 15 minutes (900000 ms) or less
326+
- Configure in LXQt System Settings > Power Management
327+
# LXDE - Only check timeout if LXDE session is running
328+
- output: pgrep lxsession && grep -r "sleep_display_ac" ~/.config/lxsession/ /etc/xdg/lxsession/
329+
includes: "sleep_display_ac.*([1-9][6-9]|[2-9][0-9]|[1-9][0-9]{2,})"
330+
remediation:
331+
- Set LXDE display sleep to 15 minutes or less
332+
- Edit ~/.config/lxsession/LXDE/desktop.conf and set sleep_display_ac=15
333+
# Sway - Only check timeout if sway is running
334+
- output: pgrep sway && grep -r "timeout" ~/.config/sway/
335+
includes: "timeout [9][1-9][0-9][0-9]|timeout [1-9][0-9]{4,}"
336+
remediation:
337+
- Configure swayidle timeout to 15 minutes (900 seconds) or less
338+
- "Add to Sway config: 'exec swayidle -w timeout 900 \"swaylock -f\"'"
339+
# i3 Window Manager - Only check timeout if i3 is running
340+
- output: pgrep i3 && grep -r "xautolock.*-time" ~/.config/i3/ ~/.i3/config
341+
includes: "-time [1-9][6-9]|-time [2-9][0-9]|-time [1-9][0-9]{2,}"
342+
remediation:
343+
- Configure xautolock timeout to 15 minutes or less
344+
- "Add to i3 config: 'exec --no-startup-id xautolock -time 15 -locker \"i3lock -c 000000\"'"
345+
# Openbox - Only check timeout if openbox is running
346+
- output: pgrep openbox && grep -r "xautolock.*-time" ~/.config/openbox/
347+
includes: "-time [1-9][6-9]|-time [2-9][0-9]|-time [1-9][0-9]{2,}"
348+
remediation:
349+
- Configure xautolock timeout to 15 minutes or less
350+
- "Add to ~/.config/openbox/autostart: 'xautolock -time 15 -locker \"i3lock -c 000000\" &'"
351+
# Generic X11 - Only if X11 is running but no specific DE detected
352+
- output: >
353+
pgrep Xorg && ! (pgrep gnome-shell || pgrep mate-session || pgrep xfce4-session ||
354+
pgrep plasmashell || pgrep cinnamon || pgrep budgie-panel || pgrep lxqt-session ||
355+
pgrep lxsession || pgrep i3 || pgrep openbox || pgrep sway) && xset q
356+
includes: "timeout:.*([9][1-9][0-9]|[1-9][0-9]{3,})"
357+
remediation:
358+
- Set X11 screen saver timeout to 15 minutes (900 seconds) or less
359+
- Run 'xset s 900' to set timeout
360+
- Add 'xset s 900' and screen locker to your startup scripts
209361
windows:
210362
# Check screensaver timeout in seconds (fail if 0 or > 900)
211363
- output: >

cmd/server/main.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ const (
6363
//go:embed templates/*
6464
var templates embed.FS
6565

66+
//go:embed static/*
67+
var staticFiles embed.FS
68+
6669
var (
6770
gitURL = flag.String("git", os.Getenv("GIT_REPO"), "Git repository URL or path to clone to temp directory (env: GIT_REPO)")
6871
clone = flag.String("clone", "", "Path to existing local git clone to work in directly")
@@ -272,6 +275,10 @@ func main() {
272275
mux.HandleFunc("/api/v1/devices", server.handleAPIDevices)
273276
mux.HandleFunc("/health", server.handleHealth)
274277

278+
// Serve static files
279+
staticHandler := http.FileServer(http.FS(staticFiles))
280+
mux.Handle("/static/", http.StripPrefix("/static/", staticHandler))
281+
275282
srv := &http.Server{
276283
Addr: ":" + *port,
277284
Handler: loggingMiddleware(mux),
@@ -877,9 +884,8 @@ func loggingMiddleware(next http.Handler) http.Handler {
877884
writer.Header().Set("X-Frame-Options", "DENY")
878885
writer.Header().Set("X-XSS-Protection", "1; mode=block")
879886
writer.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
880-
cspPolicy := "default-src 'self'; style-src 'self' 'unsafe-inline'; " +
881-
"script-src 'self' 'sha256-lx2dNpe/W18YJvrDgNmLsDSM85bNRGfsh94PEHc787A=' " +
882-
"'sha256-luoavMkf5zRI42vsq0JhiXNoMTeddLeACqCCElcW+GE='"
887+
// Simplified CSP - only allow self-hosted scripts and styles
888+
cspPolicy := "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'"
883889
writer.Header().Set("Content-Security-Policy", cspPolicy)
884890

885891
start := time.Now()

cmd/server/templates/device.html

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -578,45 +578,6 @@ <h3 class="empty-state-title">No Compliance Data Available</h3>
578578
{{end}}
579579
</div>
580580

581-
<script>
582-
function toggleCheck(header) {
583-
const checkItem = header.parentElement;
584-
checkItem.classList.toggle('expanded');
585-
}
586-
587-
document.addEventListener('DOMContentLoaded', function() {
588-
// Add click listeners to check headers
589-
document.querySelectorAll('.check-header').forEach(header => {
590-
header.addEventListener('click', function() {
591-
toggleCheck(this);
592-
});
593-
});
594-
595-
// Filter functionality
596-
document.querySelectorAll('.filter-btn').forEach(btn => {
597-
btn.addEventListener('click', function() {
598-
// Update active button
599-
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
600-
this.classList.add('active');
601-
602-
// Filter checks
603-
const filter = this.dataset.filter;
604-
document.querySelectorAll('.check-item').forEach(item => {
605-
if (filter === 'all' || item.dataset.status === filter) {
606-
item.style.display = '';
607-
} else {
608-
item.style.display = 'none';
609-
}
610-
});
611-
});
612-
});
613-
614-
// Auto-expand failed checks
615-
document.querySelectorAll('.check-item[data-status="fail"]').forEach(item => {
616-
// Optionally auto-expand failed checks
617-
// item.classList.add('expanded');
618-
});
619-
});
620-
</script>
581+
<script src="/static/device.js"></script>
621582
</body>
622583
</html>

internal/gitstore/store.go

Lines changed: 21 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,22 @@ func NewLocal(ctx context.Context, localPath string) (*Store, error) {
6363
// Check if it's already a git repository
6464
var repo *git.Repository
6565
if _, err := os.Stat(filepath.Join(absPath, ".git")); err != nil {
66-
if os.IsNotExist(err) {
67-
// Initialize new repository
68-
log.Printf("[INFO] Initializing new git repository at %s", absPath)
69-
repo, err = git.PlainInit(absPath, false)
70-
if err != nil {
71-
return nil, fmt.Errorf("failed to initialize git repository: %w", err)
72-
}
73-
74-
// Create initial commit
75-
if err := createInitialCommit(repo, absPath); err != nil {
76-
log.Printf("[WARN] Failed to create initial commit: %v", err)
77-
}
66+
if !os.IsNotExist(err) {
67+
return nil, fmt.Errorf("failed to check git repository: %w", err)
68+
}
69+
// Initialize new repository
70+
log.Printf("[INFO] Initializing new git repository at %s", absPath)
71+
repo, err = git.PlainInit(absPath, false)
72+
if err != nil {
73+
return nil, fmt.Errorf("failed to initialize git repository: %w", err)
74+
}
75+
// No initial commit needed - first device will create directory structure
76+
} else {
77+
// Open existing repository
78+
repo, err = git.PlainOpen(absPath)
79+
if err != nil {
80+
return nil, fmt.Errorf("failed to open git repository: %w", err)
7881
}
79-
return nil, fmt.Errorf("failed to check git repository: %w", err)
80-
}
81-
// Open existing repository
82-
repo, err = git.PlainOpen(absPath)
83-
if err != nil {
84-
return nil, fmt.Errorf("failed to open git repository: %w", err)
8582
}
8683

8784
s := &Store{
@@ -102,8 +99,8 @@ func NewLocal(ctx context.Context, localPath string) (*Store, error) {
10299
}
103100

104101
// Create devices directory if it doesn't exist
105-
devicesDir := filepath.Join(s.repoPath, devicesDir)
106-
if err := os.MkdirAll(devicesDir, repoDirPerm); err != nil {
102+
devicesDirPath := filepath.Join(s.repoPath, devicesDir)
103+
if err := os.MkdirAll(devicesDirPath, repoDirPerm); err != nil {
107104
return nil, fmt.Errorf("failed to create devices directory: %w", err)
108105
}
109106

@@ -188,8 +185,8 @@ func NewRemote(ctx context.Context, gitURL string) (*Store, error) {
188185
}
189186

190187
// Create devices directory if needed
191-
devicesDir := filepath.Join(s.repoPath, devicesDir)
192-
if err := os.MkdirAll(devicesDir, repoDirPerm); err != nil {
188+
devicesDirPath := filepath.Join(s.repoPath, devicesDir)
189+
if err := os.MkdirAll(devicesDirPath, repoDirPerm); err != nil {
193190
return nil, fmt.Errorf("failed to create devices directory: %w", err)
194191
}
195192

@@ -386,8 +383,8 @@ func (s *Store) LoadDevices(ctx context.Context) ([]*gitmdm.Device, error) {
386383
}
387384
}
388385

389-
devicesDir := filepath.Join(s.repoPath, devicesDir)
390-
entries, err := os.ReadDir(devicesDir)
386+
devicesDirPath := filepath.Join(s.repoPath, devicesDir)
387+
entries, err := os.ReadDir(devicesDirPath)
391388
if err != nil {
392389
if os.IsNotExist(err) {
393390
log.Print("[INFO] No devices directory found, returning empty list")
@@ -539,47 +536,3 @@ func sanitizeID(id string) string {
539536

540537
return result
541538
}
542-
543-
// createInitialCommit creates an initial commit with the devices directory.
544-
func createInitialCommit(repo *git.Repository, repoPath string) error {
545-
w, err := repo.Worktree()
546-
if err != nil {
547-
return fmt.Errorf("failed to get worktree: %w", err)
548-
}
549-
550-
// Create devices directory
551-
devicesDir := filepath.Join(repoPath, devicesDir)
552-
if err := os.MkdirAll(devicesDir, repoDirPerm); err != nil {
553-
return fmt.Errorf("failed to create devices directory: %w", err)
554-
}
555-
556-
// Create README in devices directory to ensure it's tracked
557-
readmePath := filepath.Join(devicesDir, "README.md")
558-
readmeContent := `# Device Compliance Data
559-
560-
This directory contains compliance reports for all monitored devices.
561-
Each device has its own subdirectory identified by its hardware ID.
562-
`
563-
if err := os.WriteFile(readmePath, []byte(readmeContent), 0o600); err != nil {
564-
return fmt.Errorf("failed to create README: %w", err)
565-
}
566-
567-
// Add README to staging
568-
if _, err := w.Add("devices/README.md"); err != nil {
569-
return fmt.Errorf("failed to add README: %w", err)
570-
}
571-
572-
// Create initial commit
573-
_, err = w.Commit("Initial commit - GitMDM repository initialized", &git.CommitOptions{
574-
Author: &object.Signature{
575-
Name: "GitMDM",
576-
Email: "gitmdm@localhost",
577-
When: time.Now(),
578-
},
579-
})
580-
if err != nil {
581-
return fmt.Errorf("failed to create initial commit: %w", err)
582-
}
583-
584-
return nil
585-
}

0 commit comments

Comments
 (0)