Honor unsafe WebAuthn override
- commit
- db4efc48421e6d4ba381e625081ef0cdc0fcee73
- author
- brice · · 2026-04-29 17:41 UTC
- parent
- 27813e6
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +82 -1
M internal/infrastructure/cef/app.go +48 -1
| @@ -54,7 +54,12 @@ func configureCommandLine(commandLine purecef.CommandLine) { | |||
| 54 | 54 | } | |
| 55 | 55 | ||
| 56 | 56 | func configureWebAuthnFeaturePolicy(commandLine purecef.CommandLine) { | |
| 57 | - | if commandLine == nil || cefWebAuthnUnsafeEnabled() { | |
| 57 | + | if commandLine == nil { | |
| 58 | + | return | |
| 59 | + | } | |
| 60 | + | if cefWebAuthnUnsafeEnabled() { | |
| 61 | + | removeCommaSeparatedSwitchValues(commandLine, chromiumDisableFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | |
| 62 | + | removeCommaSeparatedSwitchValues(commandLine, chromiumDisableBlinkFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | |
| 58 | 63 | return | |
| 59 | 64 | } | |
| 60 | 65 | appendUniqueCommaSeparatedSwitchValues(commandLine, chromiumDisableFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | |
| @@ -97,6 +102,48 @@ func appendUniqueCommaSeparatedSwitchValues(commandLine purecef.CommandLine, nam | |||
| 97 | 102 | commandLine.AppendSwitchWithValue(name, strings.Join(combined, ",")) | |
| 98 | 103 | } | |
| 99 | 104 | ||
| 105 | + | func removeCommaSeparatedSwitchValues(commandLine purecef.CommandLine, name string, values ...string) { | |
| 106 | + | if commandLine == nil || len(values) == 0 { | |
| 107 | + | return | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | existing := commandLine.GetSwitchValue(name) | |
| 111 | + | if existing == "" { | |
| 112 | + | return | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | removeSet := make(map[string]struct{}, len(values)) | |
| 116 | + | for _, v := range values { | |
| 117 | + | removeSet[strings.TrimSpace(v)] = struct{}{} | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | tokens := strings.Split(existing, ",") | |
| 121 | + | cleaned := make([]string, 0, len(tokens)) | |
| 122 | + | removed := false | |
| 123 | + | for _, token := range tokens { | |
| 124 | + | token = strings.TrimSpace(token) | |
| 125 | + | if token == "" { | |
| 126 | + | continue | |
| 127 | + | } | |
| 128 | + | if _, ok := removeSet[token]; ok { | |
| 129 | + | removed = true | |
| 130 | + | continue | |
| 131 | + | } | |
| 132 | + | cleaned = append(cleaned, token) | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | if !removed { | |
| 136 | + | return | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | if len(cleaned) == 0 { | |
| 140 | + | commandLine.RemoveSwitch(name) | |
| 141 | + | return | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | commandLine.AppendSwitchWithValue(name, strings.Join(cleaned, ",")) | |
| 145 | + | } | |
| 146 | + | ||
| 100 | 147 | // dumberApp implements purecef.App to provide custom scheme registration, | |
| 101 | 148 | // command-line configuration, and browser-process lifecycle callbacks. | |
| 102 | 149 | type dumberApp struct { | |
| @@ -54,7 +54,12 @@ func configureCommandLine(commandLine purecef.CommandLine) { | |||||
| 54 | } | 54 | } | ||
| 55 | 55 | ||||
| 56 | func configureWebAuthnFeaturePolicy(commandLine purecef.CommandLine) { | 56 | func configureWebAuthnFeaturePolicy(commandLine purecef.CommandLine) { | ||
| 57 | - | if commandLine == nil || cefWebAuthnUnsafeEnabled() { | 57 | + | if commandLine == nil { |
| 58 | + | return | |||
| 59 | + | } | |||
| 60 | + | if cefWebAuthnUnsafeEnabled() { | |||
| 61 | + | removeCommaSeparatedSwitchValues(commandLine, chromiumDisableFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | |||
| 62 | + | removeCommaSeparatedSwitchValues(commandLine, chromiumDisableBlinkFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | |||
| 58 | return | 63 | return | ||
| 59 | } | 64 | } | ||
| 60 | appendUniqueCommaSeparatedSwitchValues(commandLine, chromiumDisableFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | 65 | appendUniqueCommaSeparatedSwitchValues(commandLine, chromiumDisableFeaturesSwitch, cefWebAuthnFeaturesDisabledByPolicy...) | ||
| @@ -97,6 +102,48 @@ func appendUniqueCommaSeparatedSwitchValues(commandLine purecef.CommandLine, nam | |||||
| 97 | commandLine.AppendSwitchWithValue(name, strings.Join(combined, ",")) | 102 | commandLine.AppendSwitchWithValue(name, strings.Join(combined, ",")) | ||
| 98 | } | 103 | } | ||
| 99 | 104 | ||||
| 105 | + | func removeCommaSeparatedSwitchValues(commandLine purecef.CommandLine, name string, values ...string) { | |||
| 106 | + | if commandLine == nil || len(values) == 0 { | |||
| 107 | + | return | |||
| 108 | + | } | |||
| 109 | + | ||||
| 110 | + | existing := commandLine.GetSwitchValue(name) | |||
| 111 | + | if existing == "" { | |||
| 112 | + | return | |||
| 113 | + | } | |||
| 114 | + | ||||
| 115 | + | removeSet := make(map[string]struct{}, len(values)) | |||
| 116 | + | for _, v := range values { | |||
| 117 | + | removeSet[strings.TrimSpace(v)] = struct{}{} | |||
| 118 | + | } | |||
| 119 | + | ||||
| 120 | + | tokens := strings.Split(existing, ",") | |||
| 121 | + | cleaned := make([]string, 0, len(tokens)) | |||
| 122 | + | removed := false | |||
| 123 | + | for _, token := range tokens { | |||
| 124 | + | token = strings.TrimSpace(token) | |||
| 125 | + | if token == "" { | |||
| 126 | + | continue | |||
| 127 | + | } | |||
| 128 | + | if _, ok := removeSet[token]; ok { | |||
| 129 | + | removed = true | |||
| 130 | + | continue | |||
| 131 | + | } | |||
| 132 | + | cleaned = append(cleaned, token) | |||
| 133 | + | } | |||
| 134 | + | ||||
| 135 | + | if !removed { | |||
| 136 | + | return | |||
| 137 | + | } | |||
| 138 | + | ||||
| 139 | + | if len(cleaned) == 0 { | |||
| 140 | + | commandLine.RemoveSwitch(name) | |||
| 141 | + | return | |||
| 142 | + | } | |||
| 143 | + | ||||
| 144 | + | commandLine.AppendSwitchWithValue(name, strings.Join(cleaned, ",")) | |||
| 145 | + | } | |||
| 146 | + | ||||
| 100 | // dumberApp implements purecef.App to provide custom scheme registration, | 147 | // dumberApp implements purecef.App to provide custom scheme registration, | ||
| 101 | // command-line configuration, and browser-process lifecycle callbacks. | 148 | // command-line configuration, and browser-process lifecycle callbacks. | ||
| 102 | type dumberApp struct { | 149 | type dumberApp struct { | ||
M internal/infrastructure/cef/app_test.go +34 -0
| @@ -188,6 +188,8 @@ func TestConfigureCommandLine_AppendsExpectedSwitches(t *testing.T) { | |||
| 188 | 188 | } | |
| 189 | 189 | ||
| 190 | 190 | func TestConfigureCommandLine_DisablesWebAuthnByDefault(t *testing.T) { | |
| 191 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "") | |
| 192 | + | ||
| 191 | 193 | commandLine := newMutableCommandLineStub() | |
| 192 | 194 | configureCommandLine(commandLine) | |
| 193 | 195 | ||
| @@ -229,6 +231,36 @@ func TestConfigureCommandLine_WebAuthnDisablePreservesExistingDisableFeatures(t | |||
| 229 | 231 | } | |
| 230 | 232 | } | |
| 231 | 233 | ||
| 234 | + | func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesExistingWebAuthTokens(t *testing.T) { | |
| 235 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "1") | |
| 236 | + | ||
| 237 | + | commandLine := newMutableCommandLineStub() | |
| 238 | + | commandLine.AppendSwitchWithValue(chromiumDisableFeaturesSwitch, "ExistingFeature,WebAuth,AnotherFeature") | |
| 239 | + | commandLine.AppendSwitchWithValue(chromiumDisableBlinkFeaturesSwitch, "WebAuth,ExistingBlinkFeature") | |
| 240 | + | ||
| 241 | + | configureCommandLine(commandLine) | |
| 242 | + | ||
| 243 | + | if got := commandLine.GetSwitchValue(chromiumDisableFeaturesSwitch); got != "ExistingFeature,AnotherFeature" { | |
| 244 | + | t.Fatalf("disable-features = %q, want %q", got, "ExistingFeature,AnotherFeature") | |
| 245 | + | } | |
| 246 | + | if got := commandLine.GetSwitchValue(chromiumDisableBlinkFeaturesSwitch); got != "ExistingBlinkFeature" { | |
| 247 | + | t.Fatalf("disable-blink-features = %q, want %q", got, "ExistingBlinkFeature") | |
| 248 | + | } | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesSwitchWhenOnlyWebAuth(t *testing.T) { | |
| 252 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "1") | |
| 253 | + | ||
| 254 | + | commandLine := newMutableCommandLineStub() | |
| 255 | + | commandLine.AppendSwitchWithValue(chromiumDisableFeaturesSwitch, "WebAuth") | |
| 256 | + | ||
| 257 | + | configureCommandLine(commandLine) | |
| 258 | + | ||
| 259 | + | if commandLine.HasSwitch(chromiumDisableFeaturesSwitch) { | |
| 260 | + | t.Fatalf("disable-features switch should have been removed entirely, but has value %q", commandLine.GetSwitchValue(chromiumDisableFeaturesSwitch)) | |
| 261 | + | } | |
| 262 | + | } | |
| 263 | + | ||
| 232 | 264 | func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) { | |
| 233 | 265 | t.Run("trims whitespace and skips empty existing values", func(t *testing.T) { | |
| 234 | 266 | commandLine := newMutableCommandLineStub() | |
| @@ -282,6 +314,8 @@ func TestDumberBPH_OnBeforeChildProcessLaunch_AppendsNoZygote(t *testing.T) { | |||
| 282 | 314 | } | |
| 283 | 315 | ||
| 284 | 316 | func TestDumberBPH_OnBeforeChildProcessLaunch_DisablesWebAuthnByDefault(t *testing.T) { | |
| 317 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "") | |
| 318 | + | ||
| 285 | 319 | commandLine := newMutableCommandLineStub() | |
| 286 | 320 | commandLine.AppendSwitchWithValue("type", "renderer") | |
| 287 | 321 | ||
| @@ -188,6 +188,8 @@ func TestConfigureCommandLine_AppendsExpectedSwitches(t *testing.T) { | |||||
| 188 | } | 188 | } | ||
| 189 | 189 | ||||
| 190 | func TestConfigureCommandLine_DisablesWebAuthnByDefault(t *testing.T) { | 190 | func TestConfigureCommandLine_DisablesWebAuthnByDefault(t *testing.T) { | ||
| 191 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "") | |||
| 192 | + | ||||
| 191 | commandLine := newMutableCommandLineStub() | 193 | commandLine := newMutableCommandLineStub() | ||
| 192 | configureCommandLine(commandLine) | 194 | configureCommandLine(commandLine) | ||
| 193 | 195 | ||||
| @@ -229,6 +231,36 @@ func TestConfigureCommandLine_WebAuthnDisablePreservesExistingDisableFeatures(t | |||||
| 229 | } | 231 | } | ||
| 230 | } | 232 | } | ||
| 231 | 233 | ||||
| 234 | + | func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesExistingWebAuthTokens(t *testing.T) { | |||
| 235 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "1") | |||
| 236 | + | ||||
| 237 | + | commandLine := newMutableCommandLineStub() | |||
| 238 | + | commandLine.AppendSwitchWithValue(chromiumDisableFeaturesSwitch, "ExistingFeature,WebAuth,AnotherFeature") | |||
| 239 | + | commandLine.AppendSwitchWithValue(chromiumDisableBlinkFeaturesSwitch, "WebAuth,ExistingBlinkFeature") | |||
| 240 | + | ||||
| 241 | + | configureCommandLine(commandLine) | |||
| 242 | + | ||||
| 243 | + | if got := commandLine.GetSwitchValue(chromiumDisableFeaturesSwitch); got != "ExistingFeature,AnotherFeature" { | |||
| 244 | + | t.Fatalf("disable-features = %q, want %q", got, "ExistingFeature,AnotherFeature") | |||
| 245 | + | } | |||
| 246 | + | if got := commandLine.GetSwitchValue(chromiumDisableBlinkFeaturesSwitch); got != "ExistingBlinkFeature" { | |||
| 247 | + | t.Fatalf("disable-blink-features = %q, want %q", got, "ExistingBlinkFeature") | |||
| 248 | + | } | |||
| 249 | + | } | |||
| 250 | + | ||||
| 251 | + | func TestConfigureCommandLine_WebAuthnUnsafeOverrideRemovesSwitchWhenOnlyWebAuth(t *testing.T) { | |||
| 252 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "1") | |||
| 253 | + | ||||
| 254 | + | commandLine := newMutableCommandLineStub() | |||
| 255 | + | commandLine.AppendSwitchWithValue(chromiumDisableFeaturesSwitch, "WebAuth") | |||
| 256 | + | ||||
| 257 | + | configureCommandLine(commandLine) | |||
| 258 | + | ||||
| 259 | + | if commandLine.HasSwitch(chromiumDisableFeaturesSwitch) { | |||
| 260 | + | t.Fatalf("disable-features switch should have been removed entirely, but has value %q", commandLine.GetSwitchValue(chromiumDisableFeaturesSwitch)) | |||
| 261 | + | } | |||
| 262 | + | } | |||
| 263 | + | ||||
| 232 | func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) { | 264 | func TestAppendUniqueCommaSeparatedSwitchValues(t *testing.T) { | ||
| 233 | t.Run("trims whitespace and skips empty existing values", func(t *testing.T) { | 265 | t.Run("trims whitespace and skips empty existing values", func(t *testing.T) { | ||
| 234 | commandLine := newMutableCommandLineStub() | 266 | commandLine := newMutableCommandLineStub() | ||
| @@ -282,6 +314,8 @@ func TestDumberBPH_OnBeforeChildProcessLaunch_AppendsNoZygote(t *testing.T) { | |||||
| 282 | } | 314 | } | ||
| 283 | 315 | ||||
| 284 | func TestDumberBPH_OnBeforeChildProcessLaunch_DisablesWebAuthnByDefault(t *testing.T) { | 316 | func TestDumberBPH_OnBeforeChildProcessLaunch_DisablesWebAuthnByDefault(t *testing.T) { | ||
| 317 | + | t.Setenv(cefEnableWebAuthnUnsafeEnvVar, "") | |||
| 318 | + | ||||
| 285 | commandLine := newMutableCommandLineStub() | 319 | commandLine := newMutableCommandLineStub() | ||
| 286 | commandLine.AppendSwitchWithValue("type", "renderer") | 320 | commandLine.AppendSwitchWithValue("type", "renderer") | ||
| 287 | 321 | ||||