diff --git a/common/hexec/exec.go b/common/hexec/exec.go index baec66086..61b9252c4 100644 --- a/common/hexec/exec.go +++ b/common/hexec/exec.go @@ -285,6 +285,11 @@ func (e *Exec) nodePermissionArgs(name, scriptPath string) []string { args = append(args, "--allow-worker") } + if slices.Contains(perms.AllowChildProcess, name) { + silenceSecurityWarnings = true + args = append(args, "--allow-child-process") + } + if silenceSecurityWarnings { // There are no more fine grained way to do this, see https://github.com/nodejs/node/issues/59818 // If the process is configured to allow either workers or addons, Node will print warnings that's not very helpful. diff --git a/common/hexec/exec_test.go b/common/hexec/exec_test.go index 89af3f30c..0e50cb3ec 100644 --- a/common/hexec/exec_test.go +++ b/common/hexec/exec_test.go @@ -43,6 +43,7 @@ func TestNodePermissionArgs(t *testing.T) { "--allow-fs-read=" + site, "--allow-addons", "--allow-worker", + "--allow-child-process", "--disable-warning=SecurityWarning", }) }) @@ -75,6 +76,7 @@ func TestNodePermissionArgs(t *testing.T) { "--allow-fs-write=" + site, "--allow-addons", "--allow-worker", + "--allow-child-process", "--disable-warning=SecurityWarning", }) }) @@ -94,6 +96,7 @@ func TestNodePermissionArgs(t *testing.T) { "--allow-fs-write=*", "--allow-addons", "--allow-worker", + "--allow-child-process", "--disable-warning=SecurityWarning", }) }) @@ -114,6 +117,7 @@ func TestNodePermissionArgs(t *testing.T) { cfg.Node.Permissions.AllowRead = nil cfg.Node.Permissions.AllowAddons = nil cfg.Node.Permissions.AllowWorker = nil + cfg.Node.Permissions.AllowChildProcess = nil e := &Exec{ sc: cfg, workingDir: site, diff --git a/config/security/securityConfig.go b/config/security/securityConfig.go index e9590066f..0e9208790 100644 --- a/config/security/securityConfig.go +++ b/config/security/securityConfig.go @@ -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( diff --git a/config/security/securityConfig_test.go b/config/security/securityConfig_test.go index 8b4223e5d..4e120a285 100644 --- a/config/security/securityConfig_test.go +++ b/config/security/securityConfig_test.go @@ -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", ) }