mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
fix(deadline): reject day/month dates that read differently by locale
--deadline 03/04/2026 was silently parsed as March 4. Numeric dates such as 03/04/2026 or 15.01.2028 are now refused with "the order of day and month is ambiguous; use YYYY-MM-DD", and the error for other invalid values no longer suggests clearing on issue create. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
2 files changed
+20
-2
No files matched your search
@@ -5,6 +5,8 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -12,18 +14,25 @@ import (
|
|||||||
"github.com/araddon/dateparse"
|
"github.com/araddon/dateparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// numericDate matches dates such as 03/04/2026, which read as March 4 in the US and April 3
|
||||||
|
// elsewhere.
|
||||||
|
var numericDate = regexp.MustCompile(`^\d{1,2}[/.]\d{1,2}[/.]\d{2,4}$`)
|
||||||
|
|
||||||
// parseDeadline parses a --deadline value; an empty value requests clearing.
|
// parseDeadline parses a --deadline value; an empty value requests clearing.
|
||||||
// Supplied timezone offsets are kept and Unix timestamps are UTC.
|
// Supplied timezone offsets are kept and Unix timestamps are UTC.
|
||||||
func parseDeadline(value string) (*time.Time, *bool, error) {
|
func parseDeadline(value string) (*time.Time, *bool, error) {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return nil, new(true), nil
|
return nil, new(true), nil
|
||||||
}
|
}
|
||||||
|
if numericDate.MatchString(value) {
|
||||||
|
return nil, nil, clierr.Wrap(clierr.Usage, fmt.Errorf("invalid --deadline %q: the order of day and month is ambiguous; use YYYY-MM-DD", value))
|
||||||
|
}
|
||||||
date, err := dateparse.ParseAny(value)
|
date, err := dateparse.ParseAny(value)
|
||||||
if _, numericError := strconv.ParseInt(value, 10, 64); numericError == nil {
|
if _, numericError := strconv.ParseInt(value, 10, 64); numericError == nil {
|
||||||
date = date.UTC()
|
date = date.UTC()
|
||||||
}
|
}
|
||||||
if err != nil || date.IsZero() || date.Unix() == 0 || date.Year() < 1 || date.Year() > 9999 {
|
if err != nil || date.IsZero() || date.Unix() == 0 || date.Year() < 1 || date.Year() > 9999 {
|
||||||
return nil, nil, clierr.Wrap(clierr.Usage, errors.New("invalid --deadline; use YYYY-MM-DD, RFC3339, a Unix timestamp, or an empty value to clear"))
|
return nil, nil, clierr.Wrap(clierr.Usage, errors.New("invalid --deadline; use YYYY-MM-DD, RFC3339 or a Unix timestamp"))
|
||||||
}
|
}
|
||||||
return &date, nil, nil
|
return &date, nil, nil
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ func TestDeadlineInputAndExplicitClearing(t *testing.T) {
|
|||||||
for _, tc := range []struct{ value, payload string }{
|
for _, tc := range []struct{ value, payload string }{
|
||||||
{"2028-01-15", `{"due_date":"2028-01-15T00:00:00Z"}`},
|
{"2028-01-15", `{"due_date":"2028-01-15T00:00:00Z"}`},
|
||||||
{"2028-01-15T00:30:00+02:00", `{"due_date":"2028-01-15T00:30:00+02:00"}`},
|
{"2028-01-15T00:30:00+02:00", `{"due_date":"2028-01-15T00:30:00+02:00"}`},
|
||||||
{"01/15/2028", `{"due_date":"2028-01-15T00:00:00Z"}`},
|
{"Jan 15 2028", `{"due_date":"2028-01-15T00:00:00Z"}`},
|
||||||
{"1704067200000", `{"due_date":"2024-01-01T00:00:00Z"}`},
|
{"1704067200000", `{"due_date":"2024-01-01T00:00:00Z"}`},
|
||||||
{"1704067200", `{"due_date":"2024-01-01T00:00:00Z"}`},
|
{"1704067200", `{"due_date":"2024-01-01T00:00:00Z"}`},
|
||||||
{"", `{"unset_due_date":true}`},
|
{"", `{"unset_due_date":true}`},
|
||||||
@@ -41,6 +42,14 @@ func TestDeadlineInputAndExplicitClearing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAmbiguousNumericDeadlinesAreRejected(t *testing.T) {
|
||||||
|
for _, value := range []string{"03/04/2026", "04/03/2026", "01/15/2028", "15.01.2028", "3/4/26"} {
|
||||||
|
for _, command := range [][]string{{"issue", "edit", "1"}, {"issue", "create", "--title", "x"}, {"milestone", "create", "--title", "x"}} {
|
||||||
|
requireUsageBeforeRequests(t, fmt.Sprintf("invalid --deadline %q: the order of day and month is ambiguous; use YYYY-MM-DD", value), append(command, "--deadline="+value)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInvalidDeadlineBeforeInputCollectionOrNetwork(t *testing.T) {
|
func TestInvalidDeadlineBeforeInputCollectionOrNetwork(t *testing.T) {
|
||||||
for _, value := range []string{"bad", "2026-02-30", "0", "not-a-timestamp", "999999999999999999999999999999999999"} {
|
for _, value := range []string{"bad", "2026-02-30", "0", "not-a-timestamp", "999999999999999999999999999999999999"} {
|
||||||
for _, command := range [][]string{{"issue", "edit", "1"}, {"pr", "edit", "1", "--add-reviewers=alice"}, {"issue", "create"}, {"pr", "create"}} {
|
for _, command := range [][]string{{"issue", "edit", "1"}, {"pr", "edit", "1", "--add-reviewers=alice"}, {"issue", "create"}, {"pr", "create"}} {
|
||||||
|
|||||||
Reference in new issue
Block a user