config/security: Add AllowChildProcess to security.node.permissions

Some Linux setups trigger detect-libc's spawnSync('getconf') fallback
when process.report does not expose glibcVersionRuntime, breaking
tailwindcss under the Node permission model. Add AllowChildProcess
mirroring AllowAddons/AllowWorker, default to ["tailwindcss"], and
emit --allow-child-process accordingly.

Fixes #14824
This commit is contained in:
Bjørn Erik Pedersen
2026-04-29 11:35:24 +02:00
parent 454450a647
commit d65af84d15
4 changed files with 23 additions and 11 deletions
+13 -10
View File
@@ -65,11 +65,12 @@ var DefaultConfig = Config{
},
Node: Node{
Permissions: NodePermissions{
Disable: false,
AllowRead: []string{"."},
AllowWrite: []string{}, // No write access by default.
AllowAddons: []string{"tailwindcss"}, // tailwindcss does not work without addon permissions.
AllowWorker: []string{"tailwindcss"}, // tailwindcss needs worker access.
Disable: false,
AllowRead: []string{"."},
AllowWrite: []string{}, // No write access by default.
AllowAddons: []string{"tailwindcss"}, // tailwindcss does not work without addon permissions.
AllowWorker: []string{"tailwindcss"}, // tailwindcss needs worker access.
AllowChildProcess: []string{"tailwindcss"}, // detect-libc spawns getconf on some Linux setups.
},
},
}
@@ -128,11 +129,12 @@ type Node struct {
// Use "*" to allow all paths.
type NodePermissions struct {
// Disable turns off the Node.js permission model entirely.
Disable bool `json:"disable"`
AllowRead []string `json:"allowRead"`
AllowWrite []string `json:"allowWrite"`
AllowAddons []string `json:"allowAddons"`
AllowWorker []string `json:"allowWorker"`
Disable bool `json:"disable"`
AllowRead []string `json:"allowRead"`
AllowWrite []string `json:"allowWrite"`
AllowAddons []string `json:"allowAddons"`
AllowWorker []string `json:"allowWorker"`
AllowChildProcess []string `json:"allowChildProcess"`
}
// IsEnabled reports whether the Node.js permission model is active.
@@ -220,6 +222,7 @@ func DecodeConfig(cfg config.Provider) (Config, error) {
sc.Node.Permissions.AllowWrite = slices.Clone(sc.Node.Permissions.AllowWrite)
sc.Node.Permissions.AllowAddons = slices.Clone(sc.Node.Permissions.AllowAddons)
sc.Node.Permissions.AllowWorker = slices.Clone(sc.Node.Permissions.AllowWorker)
sc.Node.Permissions.AllowChildProcess = slices.Clone(sc.Node.Permissions.AllowChildProcess)
if cfg.IsSet(securityConfigKey) {
m := cfg.GetStringMap(securityConfigKey)
dec, err := mapstructure.NewDecoder(
+1 -1
View File
@@ -135,7 +135,7 @@ func TestToTOML(t *testing.T) {
got := DefaultConfig.ToTOML()
c.Assert(got, qt.Equals,
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^node$', '^postcss$', '^tailwindcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z]', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^node$', '^postcss$', '^tailwindcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z]', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowChildProcess = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
)
}