Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.2] - 2025-12-26

### Added
- Added MacOS Secure Keyboard Entry detection for supported terminals

### Changed
- Updated documentation for Secure Keyboard Entry feature
- Improved filename filtering in DumpsterFire component


## [1.1.1] - 2025-12-21

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $ dashlights --details

### Security Checks

Dashlights performs **37 concurrent security checks** across five categories: Identity & Access Management, Operational Security, Repository Hygiene, System Health, and Infrastructure Security.
Dashlights performs **38 concurrent security checks** across five categories: Identity & Access Management, Operational Security, Repository Hygiene, System Health, and Infrastructure Security.

👉 **[View the complete list of security signals →](SIGNALS.md)**

Expand Down
52 changes: 26 additions & 26 deletions SIGNALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,41 @@ Dashlights performs over 35 concurrent security checks, organized into six categ
10. 🐳 **[Exposed Socket](docs/signals/docker_socket.md)** - Checks Docker socket permissions and orphaned DOCKER_HOST [[code](src/signals/docker_socket.go)]
11. 🐛 **[Debug Mode Enabled](docs/signals/debug_enabled.md)** - Detects debug/trace/verbose environment variables [[code](src/signals/debug_enabled.go)]
12. 🔐 **[History Permissions](docs/signals/history_permissions.md)** - Checks shell history files for world-readable permissions [[code](src/signals/history_permissions.go)]
13. ⚠️ **[Insecure Curl Pipe](docs/signals/insecure_curl_pipe.md)** - Detects recent use of curl | bash or curl | sh installers [[code](src/signals/insecure_curl_pipe.go)]
14. 🔑 **[SSH Agent Key Bloat](docs/signals/ssh_agent_bloat.md)** - Detects too many keys in SSH agent (causes MaxAuthTries lockouts) [[code](src/signals/ssh_agent_bloat.go)]
15. 🔑 **[Open Door](docs/signals/ssh_keys.md)** - Detects SSH private keys with incorrect permissions [[code](src/signals/ssh_keys.go)]
13. ⌨️ **[Secure Keyboard Entry](docs/signals/secure_keyboard.md)** - Detects macOS terminal apps running without Secure Keyboard Entry enabled [[code](src/signals/secure_keyboard.go)]
14. ⚠️ **[Insecure Curl Pipe](docs/signals/insecure_curl_pipe.md)** - Detects recent use of curl | bash or curl | sh installers [[code](src/signals/insecure_curl_pipe.go)]
15. 🔑 **[SSH Agent Key Bloat](docs/signals/ssh_agent_bloat.md)** - Detects too many keys in SSH agent (causes MaxAuthTries lockouts) [[code](src/signals/ssh_agent_bloat.go)]
16. 🔑 **[Open Door](docs/signals/ssh_keys.md)** - Detects SSH private keys with incorrect permissions [[code](src/signals/ssh_keys.go)]

## Repository Hygiene

16. 📝 **[Unignored Secret](docs/signals/env_not_ignored.md)** - Checks if .env files exist but aren't in .gitignore [[code](src/signals/env_not_ignored.go)]
17. 👑 **[Root-Owned Home Files](docs/signals/root_owned_home.md)** - Finds files in $HOME owned by root [[code](src/signals/root_owned_home.go)]
18. 🖊️ **[World-Writable Configs](docs/signals/world_writable_config.md)** - Detects config files with dangerous permissions [[code](src/signals/world_writable_config.go)]
19. 🗝️ **[Dead Letter](docs/signals/untracked_crypto_keys.md)** - Finds cryptographic keys not in .gitignore [[code](src/signals/untracked_crypto_keys.go)]
20. 🔄 **[Go Replace Directive](docs/signals/go_replace.md)** - Detects replace directives in go.mod (breaks builds) [[code](src/signals/go_replace.go)]
21. 🐍 **[PyCache Pollution](docs/signals/pycache_pollution.md)** - Checks for __pycache__ directories not properly ignored [[code](src/signals/pycache_pollution.go)]
22. 📦 **[NPM RC Tokens](docs/signals/npmrc_tokens.md)** - Detects auth tokens in project .npmrc (should be in ~/.npmrc) [[code](src/signals/npmrc_tokens.go)]
23. 🦀 **[Cargo Path Dependencies](docs/signals/cargo_path_deps.md)** - Checks for path dependencies in Cargo.toml [[code](src/signals/cargo_path_deps.go)]
24. 📁 **[Missing __init__.py](docs/signals/missing_init_py.md)** - Detects Python packages missing __init__.py files [[code](src/signals/missing_init_py.go)]
25. ☕ **[Snapshot Dependency](docs/signals/snapshot_dependency.md)** - Checks for SNAPSHOT dependencies on release branches (Java/Maven) [[code](src/signals/snapshot_dependency.go)]
26. 🎬 **[Unsafe Workflow](docs/signals/unsafe_workflow.md)** - Detects dangerous GitHub Actions patterns (pwn requests, expression injection) [[code](src/signals/unsafe_workflow.go)]
27. ⚓ **[Missing Git Hooks](docs/signals/missing_git_hooks.md)** - Detects when hook manager config exists but hooks aren't installed [[code](src/signals/missing_git_hooks.go)]
17. 📝 **[Unignored Secret](docs/signals/env_not_ignored.md)** - Checks if .env files exist but aren't in .gitignore [[code](src/signals/env_not_ignored.go)]
18. 👑 **[Root-Owned Home Files](docs/signals/root_owned_home.md)** - Finds files in $HOME owned by root [[code](src/signals/root_owned_home.go)]
19. 🖊️ **[World-Writable Configs](docs/signals/world_writable_config.md)** - Detects config files with dangerous permissions [[code](src/signals/world_writable_config.go)]
20. 🗝️ **[Dead Letter](docs/signals/untracked_crypto_keys.md)** - Finds cryptographic keys not in .gitignore [[code](src/signals/untracked_crypto_keys.go)]
21. 🔄 **[Go Replace Directive](docs/signals/go_replace.md)** - Detects replace directives in go.mod (breaks builds) [[code](src/signals/go_replace.go)]
22. 🐍 **[PyCache Pollution](docs/signals/pycache_pollution.md)** - Checks for __pycache__ directories not properly ignored [[code](src/signals/pycache_pollution.go)]
23. 📦 **[NPM RC Tokens](docs/signals/npmrc_tokens.md)** - Detects auth tokens in project .npmrc (should be in ~/.npmrc) [[code](src/signals/npmrc_tokens.go)]
24. 🦀 **[Cargo Path Dependencies](docs/signals/cargo_path_deps.md)** - Checks for path dependencies in Cargo.toml [[code](src/signals/cargo_path_deps.go)]
25. 📁 **[Missing __init__.py](docs/signals/missing_init_py.md)** - Detects Python packages missing __init__.py files [[code](src/signals/missing_init_py.go)]
26. ☕ **[Snapshot Dependency](docs/signals/snapshot_dependency.md)** - Checks for SNAPSHOT dependencies on release branches (Java/Maven) [[code](src/signals/snapshot_dependency.go)]
27. 🎬 **[Unsafe Workflow](docs/signals/unsafe_workflow.md)** - Detects dangerous GitHub Actions patterns (pwn requests, expression injection) [[code](src/signals/unsafe_workflow.go)]
28. ⚓ **[Missing Git Hooks](docs/signals/missing_git_hooks.md)** - Detects when hook manager config exists but hooks aren't installed [[code](src/signals/missing_git_hooks.go)]

## System Health

28. 💾 **[Full Tank](docs/signals/disk_space.md)** - Alerts when disk usage exceeds 90% [[code](src/signals/disk_space.go)]
29. ♻️ **[Reboot Pending](docs/signals/reboot_pending.md)** - Detects pending system reboot (Linux) [[code](src/signals/reboot_pending.go)]
30. 🧟 **[Zombie Processes](docs/signals/zombie_processes.md)** - Alerts on excessive zombie processes [[code](src/signals/zombie_processes.go)]
31. 💔 **[Dangling Symlinks](docs/signals/dangling_symlinks.md)** - Detects symlinks pointing to non-existent targets [[code](src/signals/dangling_symlinks.go)]
32. ⏰ **[Time Drift Detected](docs/signals/time_drift.md)** - Detects drift between system time and filesystem time [[code](src/signals/time_drift.go)]
29. 💾 **[Full Tank](docs/signals/disk_space.md)** - Alerts when disk usage exceeds 90% [[code](src/signals/disk_space.go)]
30. ♻️ **[Reboot Pending](docs/signals/reboot_pending.md)** - Detects pending system reboot (Linux) [[code](src/signals/reboot_pending.go)]
31. 🧟 **[Zombie Processes](docs/signals/zombie_processes.md)** - Alerts on excessive zombie processes [[code](src/signals/zombie_processes.go)]
32. 💔 **[Dangling Symlinks](docs/signals/dangling_symlinks.md)** - Detects symlinks pointing to non-existent targets [[code](src/signals/dangling_symlinks.go)]
33. ⏰ **[Time Drift Detected](docs/signals/time_drift.md)** - Detects drift between system time and filesystem time [[code](src/signals/time_drift.go)]

## Infrastructure Security (InfraSec)

33. 🏗️ **[Local Terraform State](docs/signals/terraform_state_local.md)** - Checks for local terraform.tfstate files (should use remote state) [[code](src/signals/terraform_state_local.go)]
34. ☸️ **[Root Kube Context](docs/signals/root_kube_context.md)** - Alerts when Kubernetes context uses kube-system namespace [[code](src/signals/root_kube_context.go)]
35. 🔐 **[Dangerous TF_VAR](docs/signals/dangerous_tf_var.md)** - Checks for dangerous Terraform variables in environment (secrets in shell history) [[code](src/signals/dangerous_tf_var.go)]
34. 🏗️ **[Local Terraform State](docs/signals/terraform_state_local.md)** - Checks for local terraform.tfstate files (should use remote state) [[code](src/signals/terraform_state_local.go)]
35. ☸️ **[Root Kube Context](docs/signals/root_kube_context.md)** - Alerts when Kubernetes context uses kube-system namespace [[code](src/signals/root_kube_context.go)]
36. 🔐 **[Dangerous TF_VAR](docs/signals/dangerous_tf_var.md)** - Checks for dangerous Terraform variables in environment (secrets in shell history) [[code](src/signals/dangerous_tf_var.go)]

## Data Sprawl

36. 🗑️ **[Dumpster Fire](docs/signals/dumpster_fire.md)** - Detects sensitive files (dumps, logs, keys) in hot zones (Downloads, Desktop, $PWD, /tmp) [[code](src/signals/dumpster_fire.go)]
37. 🦴 **[Rotting Secrets](docs/signals/rotting_secrets.md)** - Detects old (>7 days) sensitive files that may have been forgotten [[code](src/signals/rotting_secrets.go)]

37. 🗑️ **[Dumpster Fire](docs/signals/dumpster_fire.md)** - Detects sensitive files (dumps, logs, keys) in hot zones (Downloads, Desktop, $PWD, /tmp) [[code](src/signals/dumpster_fire.go)]
38. 🦴 **[Rotting Secrets](docs/signals/rotting_secrets.md)** - Detects old (>7 days) sensitive files that may have been forgotten [[code](src/signals/rotting_secrets.go)]
133 changes: 133 additions & 0 deletions docs/signals/secure_keyboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Secure Keyboard Entry

## What this is

This signal detects when Terminal.app, iTerm2, or Ghostty is running **without** Secure Keyboard Entry enabled.

**Signal behavior**: Only triggers when a terminal application is:
1. Currently running, AND
2. Has Secure Keyboard Entry disabled (or never enabled)

**Supported applications**:
- **Terminal.app**: macOS built-in terminal
- **iTerm2**: Popular third-party terminal emulator
- **Ghostty**: GPU-accelerated terminal emulator

**Platform**: macOS only

## Why this matters

### Keylogger Protection

Secure Keyboard Entry is a macOS security feature that prevents other applications from intercepting keystrokes sent to the terminal. Without it enabled:

- **Credential theft**: Malicious software can capture passwords, API tokens, and other secrets as you type them
- **Command interception**: Attackers can see every command you execute, including those containing sensitive data
- **Session hijacking**: SSH passwords, sudo credentials, and database passwords are all vulnerable

### Real-World Scenarios

**Scenario 1: Malware infection**
A malicious application (installed via compromised software or phishing) runs a keylogger in the background. Every time you type `sudo`, enter your password, or paste an API token, it gets captured.

**Scenario 2: Credential harvesting**
You're working in a coffee shop. Another user on the same network tricks your Mac into running a background process. Without Secure Keyboard Entry, they can harvest your keystrokes remotely.

**Scenario 3: Development secrets**
You paste AWS credentials, database passwords, or GitHub tokens into your terminal. Without protection, these can be intercepted by any malicious process with accessibility permissions.

### Developer Hygiene

For developers, terminals are where sensitive operations happen:
- `ssh` with passwords or passphrases
- `git push` with credentials
- Environment variable exports with API keys
- Database connections with passwords
- AWS/GCP/Azure CLI commands
- Kubernetes `kubectl` with secrets

## How to remediate

### Terminal.app

**Enable Secure Keyboard Entry**:

1. Open Terminal.app
2. Click **Terminal** in the menu bar
3. Click **Secure Keyboard Entry** to enable it (a checkmark appears when enabled)

**Or use the keyboard shortcut**: Press `Command + Shift + K`

**Verify it's enabled**:
```bash
defaults read com.apple.Terminal SecureKeyboardEntry
# Returns 1 when enabled
```

### iTerm2

**Enable Secure Keyboard Entry**:

1. Open iTerm2
2. Click **iTerm2** in the menu bar
3. Click **Secure Keyboard Entry** to enable it (a checkmark appears when enabled)

**Verify it's enabled**:
```bash
defaults read com.googlecode.iterm2 "Secure Input"
# Returns 1 when enabled
```

### Ghostty

**Enable Secure Keyboard Entry**:

1. Open Ghostty
2. Click **Ghostty** in the menu bar
3. Click **Secure Keyboard Entry** to enable it (a checkmark appears when enabled)

**Verify it's enabled**:
```bash
defaults read com.mitchellh.ghostty SecureInput
# Returns 1 when enabled
```

### Making it Permanent

Terminal.app, iTerm2, and Ghostty remember your Secure Keyboard Entry preference. Once enabled, it persists across sessions and restarts.

**Note**: Some users disable it because it can interfere with certain accessibility features or automation tools. If you need to disable it, understand the security implications.

## Security best practices

1. **Always enable Secure Keyboard Entry** when working with sensitive data

2. **Check the setting regularly**: The setting can be toggled accidentally via keyboard shortcut

3. **Be aware of limitations**: Secure Keyboard Entry only protects the specific terminal window. Other applications can still be keylogged.

4. **Use password managers**: Instead of typing passwords, use a password manager with auto-fill

5. **Use SSH keys**: Instead of SSH passwords, use SSH key authentication

6. **Use credential helpers**: Configure Git, AWS CLI, and other tools to use secure credential storage

7. **Avoid pasting secrets**: Use environment variables, secret managers, or credential helpers instead of pasting secrets directly

## How it Works

This signal:
1. Enumerates running processes to check if Terminal.app, iTerm2, or Ghostty is running
2. If running, reads the application's preferences plist to check the Secure Keyboard Entry setting
3. Only signals if the app is running AND the setting is disabled

**Performance**: Uses native process enumeration (~1-2ms) and cached plist reading, well within the 10ms budget.

## Disabling This Signal

To disable this signal, set the environment variable:
```bash
export DASHLIGHTS_DISABLE_SECURE_KEYBOARD=1
```

To disable permanently, add the above line to your shell configuration file (`~/.zshrc`, `~/.bashrc`, etc.).
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.25
require (
github.com/alexflint/go-arg v1.6.0
github.com/fatih/color v1.18.0
github.com/mitchellh/go-ps v1.0.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -13,4 +14,5 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.25.0 // indirect
howett.net/plist v1.0.1 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand All @@ -22,5 +25,8 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
41 changes: 38 additions & 3 deletions src/signals/internal/filestat/filestat.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func DefaultSensitivePatterns() SensitiveFilePatterns {
"backup-", // Common backup prefix
},
Substrings: []string{
"prod", // Production data indicators
"prod", // Production data indicators (matched at word boundaries)
"production", // Full word also matches (e.g., "production-dump.sql")
},
}
}
Expand Down Expand Up @@ -112,16 +113,50 @@ func (p *SensitiveFilePatterns) MatchFile(name string) bool {
}
}

// Check substrings
// Check substrings with word-boundary awareness
for _, substr := range p.Substrings {
if strings.Contains(lowerName, substr) {
if containsAtWordBoundary(lowerName, substr) {
return true
}
}

return false
}

// containsAtWordBoundary checks if substr appears in s at word boundaries.
// Word boundaries are: start/end of string, or common delimiters (-_. and space).
// This prevents "prod" from matching "product", "produce", etc.
func containsAtWordBoundary(s, substr string) bool {
idx := 0
for {
pos := strings.Index(s[idx:], substr)
if pos == -1 {
return false
}
pos += idx // Adjust to absolute position

// Check if at word boundary
atStart := pos == 0 || isDelimiter(s[pos-1])
endPos := pos + len(substr)
atEnd := endPos == len(s) || isDelimiter(s[endPos])

if atStart && atEnd {
return true
}

// Move past this occurrence and keep searching
idx = pos + 1
if idx >= len(s) {
return false
}
}
}

// isDelimiter returns true if the byte is a common filename delimiter.
func isDelimiter(b byte) bool {
return b == '-' || b == '_' || b == '.' || b == ' '
}

// ScanDirectory scans a directory for files matching the patterns.
// It returns only regular files (no directories, symlinks, etc.).
// This function is shallow - it does not recurse into subdirectories.
Expand Down
Loading
Loading