fix: harden SSH trust and dependency security
This commit is contained in:
@@ -3,6 +3,7 @@ package storage
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -455,6 +456,32 @@ func TestExportProfiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportProfilesRedactsInlineSSHSecrets(t *testing.T) {
|
||||
store := setupTestStore(t)
|
||||
profile := makeSSHProfile("Export Secret", "secret.example.com")
|
||||
profile.SSH.ProxyPassword = "proxy-secret"
|
||||
profile.SSH.TOTPSecret = "totp-secret"
|
||||
if err := store.CreateProfile(profile); err != nil {
|
||||
t.Fatalf("CreateProfile() error = %v", err)
|
||||
}
|
||||
|
||||
data, err := store.ExportProfiles(false)
|
||||
if err != nil {
|
||||
t.Fatalf("ExportProfiles(false) error = %v", err)
|
||||
}
|
||||
if strings.Contains(string(data), "proxy-secret") || strings.Contains(string(data), "totp-secret") {
|
||||
t.Fatalf("ExportProfiles(false) leaked inline SSH secrets: %s", string(data))
|
||||
}
|
||||
|
||||
data, err = store.ExportProfiles(true)
|
||||
if err != nil {
|
||||
t.Fatalf("ExportProfiles(true) error = %v", err)
|
||||
}
|
||||
if !strings.Contains(string(data), "proxy-secret") || !strings.Contains(string(data), "totp-secret") {
|
||||
t.Fatalf("ExportProfiles(true) did not include inline SSH secrets: %s", string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportProfiles(t *testing.T) {
|
||||
store := setupTestStore(t)
|
||||
p := makeSSHProfile("Import Me", "import.local")
|
||||
|
||||
+27
-10
@@ -346,13 +346,15 @@ func (s *Store) UpdateLastConnected(id string) error {
|
||||
}
|
||||
|
||||
// ExportProfiles serialises all profiles as JSON. If includeSecrets is false
|
||||
// the export omits credential-related fields (currently a placeholder — all
|
||||
// secrets live in the SecretStore, not in profiles).
|
||||
// the export omits inline credential-related fields.
|
||||
func (s *Store) ExportProfiles(includeSecrets bool) ([]byte, error) {
|
||||
profiles, err := s.ListProfiles()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("storage: export failed: %w", err)
|
||||
}
|
||||
if !includeSecrets {
|
||||
profiles = redactProfileSecrets(profiles)
|
||||
}
|
||||
return json.MarshalIndent(profiles, "", " ")
|
||||
}
|
||||
|
||||
@@ -567,11 +569,11 @@ func (s *Store) SavePreferences(prefs *models.AppPreferences) error {
|
||||
func scanProfile(row *sql.Row) (*models.Profile, error) {
|
||||
p := &models.Profile{}
|
||||
var (
|
||||
protocol, tagsJSON string
|
||||
protocol, tagsJSON string
|
||||
sshJSON, telnetJSON, rawJSON, githubJSON, vncJSON sql.NullString
|
||||
lastConn sql.NullString
|
||||
favourite int
|
||||
createdAt, updatedAt string
|
||||
lastConn sql.NullString
|
||||
favourite int
|
||||
createdAt, updatedAt string
|
||||
)
|
||||
|
||||
err := row.Scan(
|
||||
@@ -634,11 +636,11 @@ func scanProfiles(rows *sql.Rows) ([]*models.Profile, error) {
|
||||
for rows.Next() {
|
||||
p := &models.Profile{}
|
||||
var (
|
||||
protocol, tagsJSON string
|
||||
protocol, tagsJSON string
|
||||
sshJSON, telnetJSON, rawJSON, githubJSON, vncJSON sql.NullString
|
||||
lastConn sql.NullString
|
||||
favourite int
|
||||
createdAt, updatedAt string
|
||||
lastConn sql.NullString
|
||||
favourite int
|
||||
createdAt, updatedAt string
|
||||
)
|
||||
|
||||
err := rows.Scan(
|
||||
@@ -720,6 +722,21 @@ func nullableJSON(ptr interface{}, data []byte) interface{} {
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func redactProfileSecrets(profiles []*models.Profile) []*models.Profile {
|
||||
redacted := make([]*models.Profile, len(profiles))
|
||||
for i, profile := range profiles {
|
||||
cp := *profile
|
||||
if cp.SSH != nil {
|
||||
sshCfg := *cp.SSH
|
||||
sshCfg.ProxyPassword = ""
|
||||
sshCfg.TOTPSecret = ""
|
||||
cp.SSH = &sshCfg
|
||||
}
|
||||
redacted[i] = &cp
|
||||
}
|
||||
return redacted
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Snippet CRUD
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user