Skip to content

Commit 1472fd8

Browse files
committed
fix: stop emitting --disable-extensions-except flag
The --disable-extensions-except flag causes Chrome to set extensions_enabled() to false, which prevents ExtensionService from creating external providers (including the enterprise policy loader). This breaks ExtensionInstallForcelist policy-based extension installation. Changes: - MergeFlags() now parses --disable-extensions-except but does NOT re-emit it; instead, paths are merged into --load-extension - Updated tests to reflect new behavior
1 parent 9f66c28 commit 1472fd8

File tree

2 files changed

+29
-48
lines changed

2 files changed

+29
-48
lines changed

server/lib/chromiumflags/chromiumflags.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,44 +119,52 @@ func ReadOptionalFlagFile(path string) ([]string, error) {
119119
// The merging logic respects extension-related flag semantics:
120120
// 1) If runtime specifies --disable-extensions, it overrides everything extension related
121121
// 2) Else if base specifies --disable-extensions and runtime does NOT specify any --load-extension, keep base disable
122-
// 3) Else, build from merged load/except
122+
// 3) Else, build from merged load-extension paths
123+
//
124+
// NOTE: --disable-extensions-except is intentionally parsed but NOT re-emitted because it causes
125+
// Chrome to disable external providers (including the policy loader), which prevents enterprise
126+
// policy extensions (ExtensionInstallForcelist) from being fetched and installed.
127+
// See Chromium source: extension_service.cc - external providers are only created when
128+
// extensions_enabled() returns true, which is false when --disable-extensions-except is used.
129+
// Any paths from --disable-extensions-except are merged into --load-extension instead.
130+
//
123131
// Non-extension flags from both base and runtime are combined with deduplication (first occurrence preserved).
124132
func MergeFlags(baseTokens, runtimeTokens []string) []string {
125133
// Buckets
126134
var (
127135
baseNonExt []string // Non-extension related flags contained in base
128136
runtimeNonExt []string // Non-extension related flags contained in runtime
129137
baseLoad []string // --load-extension flags contained in base
130-
baseExcept []string // --disable-extensions-except flags for base
138+
baseExcept []string // --disable-extensions-except flags for base (parsed but not re-emitted)
131139
rtLoad []string // --load-extension flags contained in runtime
132-
rtExcept []string // --disable-extensions-except flags contained in runtime
140+
rtExcept []string // --disable-extensions-except flags contained in runtime (parsed but not re-emitted)
133141
baseDisableAll string // --disable-extensions flag contained in base
134142
rtDisableAll string // --disable-extensions flag contained in runtime
135143
)
136144

137145
baseNonExt = parseTokenStream(baseTokens, &baseLoad, &baseExcept, &baseDisableAll)
138146
runtimeNonExt = parseTokenStream(runtimeTokens, &rtLoad, &rtExcept, &rtDisableAll)
139147

140-
// Merge extension lists
148+
// Merge extension lists - include paths from --disable-extensions-except in load paths
149+
// since we no longer emit --disable-extensions-except
141150
mergedLoad := union(baseLoad, rtLoad)
142-
mergedExcept := union(baseExcept, rtExcept)
151+
mergedLoad = union(mergedLoad, baseExcept)
152+
mergedLoad = union(mergedLoad, rtExcept)
143153

144154
// Construct final extension-related flags respecting override semantics:
145155
// 1) If runtime specifies --disable-extensions, it overrides everything extension related
146156
// 2) Else if base specifies --disable-extensions and runtime does NOT specify any --load-extension, keep base disable
147-
// 3) Else, build from merged load/except
157+
// 3) Else, build from merged load-extension paths
148158
var extFlags []string
149159
if rtDisableAll != "" {
150160
extFlags = append(extFlags, rtDisableAll)
151161
} else {
152-
if baseDisableAll != "" && len(rtLoad) == 0 {
162+
if baseDisableAll != "" && len(rtLoad) == 0 && len(rtExcept) == 0 {
153163
extFlags = append(extFlags, baseDisableAll)
154164
} else if len(mergedLoad) > 0 {
155165
extFlags = append(extFlags, "--load-extension="+strings.Join(mergedLoad, ","))
156166
}
157-
if len(mergedExcept) > 0 {
158-
extFlags = append(extFlags, "--disable-extensions-except="+strings.Join(mergedExcept, ","))
159-
}
167+
// NOTE: --disable-extensions-except is intentionally NOT emitted here
160168
}
161169

162170
// Combine and dedupe (preserving first occurrence)

server/lib/chromiumflags/chromiumflags_test.go

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -103,44 +103,17 @@ func TestMergeUnion(t *testing.T) {
103103

104104
func TestOverrideSemantics_DisableBase_LoadRuntime(t *testing.T) {
105105
// Base has --disable-extensions, runtime has --load-extension → runtime overrides, no disable-all in final
106-
baseFlags := "--disable-extensions"
107-
runtimeFlags := "--load-extension=/e1"
106+
baseFlags := []string{"--disable-extensions"}
107+
runtimeFlags := []string{"--load-extension=/e1"}
108108

109-
baseTokens := parseFlags(baseFlags)
110-
runtimeTokens := parseFlags(runtimeFlags)
111-
112-
var (
113-
baseLoad []string
114-
baseExcept []string
115-
rtLoad []string
116-
rtExcept []string
117-
baseDisable string
118-
rtDisable string
119-
)
120-
121-
_ = parseTokenStream(baseTokens, &baseLoad, &baseExcept, &baseDisable)
122-
_ = parseTokenStream(runtimeTokens, &rtLoad, &rtExcept, &rtDisable)
123-
124-
mergedLoad := union(baseLoad, rtLoad)
125-
mergedExcept := union(baseExcept, rtExcept)
126-
127-
var extFlags []string
128-
if rtDisable != "" {
129-
extFlags = append(extFlags, rtDisable)
130-
} else {
131-
if baseDisable != "" && len(rtLoad) == 0 {
132-
extFlags = append(extFlags, baseDisable)
133-
} else if len(mergedLoad) > 0 {
134-
extFlags = append(extFlags, "--load-extension="+strings.Join(mergedLoad, ","))
135-
}
136-
if len(mergedExcept) > 0 {
137-
extFlags = append(extFlags, "--disable-extensions-except="+strings.Join(mergedExcept, ","))
138-
}
139-
}
109+
got := MergeFlags(baseFlags, runtimeFlags)
140110

141-
for _, f := range extFlags {
111+
for _, f := range got {
142112
if f == "--disable-extensions" {
143-
t.Fatalf("unexpected disable-all in final flags when runtime loads extensions: %#v", extFlags)
113+
t.Fatalf("unexpected disable-all in final flags when runtime loads extensions: %#v", got)
114+
}
115+
if strings.HasPrefix(f, "--disable-extensions-except") {
116+
t.Fatalf("unexpected disable-extensions-except in final flags: %#v", got)
144117
}
145118
}
146119
}
@@ -285,10 +258,10 @@ func TestMergeFlags(t *testing.T) {
285258
want: []string{"--load-extension=/e1,/e2"},
286259
},
287260
{
288-
name: "merge disable-extensions-except flags",
261+
name: "disable-extensions-except paths merged into load-extension",
289262
baseFlags: []string{"--disable-extensions-except=/x1"},
290263
runtimeFlags: []string{"--disable-extensions-except=/x2"},
291-
want: []string{"--disable-extensions-except=/x1,/x2"},
264+
want: []string{"--load-extension=/x1,/x2"},
292265
},
293266
{
294267
name: "runtime disable-extensions overrides all",
@@ -312,7 +285,7 @@ func TestMergeFlags(t *testing.T) {
312285
name: "complex merge with extensions and non-extensions",
313286
baseFlags: []string{"--foo", "--load-extension=/e1", "--disable-extensions-except=/x1"},
314287
runtimeFlags: []string{"--bar", "--load-extension=/e2", "--disable-extensions-except=/x2"},
315-
want: []string{"--foo", "--bar", "--load-extension=/e1,/e2", "--disable-extensions-except=/x1,/x2"},
288+
want: []string{"--foo", "--bar", "--load-extension=/e1,/e2,/x1,/x2"},
316289
},
317290
}
318291

0 commit comments

Comments
 (0)