security: hardening по результатам аудита безопасности
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -282,11 +283,15 @@ func TestDump_EnvVars(t *testing.T) {
|
||||
}
|
||||
|
||||
envStr := strings.Join(capturedCmd.Env, "\n")
|
||||
|
||||
if strings.Contains(envStr, "PGPASSWORD=") {
|
||||
t.Errorf("PGPASSWORD must not be passed to pg_dump subprocess")
|
||||
}
|
||||
|
||||
wantEnvVars := []string{
|
||||
"PGHOST=myhost",
|
||||
"PGPORT=5433",
|
||||
"PGUSER=myuser",
|
||||
"PGPASSWORD=mypass",
|
||||
"PGDATABASE=mydb",
|
||||
}
|
||||
for _, wantEnv := range wantEnvVars {
|
||||
@@ -294,6 +299,102 @@ func TestDump_EnvVars(t *testing.T) {
|
||||
t.Errorf("env missing %q", wantEnv)
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(envStr, "PGPASSFILE=") {
|
||||
t.Errorf("PGPASSFILE must be set when a password is provided")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDump_PgpassFileRemovedAfterRun(t *testing.T) {
|
||||
var capturedCmd *exec.Cmd
|
||||
|
||||
adapter := &adapter{
|
||||
commandContext: func(ctx context.Context, name string, arg ...string) *exec.Cmd {
|
||||
capturedCmd = exec.CommandContext(ctx, "sh", "-c", "exit 0")
|
||||
return capturedCmd
|
||||
},
|
||||
}
|
||||
|
||||
err := adapter.Dump(
|
||||
context.Background(),
|
||||
pgdump.Options{
|
||||
Password: "secret",
|
||||
Database: "mydb",
|
||||
},
|
||||
io.Discard,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
envStr := strings.Join(capturedCmd.Env, "\n")
|
||||
passFilePrefix := "PGPASSFILE="
|
||||
idx := strings.Index(envStr, passFilePrefix)
|
||||
if idx == -1 {
|
||||
t.Fatalf("PGPASSFILE not found in env")
|
||||
}
|
||||
pgpassPath := envStr[idx+len(passFilePrefix):]
|
||||
if newlineIdx := strings.Index(pgpassPath, "\n"); newlineIdx != -1 {
|
||||
pgpassPath = pgpassPath[:newlineIdx]
|
||||
}
|
||||
|
||||
if _, statErr := os.Stat(pgpassPath); !os.IsNotExist(statErr) {
|
||||
t.Errorf("temporary pgpass file %s was not removed after Dump returned", pgpassPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWritePgPassFile_Permissions(t *testing.T) {
|
||||
path, cleanup, err := writePgPassFile(pgdump.Options{
|
||||
Host: "myhost",
|
||||
Port: 5432,
|
||||
Database: "mydb",
|
||||
User: "myuser",
|
||||
Password: "secret",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatalf("stat pgpass file: %v", err)
|
||||
}
|
||||
if info.Mode().Perm() != 0o600 {
|
||||
t.Errorf("pgpass file permissions = %o, want %o", info.Mode().Perm(), 0o600)
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read pgpass file: %v", err)
|
||||
}
|
||||
want := "myhost:5432:mydb:myuser:secret\n"
|
||||
if string(content) != want {
|
||||
t.Errorf("pgpass content = %q, want %q", string(content), want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDump_InvalidExcludeTable(t *testing.T) {
|
||||
adapter := &adapter{
|
||||
commandContext: func(ctx context.Context, name string, arg ...string) *exec.Cmd {
|
||||
return exec.CommandContext(ctx, "sh", "-c", "exit 0")
|
||||
},
|
||||
}
|
||||
|
||||
err := adapter.Dump(
|
||||
context.Background(),
|
||||
pgdump.Options{
|
||||
Database: "testdb",
|
||||
ExcludeTables: []string{"table; DROP TABLE users;--"},
|
||||
},
|
||||
io.Discard,
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid exclude-table identifier")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid exclude-table identifier") {
|
||||
t.Errorf("error = %v, want invalid exclude-table identifier", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDump_PipeWriterClosed(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user