diff --git a/parser.go b/parser.go index 8da6547a..910bd019 100644 --- a/parser.go +++ b/parser.go @@ -95,6 +95,9 @@ func (p Parser) Parse(spec string) (Schedule, error) { if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") { var err error i := strings.Index(spec, " ") + if i < 0 { + return nil, fmt.Errorf("timezone prefix missing schedule fields: %v", spec) + } eq := strings.Index(spec, "=") if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil { return nil, fmt.Errorf("provided bad location %s: %v", spec[eq+1:i], err) @@ -313,6 +316,9 @@ func getRange(expr string, r bounds) (uint64, error) { if step == 0 { return 0, fmt.Errorf("step of range should be a positive number: %s", expr) } + if step > r.max-r.min { + return 0, fmt.Errorf("step (%d) above maximum (%d): %s", step, r.max-r.min, expr) + } return getBits(start, end, step) | extra, nil } diff --git a/parser_test.go b/parser_test.go index 41c8c520..b97b9b3c 100644 --- a/parser_test.go +++ b/parser_test.go @@ -334,6 +334,18 @@ func TestStandardSpecSchedule(t *testing.T) { expr: "* * * *", err: "expected exactly 5 fields", }, + { + // Step value larger than the field range should be rejected. + // */90 in the minute field (range 0-59) would only ever fire at + // minute 0, silently ignoring the intent. See issue #543. + expr: "*/90 * * * *", + err: "step (90) above maximum (59)", + }, + { + // Same check for hours: */30 is the max valid step (0-23 → range 23). + expr: "* */30 * * *", + err: "step (30) above maximum (23)", + }, } for _, c := range entries { @@ -362,6 +374,18 @@ func every5min(loc *time.Location) *SpecSchedule { return &SpecSchedule{1 << 0, 1 << 5, all(hours), all(dom), all(months), all(dow), loc} } +// TestParseTZMissingFields verifies that a TZ/CRON_TZ prefix with no +// trailing schedule fields returns an error rather than panicking with a +// slice-bounds-out-of-range crash. See GitHub issue #554. +func TestParseTZMissingFields(t *testing.T) { + cases := []string{"TZ=0", "TZ=UTC", "CRON_TZ=America/New_York"} + for _, spec := range cases { + if _, err := ParseStandard(spec); err == nil { + t.Errorf("ParseStandard(%q): expected error for missing schedule fields, got nil", spec) + } + } +} + func every5min5s(loc *time.Location) *SpecSchedule { return &SpecSchedule{1 << 5, 1 << 5, all(hours), all(dom), all(months), all(dow), loc} }