fix: harden read-only target diagnostics
This commit is contained in:
+17
-1
@@ -535,7 +535,7 @@ func (a *Adapter) readSettings(ctx context.Context) (settings, error) {
|
||||
if result.HTTPAuthUser, err = a.getString(ctx, httpSchema, "authentication-user"); err != nil {
|
||||
return settings{}, err
|
||||
}
|
||||
if result.HTTPAuthPassword, err = a.getString(ctx, httpSchema, "authentication-password"); err != nil {
|
||||
if result.HTTPAuthPassword, err = a.getSensitiveString(ctx, httpSchema, "authentication-password"); err != nil {
|
||||
return settings{}, err
|
||||
}
|
||||
if result.HTTPSHost, err = a.getString(ctx, httpsSchema, "host"); err != nil {
|
||||
@@ -560,6 +560,22 @@ func (a *Adapter) getString(ctx context.Context, schema, key string) (string, er
|
||||
}
|
||||
return parseString(raw)
|
||||
}
|
||||
|
||||
// getSensitiveString deliberately suppresses both command error detail and raw
|
||||
// output. A password value is secret even when malformed, and command runners
|
||||
// are not trusted to keep that value out of their errors.
|
||||
func (a *Adapter) getSensitiveString(ctx context.Context, schema, key string) (string, error) {
|
||||
raw, err := a.run(ctx, "get", schema, key)
|
||||
if err != nil {
|
||||
return "", safeRedaction(fmt.Sprintf("get %s %s failed; sensitive output suppressed", schema, key), err)
|
||||
}
|
||||
value, err := parseString(strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return "", errors.New("parse " + schema + " " + key + " failed; sensitive output suppressed")
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (a *Adapter) getInt(ctx context.Context, schema, key string) (int, error) {
|
||||
raw, err := a.get(ctx, schema, key)
|
||||
if err != nil {
|
||||
|
||||
@@ -108,6 +108,49 @@ func active() *profile.Profile {
|
||||
return &profile.Profile{Name: "office", Mode: profile.AdvancedMode, HTTPURL: "http://proxy.example:8080", HTTPSURL: "http://secure.example:8443", AllURL: "socks5://socks.example:1080", Bypass: []string{"localhost", ".example.com", "10.0.0.0/8", "2001:db8::1"}}
|
||||
}
|
||||
|
||||
func TestDiagnosticsSuppressSensitiveGSettingsOutputAndRunnerErrors(t *testing.T) {
|
||||
const secret = "GNOME-DIAGNOSTIC-SECRET-7f3a"
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
value string
|
||||
fail bool
|
||||
}{
|
||||
{name: "malformed value", value: "unterminated-" + secret},
|
||||
{name: "runner error", value: secret, fail: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
r := newRunner()
|
||||
key := "org.gnome.system.proxy.http authentication-password"
|
||||
r.values[key] = test.value
|
||||
if test.fail {
|
||||
r.fail = func(c gnome.Command) error {
|
||||
if reflect.DeepEqual(c.Args, []string{"get", "org.gnome.system.proxy.http", "authentication-password"}) {
|
||||
return errors.New("runner exposed " + secret)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
a := adapter(r, &memoryState{})
|
||||
checks := a.DiagnosticChecks(context.Background(), active())
|
||||
for _, check := range checks {
|
||||
if strings.Contains(check.Evidence, secret) {
|
||||
t.Fatalf("diagnostic evidence exposed password: %#v", checks)
|
||||
}
|
||||
}
|
||||
result := a.SetDesired(context.Background(), true, active())
|
||||
operationError := ""
|
||||
if result.Err != nil {
|
||||
operationError = result.Err.Error()
|
||||
}
|
||||
for _, evidence := range []string{a.Availability(context.Background()).Evidence, a.Inspect(context.Background(), active()).Evidence, operationError} {
|
||||
if strings.Contains(evidence, secret) {
|
||||
t.Fatalf("Target evidence or loggable operation error exposed password: %q", evidence)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiagnosticChecksCoverCommandSchemasSessionAndAuthorityWithoutMutation(t *testing.T) {
|
||||
r, state := newRunner(), &memoryState{}
|
||||
a := adapter(r, state)
|
||||
|
||||
Reference in New Issue
Block a user