mirror of
https://github.com/dokku/dokku.git
synced 2026-07-10 12:36:13 +02:00
The docker-local scheduler wrote each app.json cron command verbatim into the dokku user's crontab, where cron's `bash -c` interpreted any shell metacharacters in the command on the host as the dokku user rather than inside the container. The crontab line is now `dokku cron:run <app> <cron-id>`, and the command is resolved from app.json and exec'd inside the container at run time. Commands containing shell operators are also rejected when app.json is validated at deploy time.
103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
package cron
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDokkuRunCommandAppTaskDispatchesViaCronRun(t *testing.T) {
|
|
task := CronTask{
|
|
App: "myapp",
|
|
ID: "abc123",
|
|
Command: "echo CRON_OK; echo hi > /tmp/appjson-test.txt",
|
|
Schedule: "* * * * *",
|
|
ConcurrencyPolicy: "allow",
|
|
}
|
|
|
|
got := task.DokkuRunCommand()
|
|
want := "dokku cron:run myapp abc123"
|
|
if got != want {
|
|
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
|
}
|
|
|
|
if strings.Contains(got, task.Command) {
|
|
t.Errorf("DokkuRunCommand() leaked user command into crontab line: %q", got)
|
|
}
|
|
if strings.ContainsAny(got, ";>|&`$") {
|
|
t.Errorf("DokkuRunCommand() contains shell metacharacters: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestDokkuRunCommandPlainCommandStillUsesCronRun(t *testing.T) {
|
|
task := CronTask{
|
|
App: "myapp",
|
|
ID: "abc123",
|
|
Command: "npm run send-email",
|
|
Schedule: "@daily",
|
|
ConcurrencyPolicy: "forbid",
|
|
}
|
|
|
|
got := task.DokkuRunCommand()
|
|
want := "dokku cron:run myapp abc123"
|
|
if got != want {
|
|
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDokkuRunCommandAltCommandUnchanged(t *testing.T) {
|
|
task := CronTask{
|
|
ID: "abc123",
|
|
AltCommand: "/usr/bin/some-internal-task --flag",
|
|
}
|
|
|
|
got := task.DokkuRunCommand()
|
|
want := "/usr/bin/some-internal-task --flag"
|
|
if got != want {
|
|
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestValidateCronCommandAcceptsValidCommands(t *testing.T) {
|
|
cases := []string{
|
|
"python3 task.py schedule",
|
|
"npm run send-email",
|
|
"sh -c 'echo CRON_OK; echo hi > /tmp/x.txt'",
|
|
`node -e 'console.log(1)'`,
|
|
"true",
|
|
}
|
|
for _, cmd := range cases {
|
|
if err := ValidateCronCommand(cmd); err != nil {
|
|
t.Errorf("ValidateCronCommand(%q) returned error: %v", cmd, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateCronCommandRejectsShellOperators(t *testing.T) {
|
|
cases := []string{
|
|
"echo CRON_OK; echo hi > /tmp/x.txt",
|
|
"cmd1 && cmd2",
|
|
"cmd | other",
|
|
"cmd > file",
|
|
"cmd $(other)",
|
|
}
|
|
for _, cmd := range cases {
|
|
if err := ValidateCronCommand(cmd); err == nil {
|
|
t.Errorf("ValidateCronCommand(%q) accepted a command containing a shell operator", cmd)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDokkuRunCommandAltCommandWithLogFile(t *testing.T) {
|
|
task := CronTask{
|
|
ID: "abc123",
|
|
AltCommand: "/usr/bin/some-internal-task",
|
|
LogFile: "/var/log/dokku/internal-task.log",
|
|
}
|
|
|
|
got := task.DokkuRunCommand()
|
|
want := "/usr/bin/some-internal-task &>> /var/log/dokku/internal-task.log"
|
|
if got != want {
|
|
t.Errorf("DokkuRunCommand() = %q, want %q", got, want)
|
|
}
|
|
}
|